MUCH LOVE for testing
[sinatra.git] / lib / sinatra / event.rb
blob61c39d69fcf5f71ab5f90bc0c4ad57cab2de5389
1 require 'thread'
3 module Sinatra
4   
5   module EventManager # :nodoc:
6     extend self
8     def reset!
9       @events.clear if @events
10     end
12     def events
13       @events || []
14     end
15     
16     def register_event(event)
17       (@events ||= []) << event
18     end
19     
20     def determine_event(verb, path, if_nil = :present_error)
21       event = events.find(method(if_nil)) do |e|
22         e.verb == verb && e.recognize(path)
23       end
24     end
25     
26     def present_error
27       determine_event(:get, '404', :not_found)
28     end
29     
30     def not_found
31       Event.new(:get, 'not_found', false) do
32         status 404
33     
34         if request.path_info == '/' && request.request_method == 'GET'
35           erb :default_index, :views_directory => SINATRA_ROOT + '/files'
36         else
37           erb :not_found, :views_directory => SINATRA_ROOT + '/files'
38         end
39       end
40     end
41     
42   end
43     
44   class Event # :nodoc:
46     cattr_accessor :logger
47     cattr_accessor :after_filters
48     cattr_accessor :before_filters
49     
50     @@mutex = Mutex.new
51     
52     self.before_filters = []
53     self.after_filters = []
54     
55     def self.reset!
56       self.before_filters.clear
57       self.after_filters.clear
58     end
59     
60     def self.before_attend(method_name = nil, options ={}, &block)
61       setup_filter(:before_filters, method_name, options, &block)
62     end
64     def self.after_attend(method_name = nil, options = {}, &block)
65       setup_filter(:after_filters, method_name, options, &block)
66     end
67     
68     def self.setup_filter(filter_set_name, method_name, options = {}, &block)
69       raise "Must specify method or block" if method_name.nil? and !block_given?
70       value = if block_given?
71         block
72       else
73         method_name
74       end
75       insert_index = options[:infront] == true ? 0 : -1
76       send(filter_set_name).insert(insert_index, value)
77     end
78           
79     attr_reader :path, :verb
80     
81     def initialize(verb, path, register = true, &block)
82       @verb = verb
83       @path = path
84       @route = Route.new(path)
85       @block = block
86       EventManager.register_event(self) if register
87     end
88     
89     def attend(request)
90       request.params.merge!(@route.params)
91       context = EventContext.new(request)
92       run_safely do
93         caught = catch(:halt) do
94           call_filters(before_filters, context)
95         end
96         body = case caught
97           when :filter_chain_completed
98             begin
99               context.instance_eval(&@block) if @block
100             rescue => e
101               context.error e
102             end
103           when Symbol
104             context.send(caught)
105           when String
106             caught
107           when Fixnum
108             context.status caught
109         end
110         context.body context.body || body || ''
111         call_filters(after_filters, context)
112       end
113       context.log_event
114       context
115     end
116     alias :call :attend
118     def recognize(path)
119       @route.recognize(path)
120     end
122     private
123     
124       def run_safely
125         if Options.use_mutex?
126           @@mutex.synchronize do
127             yield
128           end
129         else
130           yield
131         end
132       end
133       
134       # Adapted from Merb
135       # calls a filter chain according to rules.
136       def call_filters(filter_set, context)
137         filter_set.each do |filter|
138           case filter
139             when Symbol, String
140              context.send(filter)
141             when Proc
142              context.instance_eval(&filter)
143           end
144         end
145         :filter_chain_completed
146       end
147       
148   end
149   
150   class StaticEvent < Event # :nodoc:
151     
152     def initialize(path, root, register = true)
153       @root = root
154       super(:get, path, register)
155     end
157     def recognize(path)
158       filename = physical_path_for(path)
159       File.exists?(filename) && File.file?(filename)
160     end
161     
162     def physical_path_for(path)
163       path.gsub(/^#{@path}/, @root)
164     end
165     
166     def attend(request)
167       @filename = physical_path_for(request.path_info)
168       context = EventContext.new(request)
169       context.body self
170       context.header 'Content-Type' => MIME_TYPES[File.extname(@filename)[1..-1]]
171       context.header 'Content-Length' => File.size(@filename).to_s
172       context
173     end
174     
175     def each
176       File.open(@filename, "rb") do |file|
177         while part = file.read(8192)
178           yield part
179         end
180       end
181     end
182     
183     # :stopdoc:
184     # From WEBrick.
185     MIME_TYPES = {
186       "ai"    => "application/postscript",
187       "asc"   => "text/plain",
188       "avi"   => "video/x-msvideo",
189       "bin"   => "application/octet-stream",
190       "bmp"   => "image/bmp",
191       "class" => "application/octet-stream",
192       "cer"   => "application/pkix-cert",
193       "crl"   => "application/pkix-crl",
194       "crt"   => "application/x-x509-ca-cert",
195      #"crl"   => "application/x-pkcs7-crl",
196       "css"   => "text/css",
197       "dms"   => "application/octet-stream",
198       "doc"   => "application/msword",
199       "dvi"   => "application/x-dvi",
200       "eps"   => "application/postscript",
201       "etx"   => "text/x-setext",
202       "exe"   => "application/octet-stream",
203       "gif"   => "image/gif",
204       "htm"   => "text/html",
205       "html"  => "text/html",
206       "jpe"   => "image/jpeg",
207       "jpeg"  => "image/jpeg",
208       "jpg"   => "image/jpeg",
209       "lha"   => "application/octet-stream",
210       "lzh"   => "application/octet-stream",
211       "mov"   => "video/quicktime",
212       "mpe"   => "video/mpeg",
213       "mpeg"  => "video/mpeg",
214       "mpg"   => "video/mpeg",
215       "pbm"   => "image/x-portable-bitmap",
216       "pdf"   => "application/pdf",
217       "pgm"   => "image/x-portable-graymap",
218       "png"   => "image/png",
219       "pnm"   => "image/x-portable-anymap",
220       "ppm"   => "image/x-portable-pixmap",
221       "ppt"   => "application/vnd.ms-powerpoint",
222       "ps"    => "application/postscript",
223       "qt"    => "video/quicktime",
224       "ras"   => "image/x-cmu-raster",
225       "rb"    => "text/plain",
226       "rd"    => "text/plain",
227       "rtf"   => "application/rtf",
228       "sgm"   => "text/sgml",
229       "sgml"  => "text/sgml",
230       "tif"   => "image/tiff",
231       "tiff"  => "image/tiff",
232       "txt"   => "text/plain",
233       "xbm"   => "image/x-xbitmap",
234       "xls"   => "application/vnd.ms-excel",
235       "xml"   => "text/xml",
236       "xpm"   => "image/x-xpixmap",
237       "xwd"   => "image/x-xwindowdump",
238       "zip"   => "application/zip",
239     }
240     # :startdoc:
241   
242   end  
243