Optional mutex lock
[sinatra.git] / lib / sinatra / event.rb
blob7918e51d3c409b7c6f53e08281f803c9e60a78eb
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     
49     @@mutex = Mutex.new
50     
51     self.after_filters = []
52     
53     def self.after_attend(filter)
54       after_filters << filter
55     end
56     
57     after_attend :log_event
58     
59     attr_reader :path, :verb
60     
61     def initialize(verb, path, register = true, &block)
62       @verb = verb
63       @path = path
64       @route = Route.new(path)
65       @block = block
66       EventManager.register_event(self) if register
67     end
68     
69     def attend(request)
70       request.params.merge!(@route.params)
71       context = EventContext.new(request)
72       begin
73         result = run_safely { context.instance_eval(&@block) if @block }
74         context.body context.body || result || ''
75       rescue => e
76         context.error e
77       end
78       run_safely { run_through_after_filters(context) }
79       context
80     end
81     alias :call :attend
83     def recognize(path)
84       @route.recognize(path)
85     end
87     private
88     
89       def run_safely
90         if Options.use_mutex?
91           @@mutex.synchronize do
92             yield
93           end
94         else
95           yield
96         end
97       end
98   
99       def run_through_after_filters(context)
100         after_filters.each { |filter| context.send(filter) }
101       end
102       
103   end
104   
105   class StaticEvent < Event # :nodoc:
106     
107     def initialize(path, root, register = true)
108       @root = root
109       super(:get, path, register)
110     end
112     def recognize(path)
113       File.exists?(physical_path_for(path))
114     end
115     
116     def physical_path_for(path)
117       path.gsub(/^#{@path}/, @root)
118     end
119     
120     def attend(request)
121       @filename = physical_path_for(request.path_info)
122       context = EventContext.new(request)
123       context.body self
124       context.header 'Content-Type' => MIME_TYPES[File.extname(@filename)[1..-1]]
125       context.header 'Content-Length' => File.size(@filename).to_s
126       context
127     end
128     
129     def each
130       File.open(@filename, "rb") do |file|
131         while part = file.read(8192)
132           yield part
133         end
134       end
135     end
136     
137     # :stopdoc:
138     # From WEBrick.
139     MIME_TYPES = {
140       "ai"    => "application/postscript",
141       "asc"   => "text/plain",
142       "avi"   => "video/x-msvideo",
143       "bin"   => "application/octet-stream",
144       "bmp"   => "image/bmp",
145       "class" => "application/octet-stream",
146       "cer"   => "application/pkix-cert",
147       "crl"   => "application/pkix-crl",
148       "crt"   => "application/x-x509-ca-cert",
149      #"crl"   => "application/x-pkcs7-crl",
150       "css"   => "text/css",
151       "dms"   => "application/octet-stream",
152       "doc"   => "application/msword",
153       "dvi"   => "application/x-dvi",
154       "eps"   => "application/postscript",
155       "etx"   => "text/x-setext",
156       "exe"   => "application/octet-stream",
157       "gif"   => "image/gif",
158       "htm"   => "text/html",
159       "html"  => "text/html",
160       "jpe"   => "image/jpeg",
161       "jpeg"  => "image/jpeg",
162       "jpg"   => "image/jpeg",
163       "lha"   => "application/octet-stream",
164       "lzh"   => "application/octet-stream",
165       "mov"   => "video/quicktime",
166       "mpe"   => "video/mpeg",
167       "mpeg"  => "video/mpeg",
168       "mpg"   => "video/mpeg",
169       "pbm"   => "image/x-portable-bitmap",
170       "pdf"   => "application/pdf",
171       "pgm"   => "image/x-portable-graymap",
172       "png"   => "image/png",
173       "pnm"   => "image/x-portable-anymap",
174       "ppm"   => "image/x-portable-pixmap",
175       "ppt"   => "application/vnd.ms-powerpoint",
176       "ps"    => "application/postscript",
177       "qt"    => "video/quicktime",
178       "ras"   => "image/x-cmu-raster",
179       "rb"    => "text/plain",
180       "rd"    => "text/plain",
181       "rtf"   => "application/rtf",
182       "sgm"   => "text/sgml",
183       "sgml"  => "text/sgml",
184       "tif"   => "image/tiff",
185       "tiff"  => "image/tiff",
186       "txt"   => "text/plain",
187       "xbm"   => "image/x-xbitmap",
188       "xls"   => "application/vnd.ms-excel",
189       "xml"   => "text/xml",
190       "xpm"   => "image/x-xpixmap",
191       "xwd"   => "image/x-xwindowdump",
192       "zip"   => "application/zip",
193     }
194     # :startdoc:
195   
196   end  
197