FIX: StaticEvents should ignore directories during path recognition
[sinatra.git] / lib / sinatra / event.rb
blobd90545fc997150e0b5b4f5b22c6fdb0cfb2dc3dc
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.before_attend(method_name = nil, &block)
56       setup_filter(:before_filters, method_name, &block)
57     end
59     def self.after_attend(method_name = nil, &block)
60       setup_filter(:after_filters, method_name, &block)
61     end
62     
63     def self.setup_filter(filter_set_name, method_name, &block)
64       raise "Must specify method or block" if method_name.nil? and !block_given?
65       send(filter_set_name) << if block_given?
66         block
67       else
68         method_name
69       end
70     end
71       
72     after_attend :log_event
73     
74     attr_reader :path, :verb
75     
76     def initialize(verb, path, register = true, &block)
77       @verb = verb
78       @path = path
79       @route = Route.new(path)
80       @block = block
81       EventManager.register_event(self) if register
82     end
83     
84     def attend(request)
85       request.params.merge!(@route.params)
86       context = EventContext.new(request)
87       run_safely do
88         caught = catch(:halt) do
89           call_filters(before_filters, context)
90         end
91         body = case caught
92           when :filter_chain_completed
93             begin
94               context.instance_eval(&@block) if @block
95             rescue => e
96               context.error e
97             end
98           when Symbol
99             context.send(caught)
100           when String
101             caught
102           when Fixnum
103             context.status caught
104         end
105         context.body context.body || body || ''
106         call_filters(after_filters, context)
107       end
108       context
109     end
110     alias :call :attend
112     def recognize(path)
113       @route.recognize(path)
114     end
116     private
117     
118       def run_safely
119         if Options.use_mutex?
120           @@mutex.synchronize do
121             yield
122           end
123         else
124           yield
125         end
126       end
127       
128       # Adapted from Merb
129       # calls a filter chain according to rules.
130       def call_filters(filter_set, context)
131         filter_set.each do |filter|
132           case filter
133             when Symbol, String
134              context.send(filter)
135             when Proc
136              context.instance_eval(&filter)
137           end
138         end
139         :filter_chain_completed
140       end
141       
142   end
143   
144   class StaticEvent < Event # :nodoc:
145     
146     def initialize(path, root, register = true)
147       @root = root
148       super(:get, path, register)
149     end
151     def recognize(path)
152       filename = physical_path_for(path)
153       File.exists?(filename) && File.file?(filename)
154     end
155     
156     def physical_path_for(path)
157       path.gsub(/^#{@path}/, @root)
158     end
159     
160     def attend(request)
161       @filename = physical_path_for(request.path_info)
162       context = EventContext.new(request)
163       context.body self
164       context.header 'Content-Type' => MIME_TYPES[File.extname(@filename)[1..-1]]
165       context.header 'Content-Length' => File.size(@filename).to_s
166       context
167     end
168     
169     def each
170       File.open(@filename, "rb") do |file|
171         while part = file.read(8192)
172           yield part
173         end
174       end
175     end
176     
177     # :stopdoc:
178     # From WEBrick.
179     MIME_TYPES = {
180       "ai"    => "application/postscript",
181       "asc"   => "text/plain",
182       "avi"   => "video/x-msvideo",
183       "bin"   => "application/octet-stream",
184       "bmp"   => "image/bmp",
185       "class" => "application/octet-stream",
186       "cer"   => "application/pkix-cert",
187       "crl"   => "application/pkix-crl",
188       "crt"   => "application/x-x509-ca-cert",
189      #"crl"   => "application/x-pkcs7-crl",
190       "css"   => "text/css",
191       "dms"   => "application/octet-stream",
192       "doc"   => "application/msword",
193       "dvi"   => "application/x-dvi",
194       "eps"   => "application/postscript",
195       "etx"   => "text/x-setext",
196       "exe"   => "application/octet-stream",
197       "gif"   => "image/gif",
198       "htm"   => "text/html",
199       "html"  => "text/html",
200       "jpe"   => "image/jpeg",
201       "jpeg"  => "image/jpeg",
202       "jpg"   => "image/jpeg",
203       "lha"   => "application/octet-stream",
204       "lzh"   => "application/octet-stream",
205       "mov"   => "video/quicktime",
206       "mpe"   => "video/mpeg",
207       "mpeg"  => "video/mpeg",
208       "mpg"   => "video/mpeg",
209       "pbm"   => "image/x-portable-bitmap",
210       "pdf"   => "application/pdf",
211       "pgm"   => "image/x-portable-graymap",
212       "png"   => "image/png",
213       "pnm"   => "image/x-portable-anymap",
214       "ppm"   => "image/x-portable-pixmap",
215       "ppt"   => "application/vnd.ms-powerpoint",
216       "ps"    => "application/postscript",
217       "qt"    => "video/quicktime",
218       "ras"   => "image/x-cmu-raster",
219       "rb"    => "text/plain",
220       "rd"    => "text/plain",
221       "rtf"   => "application/rtf",
222       "sgm"   => "text/sgml",
223       "sgml"  => "text/sgml",
224       "tif"   => "image/tiff",
225       "tiff"  => "image/tiff",
226       "txt"   => "text/plain",
227       "xbm"   => "image/x-xbitmap",
228       "xls"   => "application/vnd.ms-excel",
229       "xml"   => "text/xml",
230       "xpm"   => "image/x-xpixmap",
231       "xwd"   => "image/x-xwindowdump",
232       "zip"   => "application/zip",
233     }
234     # :startdoc:
235   
236   end  
237