public
Fork of bmizerany/sinatra
Description: Classy web-development dressed in a DSL (official / canonical repo)
Homepage: http://sinatra.github.com
Clone URL: git://github.com/sinatra/sinatra.git
sinatra / lib / sinatra / base.rb
88d91bdf » rtomayko 2009-02-03 Require 'thread' since we u... 1 require 'thread'
123cc5b5 » rtomayko 2009-01-06 Depend on Rack 0.9.0 (up fr... 2 require 'time'
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 3 require 'uri'
4 require 'rack'
5 require 'rack/builder'
ccd2e9d5 » rtomayko 2009-03-24 Rename showexceptions.rb fo... 6 require 'sinatra/showexceptions'
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 7
8 module Sinatra
3b9578f4 » rtomayko 2009-04-25 bump version to 0.9.1.3 and... 9 VERSION = '0.9.1.3'
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 10
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 11 # The request object. See Rack::Request for more info:
12 # http://rack.rubyforge.org/doc/classes/Rack/Request.html
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 13 class Request < Rack::Request
14 def user_agent
15 @env['HTTP_USER_AGENT']
16 end
38778edd » cypher 2009-01-07 Add filtering support for A... 17
d92d07b3 » cypher 2009-03-24 Add more API docs 18 # Returns an array of acceptable media types for the response
38778edd » cypher 2009-01-07 Add filtering support for A... 19 def accept
a1d9001a » Devlin Daley 2009-01-28 Fix :provides crashes with ... 20 @env['HTTP_ACCEPT'].to_s.split(',').map { |a| a.strip }
38778edd » cypher 2009-01-07 Add filtering support for A... 21 end
4a75d9ed » rtomayko 2009-01-15 Fix Request#params on PUT r... 22
23 # Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
24 def params
25 self.GET.update(self.POST)
26 rescue EOFError => boom
27 self.GET
28 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 29 end
30
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 31 # The response object. See Rack::Response and Rack::ResponseHelpers for
32 # more info:
33 # http://rack.rubyforge.org/doc/classes/Rack/Response.html
34 # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 35 class Response < Rack::Response
54b9ecf3 » rtomayko 2008-12-22 ensure that Content-Length ... 36 def finish
37 @body = block if block_given?
38 if [204, 304].include?(status.to_i)
39 header.delete "Content-Type"
40 [status.to_i, header.to_hash, []]
41 else
42 body = @body || []
43 body = [body] if body.respond_to? :to_str
7196ea41 » ichverstehe 2009-02-24 Fix issues with Content-Len... 44 if body.respond_to?(:to_ary)
54b9ecf3 » rtomayko 2008-12-22 ensure that Content-Length ... 45 header["Content-Length"] = body.to_ary.
c7dfca86 » rtomayko 2009-02-03 Calculate Content-Length us... 46 inject(0) { |len, part| len + part.bytesize }.to_s
54b9ecf3 » rtomayko 2008-12-22 ensure that Content-Length ... 47 end
48 [status.to_i, header.to_hash, body]
49 end
50 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 51 end
52
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 53 class NotFound < NameError #:nodoc:
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 54 def code ; 404 ; end
55 end
56
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 57 # Methods available to routes, before filters, and views.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 58 module Helpers
59 # Set or retrieve the response status code.
60 def status(value=nil)
61 response.status = value if value
62 response.status
63 end
64
65 # Set or retrieve the response body. When a block is given,
66 # evaluation is deferred until the body is read with #each.
67 def body(value=nil, &block)
68 if block_given?
69 def block.each ; yield call ; end
70 response.body = block
71 else
72 response.body = value
73 end
74 end
75
76 # Halt processing and redirect to the URI provided.
77 def redirect(uri, *args)
78 status 302
79 response['Location'] = uri
49c60151 » rtomayko 2009-01-09 Fix ruby warnings 80 halt(*args)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 81 end
82
83 # Halt processing and return the error status provided.
84 def error(code, body=nil)
585ea103 » bmizerany 2009-01-29 Being align anal Comment 85 code, body = 500, code.to_str if code.respond_to? :to_str
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 86 response.body = body unless body.nil?
87 halt code
88 end
89
90 # Halt processing and return a 404 Not Found.
91 def not_found(body=nil)
92 error 404, body
93 end
94
4fc9cb8b » rtomayko 2009-03-02 Undeprecate headers helper ... 95 # Set multiple response headers with Hash.
96 def headers(hash=nil)
97 response.headers.merge! hash if hash
98 response.headers
99 end
100
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 101 # Access the underlying Rack session.
102 def session
103 env['rack.session'] ||= {}
104 end
105
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 106 # Look up a media type by file extension in Rack's mime registry.
107 def media_type(type)
38778edd » cypher 2009-01-07 Add filtering support for A... 108 Base.media_type(type)
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 109 end
110
111 # Set the Content-Type of the response body given a media type or file
112 # extension.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 113 def content_type(type, params={})
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 114 media_type = self.media_type(type)
115 fail "Unknown media type: %p" % type if media_type.nil?
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 116 if params.any?
117 params = params.collect { |kv| "%s=%s" % kv }.join(', ')
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 118 response['Content-Type'] = [media_type, params].join(";")
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 119 else
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 120 response['Content-Type'] = media_type
121 end
122 end
123
124 # Set the Content-Disposition to "attachment" with the specified filename,
125 # instructing the user agents to prompt to save.
126 def attachment(filename=nil)
127 response['Content-Disposition'] = 'attachment'
128 if filename
129 params = '; filename="%s"' % File.basename(filename)
130 response['Content-Disposition'] << params
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 131 end
132 end
133
877bfb37 » rtomayko 2009-01-24 Fix send_file :disposition ... 134 # Use the contents of the file at +path+ as the response body.
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 135 def send_file(path, opts={})
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 136 stat = File.stat(path)
137 last_modified stat.mtime
877bfb37 » rtomayko 2009-01-24 Fix send_file :disposition ... 138
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 139 content_type media_type(opts[:type]) ||
140 media_type(File.extname(path)) ||
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 141 response['Content-Type'] ||
142 'application/octet-stream'
877bfb37 » rtomayko 2009-01-24 Fix send_file :disposition ... 143
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 144 response['Content-Length'] ||= (opts[:length] || stat.size).to_s
877bfb37 » rtomayko 2009-01-24 Fix send_file :disposition ... 145
146 if opts[:disposition] == 'attachment' || opts[:filename]
147 attachment opts[:filename] || path
148 elsif opts[:disposition] == 'inline'
149 response['Content-Disposition'] = 'inline'
150 end
151
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 152 halt StaticFile.open(path, 'rb')
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 153 rescue Errno::ENOENT
154 not_found
155 end
156
9dec74e3 » rtomayko 2009-03-24 Even more API docs 157 # Rack response body used to deliver static files. The file contents are
158 # generated iteratively in 8K chunks.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 159 class StaticFile < ::File #:nodoc:
160 alias_method :to_path, :path
161 def each
c0a2fbc8 » rtomayko 2009-01-21 Make static files work with... 162 rewind
a4a1ecf2 » splattael 2009-01-08 Use a power of 2 for send_f... 163 while buf = read(8192)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 164 yield buf
165 end
166 end
167 end
168
169 # Set the last modified time of the resource (HTTP 'Last-Modified' header)
170 # and halt if conditional GET matches. The +time+ argument is a Time,
171 # DateTime, or other object that responds to +to_time+.
172 #
173 # When the current request includes an 'If-Modified-Since' header that
174 # matches the time specified, execution is immediately halted with a
175 # '304 Not Modified' response.
176 def last_modified(time)
177 time = time.to_time if time.respond_to?(:to_time)
178 time = time.httpdate if time.respond_to?(:httpdate)
179 response['Last-Modified'] = time
180 halt 304 if time == request.env['HTTP_IF_MODIFIED_SINCE']
181 time
182 end
183
184 # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
185 # GET matches. The +value+ argument is an identifier that uniquely
186 # identifies the current version of the resource. The +strength+ argument
187 # indicates whether the etag should be used as a :strong (default) or :weak
188 # cache validator.
189 #
190 # When the current request includes an 'If-None-Match' header with a
191 # matching etag, execution is immediately halted. If the request method is
192 # GET or HEAD, a '304 Not Modified' response is sent.
ed923caf » rtomayko 2008-12-21 improve test coverage 193 def etag(value, kind=:strong)
194 raise TypeError, ":strong or :weak expected" if ![:strong,:weak].include?(kind)
195 value = '"%s"' % value
196 value = 'W/' + value if kind == :weak
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 197 response['ETag'] = value
198
ed923caf » rtomayko 2008-12-21 improve test coverage 199 # Conditional GET check
200 if etags = env['HTTP_IF_NONE_MATCH']
201 etags = etags.split(/\s*,\s*/)
202 halt 304 if etags.include?(value) || etags.include?('*')
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 203 end
204 end
8019e117 » bmizerany 2009-01-29 Sugar added: redirect back 205
206 ## Sugar for redirect (example: redirect back)
207 def back ; request.referer ; end
208
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 209 end
210
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 211 # Template rendering methods. Each method takes a the name of a template
d92d07b3 » cypher 2009-03-24 Add more API docs 212 # to render as a Symbol and returns a String with the rendered output,
213 # as well as an optional hash with additional options.
214 #
215 # `template` is either the name or path of the template as symbol
216 # (Use `:'subdir/myview'` for views in subdirectories), or a string
217 # that will be rendered.
218 #
219 # Possible options are:
220 # :layout If set to false, no layout is rendered, otherwise
221 # the specified layout is used (Ignored for `sass`)
222 # :locals A hash with local variables that should be available
223 # in the template
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 224 module Templates
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 225 def erb(template, options={}, locals={})
226 render :erb, template, options, locals
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 227 end
228
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 229 def haml(template, options={}, locals={})
230 render :haml, template, options, locals
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 231 end
232
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 233 def sass(template, options={}, locals={})
234 options[:layout] = false
235 render :sass, template, options, locals
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 236 end
237
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 238 def builder(template=nil, options={}, locals={}, &block)
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 239 options, template = template, nil if template.is_a?(Hash)
240 template = lambda { block } if template.nil?
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 241 render :builder, template, options, locals
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 242 end
243
244 private
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 245 def render(engine, template, options={}, locals={})
246 # merge app-level options
247 options = self.class.send(engine).merge(options) if self.class.respond_to?(engine)
248
249 # extract generic options
250 layout = options.delete(:layout)
251 layout = :layout if layout.nil? || layout == true
252 views = options.delete(:views) || self.class.views || "./views"
253 locals = options.delete(:locals) || locals || {}
254
255 # render template
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 256 data, options[:filename], options[:line] = lookup_template(engine, template, views)
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 257 output = __send__("render_#{engine}", template, data, options, locals)
258
259 # render layout
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 260 if layout
261 data, options[:filename], options[:line] = lookup_layout(engine, layout, views)
262 if data
263 output = __send__("render_#{engine}", layout, data, options, {}) { output }
264 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 265 end
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 266
267 output
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 268 end
269
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 270 def lookup_template(engine, template, views_dir, filename = nil, line = nil)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 271 case template
272 when Symbol
273 if cached = self.class.templates[template]
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 274 lookup_template(engine, cached[:template], views_dir, cached[:filename], cached[:line])
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 275 else
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 276 path = ::File.join(views_dir, "#{template}.#{engine}")
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 277 [ ::File.read(path), path, 1 ]
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 278 end
279 when Proc
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 280 filename, line = self.class.caller_locations.first if filename.nil?
281 [ template.call, filename, line.to_i ]
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 282 when String
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 283 filename, line = self.class.caller_locations.first if filename.nil?
284 [ template, filename, line.to_i ]
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 285 else
286 raise ArgumentError
287 end
288 end
289
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 290 def lookup_layout(engine, template, views_dir)
291 lookup_template(engine, template, views_dir)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 292 rescue Errno::ENOENT
293 nil
294 end
295
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 296 def render_erb(template, data, options, locals, &block)
090c4d79 » rtomayko 2009-02-21 Fix weirdness with partials... 297 original_out_buf = @_out_buf
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 298 data = data.call if data.kind_of? Proc
090c4d79 » rtomayko 2009-02-21 Fix weirdness with partials... 299
f3ed6e4d » Matias Käkelä 2009-02-18 Allow helper methods to use... 300 instance = ::ERB.new(data, nil, nil, '@_out_buf')
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 301 locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
090c4d79 » rtomayko 2009-02-21 Fix weirdness with partials... 302
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 303 filename = options.delete(:filename) || '(__ERB__)'
304 line = options.delete(:line) || 1
305 line -= 1 if instance.src =~ /^#coding:/
306
307 render_binding = binding
308 eval locals_assigns.join("\n"), render_binding
309 eval instance.src, render_binding, filename, line
090c4d79 » rtomayko 2009-02-21 Fix weirdness with partials... 310 @_out_buf, result = original_out_buf, @_out_buf
311 result
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 312 end
313
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 314 def render_haml(template, data, options, locals, &block)
315 ::Haml::Engine.new(data, options).render(self, locals, &block)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 316 end
317
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 318 def render_sass(template, data, options, locals, &block)
319 ::Sass::Engine.new(data, options).render
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 320 end
321
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 322 def render_builder(template, data, options, locals, &block)
323 options = { :indent => 2 }.merge(options)
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 324 filename = options.delete(:filename) || '<BUILDER>'
325 line = options.delete(:line) || 1
49adaa53 » rtomayko 2009-03-27 Sane template options [#191] 326 xml = ::Builder::XmlMarkup.new(options)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 327 if data.respond_to?(:to_str)
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 328 eval data.to_str, binding, filename, line
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 329 elsif data.kind_of?(Proc)
330 data.call(xml)
331 end
332 xml.target!
333 end
334 end
335
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 336 # Base class for all Sinatra applications and middleware.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 337 class Base
338 include Rack::Utils
339 include Helpers
340 include Templates
341
342 attr_accessor :app
343
344 def initialize(app=nil)
345 @app = app
346 yield self if block_given?
347 end
348
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 349 # Rack call interface.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 350 def call(env)
351 dup.call!(env)
352 end
353
354 attr_accessor :env, :request, :response, :params
355
356 def call!(env)
6a4fd6a3 » bmizerany 2009-01-07 pretty equals 357 @env = env
358 @request = Request.new(env)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 359 @response = Response.new
6a4fd6a3 » bmizerany 2009-01-07 pretty equals 360 @params = nil
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 361
362 invoke { dispatch! }
363 invoke { error_block!(response.status) }
364
7196ea41 » ichverstehe 2009-02-24 Fix issues with Content-Len... 365 status, header, body = @response.finish
98427cef » rtomayko 2009-02-25 Better Content-Length handl... 366
367 # Never produce a body on HEAD requests. Do retain the Content-Length
368 # unless it's "0", in which case we assume it was calculated erroneously
369 # for a manual HEAD response and remove it entirely.
370 if @env['REQUEST_METHOD'] == 'HEAD'
371 body = []
372 header.delete('Content-Length') if header['Content-Length'] == '0'
373 end
374
7196ea41 » ichverstehe 2009-02-24 Fix issues with Content-Len... 375 [status, header, body]
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 376 end
377
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 378 # Access options defined with Base.set.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 379 def options
380 self.class
381 end
382
d92d07b3 » cypher 2009-03-24 Add more API docs 383 # Exit the current block, halts any further processing
384 # of the request, and returns the specified response.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 385 def halt(*response)
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 386 response = response.first if response.length == 1
387 throw :halt, response
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 388 end
389
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 390 # Pass control to the next matching route.
d92d07b3 » cypher 2009-03-24 Add more API docs 391 # If there are no more matching routes, Sinatra will
392 # return a 404 response.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 393 def pass
394 throw :pass
395 end
396
cf32f2e6 » rtomayko 2009-02-22 Middleware enhancements: au... Comment 397 # Forward the request to the downstream app -- middleware only.
398 def forward
399 fail "downstream app not set" unless @app.respond_to? :call
400 status, headers, body = @app.call(@request.env)
401 @response.status = status
402 @response.body = body
4fc9cb8b » rtomayko 2009-03-02 Undeprecate headers helper ... 403 @response.headers.merge! headers
cf32f2e6 » rtomayko 2009-02-22 Middleware enhancements: au... Comment 404 nil
405 end
406
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 407 private
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 408 # Run before filters and then locate and run a matching route.
409 def route!
eb18c9be » reinh 2009-04-08 Use Rack 1.0's built in nes... 410 # enable nested params in Rack < 1.0; allow indifferent access
411 @params =
412 if Rack::Utils.respond_to?(:parse_nested_query)
413 indifferent_params(@request.params)
414 else
415 nested_params(@request.params)
416 end
8dbd919f » bmizerany 2009-01-24 Give access to GET/POST par... 417
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 418 # before filters
419 self.class.filters.each { |block| instance_eval(&block) }
9acfa0a3 » rtomayko 2009-01-18 Halting a before block shou... 420
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 421 # routes
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 422 if routes = self.class.routes[@request.request_method]
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 423 original_params = @params
d212de6a » rtomayko 2009-02-17 Fix routes don't match with... 424 path = unescape(@request.path_info)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 425
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 426 routes.each do |pattern, keys, conditions, block|
bc7a939c » rtomayko 2009-01-19 General spec coverage impro... 427 if match = pattern.match(path)
d212de6a » rtomayko 2009-02-17 Fix routes don't match with... 428 values = match.captures.to_a
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 429 params =
430 if keys.any?
431 keys.zip(values).inject({}) do |hash,(k,v)|
432 if k == 'splat'
433 (hash[k] ||= []) << v
434 else
435 hash[k] = v
436 end
437 hash
438 end
439 elsif values.any?
440 {'captures' => values}
441 else
442 {}
443 end
1fa9807f » foca 2009-01-15 Nested params (e.g., "post[... 444 @params = original_params.merge(params)
6569d1b0 » bdimcheff 2009-01-28 Added route block params in... 445 @block_params = values
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 446
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 447 catch(:pass) do
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 448 conditions.each { |cond|
449 throw :pass if instance_eval(&cond) == false }
1a0bdae8 » visionmedia 2009-03-26 Added Base#route_eval for h... Comment 450 route_eval(&block)
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 451 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 452 end
453 end
454 end
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 455
8644b307 » rtomayko 2009-03-26 Base#route_missing - replac... 456 route_missing
457 end
458
1a0bdae8 » visionmedia 2009-03-26 Added Base#route_eval for h... Comment 459 # Run a route block and throw :halt with the result.
460 def route_eval(&block)
461 throw :halt, instance_eval(&block)
462 end
463
8644b307 » rtomayko 2009-03-26 Base#route_missing - replac... 464 # No matching route was found or all routes passed. The default
465 # implementation is to forward the request downstream when running
466 # as middleware (@app is non-nil); when no downstream app is set, raise
467 # a NotFound exception. Subclasses can override this method to perform
468 # custom route miss logic.
469 def route_missing
cf32f2e6 » rtomayko 2009-02-22 Middleware enhancements: au... Comment 470 if @app
471 forward
472 else
473 raise NotFound
474 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 475 end
476
eb18c9be » reinh 2009-04-08 Use Rack 1.0's built in nes... 477 # Enable string or symbol key access to the nested params hash.
478 def indifferent_params(params)
479 params = indifferent_hash.merge(params)
480 params.each do |key, value|
481 next unless value.is_a?(Hash)
482 params[key] = indifferent_params(value)
483 end
484 end
485
486 # Recursively replace the params hash with a nested indifferent
487 # hash. Rack 1.0 has a built in implementation of this method - remove
488 # this once Rack 1.0 is required.
1fa9807f » foca 2009-01-15 Nested params (e.g., "post[... 489 def nested_params(params)
490 return indifferent_hash.merge(params) if !params.keys.join.include?('[')
491 params.inject indifferent_hash do |res, (key,val)|
5771f101 » rtomayko 2009-01-25 Fast(er) and simpler nested... 492 if key.include?('[')
493 head = key.split(/[\]\[]+/)
494 last = head.pop
495 head.inject(res){ |hash,k| hash[k] ||= indifferent_hash }[last] = val
40cd096b » nakajima 2009-01-21 Preserve non-nested params... 496 else
497 res[key] = val
1fa9807f » foca 2009-01-15 Nested params (e.g., "post[... 498 end
499 res
500 end
501 end
502
503 def indifferent_hash
504 Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
505 end
506
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 507 # Run the block with 'throw :halt' support and apply result to the response.
508 def invoke(&block)
15863661 » rtomayko 2009-01-16 Get rid of check for Unboun... 509 res = catch(:halt) { instance_eval(&block) }
8a4d1a0b » rtomayko 2009-01-18 Fix redirect/halt in before... 510 return if res.nil?
511
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 512 case
513 when res.respond_to?(:to_str)
514 @response.body = [res]
515 when res.respond_to?(:to_ary)
516 res = res.to_ary
517 if Fixnum === res.first
518 if res.length == 3
519 @response.status, headers, body = res
520 @response.body = body if body
521 headers.each { |k, v| @response.headers[k] = v } if headers
522 elsif res.length == 2
523 @response.status = res.first
585ea103 » bmizerany 2009-01-29 Being align anal Comment 524 @response.body = res.last
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 525 else
526 raise TypeError, "#{res.inspect} not supported"
527 end
528 else
529 @response.body = res
530 end
531 when res.respond_to?(:each)
532 @response.body = res
533 when (100...599) === res
534 @response.status = res
535 end
9acfa0a3 » rtomayko 2009-01-18 Halting a before block shou... 536
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 537 res
538 end
539
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 540 # Dispatch a request with error handling.
541 def dispatch!
542 route!
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 543 rescue NotFound => boom
f76353a3 » sr 2009-01-26 Split Base#dispatch into sm... 544 handle_not_found!(boom)
545 rescue ::Exception => boom
546 handle_exception!(boom)
547 end
548
549 def handle_not_found!(boom)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 550 @env['sinatra.error'] = boom
585ea103 » bmizerany 2009-01-29 Being align anal Comment 551 @response.status = 404
552 @response.body = ['<h1>Not Found</h1>']
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 553 error_block! boom.class, NotFound
f76353a3 » sr 2009-01-26 Split Base#dispatch into sm... 554 end
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 555
f76353a3 » sr 2009-01-26 Split Base#dispatch into sm... 556 def handle_exception!(boom)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 557 @env['sinatra.error'] = boom
b5faa571 » rtomayko 2009-01-09 Dump backtrace to rack.erro... 558
f76353a3 » sr 2009-01-26 Split Base#dispatch into sm... 559 dump_errors!(boom) if options.dump_errors?
2cbc4912 » rtomayko 2009-03-24 Always raise errors when sh... 560 raise boom if options.raise_errors? || options.show_exceptions?
b5faa571 » rtomayko 2009-01-09 Dump backtrace to rack.erro... 561
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 562 @response.status = 500
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 563 error_block! boom.class, Exception
564 end
565
566 # Find an custom error block for the key(s) specified.
567 def error_block!(*keys)
568 errmap = self.class.errors
569 keys.each do |key|
570 if block = errmap[key]
571 res = instance_eval(&block)
572 return res
573 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 574 end
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 575 nil
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 576 end
577
f76353a3 » sr 2009-01-26 Split Base#dispatch into sm... 578 def dump_errors!(boom)
579 backtrace = clean_backtrace(boom.backtrace)
580 msg = ["#{boom.class} - #{boom.message}:",
581 *backtrace].join("\n ")
582 @env['rack.errors'].write(msg)
583 end
584
8f6743fb » sr 2009-01-21 Clean framework/lib paths i... 585 def clean_backtrace(trace)
586 return trace unless options.clean_trace?
587
588 trace.reject { |line|
589 line =~ /lib\/sinatra.*\.rb/ ||
590 (defined?(Gem) && line.include?(Gem.dir))
591 }.map! { |line| line.gsub(/^\.\//, '') }
592 end
593
88321f25 » bmizerany 2009-01-06 pretty equals 594 @routes = {}
595 @filters = []
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 596 @conditions = []
88321f25 » bmizerany 2009-01-06 pretty equals 597 @templates = {}
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 598 @middleware = []
88321f25 » bmizerany 2009-01-06 pretty equals 599 @errors = {}
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 600 @prototype = nil
8b8ddfef » bmizerany 2009-03-09 Hook mechanizm for route_ad... Comment 601 @extensions = []
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 602
603 class << self
604 attr_accessor :routes, :filters, :conditions, :templates,
605 :middleware, :errors
606
d92d07b3 » cypher 2009-03-24 Add more API docs 607 # Sets an option to the given value. If the value is a proc,
608 # the proc will be called every time the option is accessed.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 609 def set(option, value=self)
610 if value.kind_of?(Proc)
611 metadef(option, &value)
612 metadef("#{option}?") { !!__send__(option) }
613 metadef("#{option}=") { |val| set(option, Proc.new{val}) }
614 elsif value == self && option.respond_to?(:to_hash)
c8f1c7cb » rtomayko 2009-01-30 Fix passing a method as a b... 615 option.to_hash.each { |k,v| set(k, v) }
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 616 elsif respond_to?("#{option}=")
617 __send__ "#{option}=", value
618 else
619 set option, Proc.new{value}
620 end
621 self
622 end
623
d92d07b3 » cypher 2009-03-24 Add more API docs 624 # Same as calling `set :option, true` for each of the given options.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 625 def enable(*opts)
626 opts.each { |key| set(key, true) }
627 end
628
d92d07b3 » cypher 2009-03-24 Add more API docs 629 # Same as calling `set :option, false` for each of the given options.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 630 def disable(*opts)
631 opts.each { |key| set(key, false) }
632 end
633
d92d07b3 » cypher 2009-03-24 Add more API docs 634 # Define a custom error handler. Optionally takes either an Exception
635 # class, or an HTTP status code to specify which errors should be
636 # handled.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 637 def error(codes=Exception, &block)
638 if codes.respond_to? :each
639 codes.each { |err| error(err, &block) }
640 else
641 @errors[codes] = block
642 end
643 end
644
d92d07b3 » cypher 2009-03-24 Add more API docs 645 # Sugar for `error(404) { ... }`
f41472f8 » rtomayko 2009-01-10 Deprecation warnings for 0.... 646 def not_found(&block)
647 error 404, &block
648 end
649
9dec74e3 » rtomayko 2009-03-24 Even more API docs 650 # Define a named template. The block must return the template source.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 651 def template(name, &block)
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 652 filename, line = caller_locations.first
653 templates[name] = { :filename => filename, :line => line, :template => block }
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 654 end
655
9dec74e3 » rtomayko 2009-03-24 Even more API docs 656 # Define the layout template. The block must return the template source.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 657 def layout(name=:layout, &block)
658 template name, &block
659 end
660
9dec74e3 » rtomayko 2009-03-24 Even more API docs 661 # Load embeded templates from the file; uses the caller's __FILE__
662 # when no file is specified.
271a421b » rtomayko 2009-03-11 Fix activesupport borkednes... Comment 663 def use_in_file_templates!(file=nil)
664 file ||= caller_files.first
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 665 app, data =
666 ::IO.read(file).split(/^__END__$/, 2) rescue nil
b88c0f57 » Samuel Goebert 2009-04-09 Fixed an uncought exception... 667
668 if data
49c60151 » rtomayko 2009-01-09 Fix ruby warnings 669 data.gsub!(/\r\n/, "\n")
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 670 lines = app.count("\n") + 1
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 671 template = nil
672 data.each_line do |line|
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 673 lines += 1
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 674 if line =~ /^@@\s*(.*)/
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 675 template = ''
676 templates[$1.to_sym] = { :filename => file, :line => lines, :template => template }
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 677 elsif template
678 template << line
679 end
680 end
681 end
682 end
683
38778edd » cypher 2009-01-07 Add filtering support for A... 684 # Look up a media type by file extension in Rack's mime registry.
685 def media_type(type)
686 return type if type.nil? || type.to_s.include?('/')
687 type = ".#{type}" unless type.to_s[0] == ?.
688 Rack::Mime.mime_type(type, nil)
689 end
690
9dec74e3 » rtomayko 2009-03-24 Even more API docs 691 # Define a before filter. Filters are run before all requests
692 # within the same context as route handlers and may access/modify the
693 # request and response.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 694 def before(&block)
9acfa0a3 » rtomayko 2009-01-18 Halting a before block shou... 695 @filters << block
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 696 end
697
9dec74e3 » rtomayko 2009-03-24 Even more API docs 698 # Add a route condition. The route is considered non-matching when the
699 # block returns false.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 700 def condition(&block)
701 @conditions << block
702 end
703
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 704 private
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 705 def host_name(pattern)
706 condition { pattern === request.host }
707 end
708
709 def user_agent(pattern)
710 condition {
711 if request.user_agent =~ pattern
712 @params[:agent] = $~[1..-1]
713 true
714 else
715 false
716 end
717 }
718 end
719
38778edd » cypher 2009-01-07 Add filtering support for A... 720 def accept_mime_types(types)
721 types = [types] unless types.kind_of? Array
722 types.map!{|t| media_type(t)}
723
724 condition {
725 matching_types = (request.accept & types)
726 unless matching_types.empty?
727 response.headers['Content-Type'] = matching_types.first
728 true
729 else
730 false
731 end
732 }
733 end
734
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 735 public
d92d07b3 » cypher 2009-03-24 Add more API docs 736 # Defining a `GET` handler also automatically defines
737 # a `HEAD` handler.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 738 def get(path, opts={}, &block)
739 conditions = @conditions.dup
d3936ad0 » rtomayko 2009-01-16 Fix route leaks; uses Unbou... 740 route('GET', path, opts, &block)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 741
742 @conditions = conditions
0ade0bee » rtomayko 2009-01-24 Much needed refactoring in ... 743 route('HEAD', path, opts, &block)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 744 end
745
ed9c3b37 » sr 2009-04-13 Code alignment 746 def put(path, opts={}, &bk); route 'PUT', path, opts, &bk end
747 def post(path, opts={}, &bk); route 'POST', path, opts, &bk end
748 def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk end
749 def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 750
751 private
d3936ad0 » rtomayko 2009-01-16 Fix route leaks; uses Unbou... 752 def route(verb, path, opts={}, &block)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 753 host_name opts[:host] if opts.key?(:host)
754 user_agent opts[:agent] if opts.key?(:agent)
38778edd » cypher 2009-01-07 Add filtering support for A... 755 accept_mime_types opts[:provides] if opts.key?(:provides)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 756
757 pattern, keys = compile(path)
758 conditions, @conditions = @conditions, []
524f78b3 » bmizerany 2009-01-08 Define routes as methods on... 759
d3936ad0 » rtomayko 2009-01-16 Fix route leaks; uses Unbou... 760 define_method "#{verb} #{path}", &block
761 unbound_method = instance_method("#{verb} #{path}")
dfcb2527 » rtomayko 2009-02-02 Fix block param arity handl... 762 block =
763 if block.arity != 0
764 lambda { unbound_method.bind(self).call(*@block_params) }
765 else
766 lambda { unbound_method.bind(self).call }
767 end
524f78b3 » bmizerany 2009-01-08 Define routes as methods on... 768
d6ceeb0b » visionmedia 2009-04-01 Added proc as argument for ... 769 invoke_hook(:route_added, verb, path, block)
8b8ddfef » bmizerany 2009-03-09 Hook mechanizm for route_ad... Comment 770
d3936ad0 » rtomayko 2009-01-16 Fix route leaks; uses Unbou... 771 (routes[verb] ||= []).
15863661 » rtomayko 2009-01-16 Get rid of check for Unboun... 772 push([pattern, keys, conditions, block]).last
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 773 end
774
8b8ddfef » bmizerany 2009-03-09 Hook mechanizm for route_ad... Comment 775 def invoke_hook(name, *args)
776 extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
777 end
778
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 779 def compile(path)
780 keys = []
781 if path.respond_to? :to_str
12eced84 » cypher 2009-01-22 Escape '.', '+', '$' and pa... 782 special_chars = %w{. + ( )}
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 783 pattern =
d212de6a » rtomayko 2009-02-17 Fix routes don't match with... 784 path.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
12eced84 » cypher 2009-01-22 Escape '.', '+', '$' and pa... 785 case match
786 when "*"
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 787 keys << 'splat'
788 "(.*?)"
12eced84 » cypher 2009-01-22 Escape '.', '+', '$' and pa... 789 when *special_chars
790 Regexp.escape(match)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 791 else
792 keys << $2[1..-1]
a2f5803e » rtomayko 2009-02-17 Allow dot in named param ca... 793 "([^/?&#]+)"
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 794 end
795 end
796 [/^#{pattern}$/, keys]
bc7a939c » rtomayko 2009-01-19 General spec coverage impro... 797 elsif path.respond_to? :match
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 798 [path, keys]
799 else
800 raise TypeError, path
801 end
802 end
803
804 public
d92d07b3 » cypher 2009-03-24 Add more API docs 805 # Makes the methods defined in the block and in the Modules given
806 # in `extensions` available to the handlers and templates
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 807 def helpers(*extensions, &block)
eba6de69 » rtomayko 2009-02-22 Add registered callback for... 808 class_eval(&block) if block_given?
809 include *extensions if extensions.any?
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 810 end
811
8b8ddfef » bmizerany 2009-03-09 Hook mechanizm for route_ad... Comment 812 def extensions
813 (@extensions + (superclass.extensions rescue [])).uniq
814 end
815
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 816 def register(*extensions, &block)
eba6de69 » rtomayko 2009-02-22 Add registered callback for... 817 extensions << Module.new(&block) if block_given?
8b8ddfef » bmizerany 2009-03-09 Hook mechanizm for route_ad... Comment 818 @extensions += extensions
eba6de69 » rtomayko 2009-02-22 Add registered callback for... 819 extensions.each do |extension|
820 extend extension
821 extension.registered(self) if extension.respond_to?(:registered)
822 end
c094757b » foca 2009-01-26 Add #helpers into Sinatra::... Comment 823 end
824
ed9c3b37 » sr 2009-04-13 Code alignment 825 def development?; environment == :development end
826 def production?; environment == :production end
827 def test?; environment == :test end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 828
d92d07b3 » cypher 2009-03-24 Add more API docs 829 # Set configuration options for Sinatra and/or the app.
830 # Allows scoping of settings for certain environments.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 831 def configure(*envs, &block)
54794fc8 » visionmedia 2009-03-31 Pass the class to configure... 832 yield self if envs.empty? || envs.include?(environment.to_sym)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 833 end
834
d92d07b3 » cypher 2009-03-24 Add more API docs 835 # Use the specified Rack middleware
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 836 def use(middleware, *args, &block)
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 837 @prototype = nil
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 838 @middleware << [middleware, args, block]
839 end
840
d92d07b3 » cypher 2009-03-24 Add more API docs 841 # Run the Sinatra app as a self-hosted server using
842 # Thin, Mongrel or WEBrick (in that order)
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 843 def run!(options={})
e1f8b4e2 » rtomayko 2009-01-16 Fallback on mongrel then we... 844 set options
585ea103 » bmizerany 2009-01-29 Being align anal Comment 845 handler = detect_rack_handler
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 846 handler_name = handler.name.gsub(/.*::/, '')
847 puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
42cb875d » sam-github 2009-02-17 Put less stuff on STDOUT wh... 848 "on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 849 handler.run self, :Host => host, :Port => port do |server|
850 trap(:INT) do
da604ea8 » bmizerany 2009-01-06 FIX #58: Thin needs a hard... 851 ## Use thins' hard #stop! if available, otherwise just #stop
852 server.respond_to?(:stop!) ? server.stop! : server.stop
42cb875d » sam-github 2009-02-17 Put less stuff on STDOUT wh... 853 puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 854 end
855 end
856 rescue Errno::EADDRINUSE => e
857 puts "== Someone is already performing on port #{port}!"
858 end
859
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 860 # The prototype instance used to process requests.
861 def prototype
862 @prototype ||= new
863 end
864
865 # Create a new instance of the class fronted by its middleware
866 # pipeline. The object is guaranteed to respond to #call but may not be
867 # an instance of the class new was called on.
868 def new(*args, &bk)
869 builder = Rack::Builder.new
08b142b4 » rtomayko 2009-02-26 Never use Session middlewar... 870 builder.use Rack::Session::Cookie if sessions? && !test?
2cbc4912 » rtomayko 2009-03-24 Always raise errors when sh... 871 builder.use Rack::CommonLogger if logging?
872 builder.use Rack::MethodOverride if methodoverride?
873 builder.use ShowExceptions if show_exceptions?
60bdca96 » sr 2009-01-23 Initial Sinatra-specific Sh... 874
d5f2028d » rtomayko 2009-03-24 Remove any chance of block ... 875 @middleware.each { |c,a,b| builder.use(c, *a, &b) }
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 876 builder.run super
877 builder.to_app
878 end
879
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 880 def call(env)
13fc79d3 » rtomayko 2009-03-24 Remove support for source f... 881 synchronize { prototype.call(env) }
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 882 end
883
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 884 def reset!(base=superclass)
885 @routes = base.dupe_routes
886 @templates = base.templates.dup
887 @conditions = []
888 @filters = base.filters.dup
889 @errors = base.errors.dup
890 @middleware = base.middleware.dup
891 @prototype = nil
8b8ddfef » bmizerany 2009-03-09 Hook mechanizm for route_ad... Comment 892 @extensions = []
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 893 end
894
895 protected
896 def dupe_routes
897 routes.inject({}) do |hash,(request_method,routes)|
898 hash[request_method] = routes.dup
899 hash
900 end
901 end
902
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 903 private
e1f8b4e2 » rtomayko 2009-01-16 Fallback on mongrel then we... 904 def detect_rack_handler
905 servers = Array(self.server)
906 servers.each do |server_name|
907 begin
e9cc0f71 » reinh 2009-04-08 Rack::Handler.get expects c... 908 return Rack::Handler.get(server_name.capitalize)
e1f8b4e2 » rtomayko 2009-01-16 Fallback on mongrel then we... 909 rescue LoadError
bc7a939c » rtomayko 2009-01-19 General spec coverage impro... 910 rescue NameError
e1f8b4e2 » rtomayko 2009-01-16 Fallback on mongrel then we... 911 end
912 end
913 fail "Server handler (#{servers.join(',')}) not found."
914 end
915
df1ce65d » raggi 2009-02-04 Moved reloading and mutex l... Comment 916 def inherited(subclass)
d7eabb97 » rtomayko 2009-02-26 Fix middleware not running ... 917 subclass.reset! self
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 918 super
919 end
920
df1ce65d » raggi 2009-02-04 Moved reloading and mutex l... Comment 921 @@mutex = Mutex.new
922 def synchronize(&block)
923 if lock?
924 @@mutex.synchronize(&block)
925 else
926 yield
927 end
928 end
929
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 930 def metadef(message, &block)
931 (class << self; self; end).
932 send :define_method, message, &block
933 end
271a421b » rtomayko 2009-03-11 Fix activesupport borkednes... Comment 934
cfdf97d4 » sbfaulkner 2009-04-05 template backtraces ftw [#1... 935 public
9d674494 » sbfaulkner 2009-04-05 refactor caller_files 936 CALLERS_TO_IGNORE = [
937 /lib\/sinatra.*\.rb$/, # all sinatra code
938 /\(.*\)/, # generated code
939 /custom_require\.rb$/, # rubygems require hacks
940 /active_support/, # active_support require hacks
941 ] unless self.const_defined?('CALLERS_TO_IGNORE')
942
043e987a » rtomayko 2009-04-30 Add RUBY_IGNORE_CALLERS to ... 943 # add rubinius (and hopefully other VM impls) ignore patterns ...
944 CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
945
271a421b » rtomayko 2009-03-11 Fix activesupport borkednes... Comment 946 # Like Kernel#caller but excluding certain magic entries and without
947 # line / method information; the resulting array contains filenames only.
948 def caller_files
9d674494 » sbfaulkner 2009-04-05 refactor caller_files 949 caller_locations.
950 map { |file,line| file }
951 end
952
953 def caller_locations
271a421b » rtomayko 2009-03-11 Fix activesupport borkednes... Comment 954 caller(1).
9d674494 » sbfaulkner 2009-04-05 refactor caller_files 955 map { |line| line.split(/:(?=\d|in )/)[0,2] }.
956 reject { |file,line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
271a421b » rtomayko 2009-03-11 Fix activesupport borkednes... Comment 957 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 958 end
959
960 set :raise_errors, true
b5faa571 » rtomayko 2009-01-09 Dump backtrace to rack.erro... 961 set :dump_errors, false
8f6743fb » sr 2009-01-21 Clean framework/lib paths i... 962 set :clean_trace, true
60bdca96 » sr 2009-01-23 Initial Sinatra-specific Sh... 963 set :show_exceptions, Proc.new { development? }
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 964 set :sessions, false
965 set :logging, false
966 set :methodoverride, false
967 set :static, false
968 set :environment, (ENV['RACK_ENV'] || :development).to_sym
969
970 set :run, false
e1f8b4e2 » rtomayko 2009-01-16 Fallback on mongrel then we... 971 set :server, %w[thin mongrel webrick]
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 972 set :host, '0.0.0.0'
973 set :port, 4567
974
975 set :app_file, nil
976 set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
977 set :views, Proc.new { root && File.join(root, 'views') }
978 set :public, Proc.new { root && File.join(root, 'public') }
13fc79d3 » rtomayko 2009-03-24 Remove support for source f... 979 set :lock, false
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 980
981 # static files route
982 get(/.*[^\/]$/) do
983 pass unless options.static? && options.public?
ed98aed3 » rtomayko 2009-03-09 Fix directory traversal vul... 984 public_dir = File.expand_path(options.public)
985 path = File.expand_path(public_dir + unescape(request.path_info))
986 pass if path[0, public_dir.length] != public_dir
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 987 pass unless File.file?(path)
1c124f5d » rtomayko 2008-12-22 unbreak send_file compat sp... 988 send_file path, :disposition => nil
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 989 end
990
991 error ::Exception do
992 response.status = 500
993 content_type 'text/html'
994 '<h1>Internal Server Error</h1>'
995 end
996
997 configure :development do
998 get '/__sinatra__/:image.png' do
999 filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
1000 content_type :png
1001 send_file filename
1002 end
1003
1004 error NotFound do
87bf495b » sr 2009-04-17 Ensure development NotFound... 1005 content_type 'text/html'
1006
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1007 (<<-HTML).gsub(/^ {8}/, '')
1008 <!DOCTYPE html>
1009 <html>
1010 <head>
1011 <style type="text/css">
1012 body { text-align:center;font-family:helvetica,arial;font-size:22px;
1013 color:#888;margin:20px}
1014 #c {margin:0 auto;width:500px;text-align:left}
1015 </style>
1016 </head>
1017 <body>
1018 <h2>Sinatra doesn't know this ditty.</h2>
1019 <img src='/__sinatra__/404.png'>
1020 <div id="c">
1021 Try this:
1022 <pre>#{request.request_method.downcase} '#{request.path_info}' do\n "Hello World"\nend</pre>
1023 </div>
1024 </body>
1025 </html>
1026 HTML
1027 end
1028 end
1029 end
1030
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1031 # Base class for classic style (top-level) applications.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1032 class Default < Base
2fdecaee » sr 2009-02-14 Add full test coverage for ... 1033 set :raise_errors, Proc.new { test? }
b5faa571 » rtomayko 2009-01-09 Dump backtrace to rack.erro... 1034 set :dump_errors, true
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1035 set :sessions, false
5a33a958 » sr 2009-02-22 Set sensible defaults on De... 1036 set :logging, Proc.new { ! test? }
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1037 set :methodoverride, true
1038 set :static, true
5a33a958 » sr 2009-02-22 Set sensible defaults on De... 1039 set :run, Proc.new { ! test? }
dbdc13bf » bmizerany 2009-01-06 Cleaning up (class << self;... 1040
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1041 def self.register(*extensions, &block) #:nodoc:
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 1042 added_methods = extensions.map {|m| m.public_instance_methods }.flatten
1043 Delegator.delegate *added_methods
1044 super(*extensions, &block)
1045 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1046 end
1047
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1048 # The top-level Application. All DSL methods executed on main are delegated
1049 # to this class.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1050 class Application < Default
1051 end
1052
9dec74e3 » rtomayko 2009-03-24 Even more API docs 1053 # Sinatra delegation mixin. Mixing this module into an object causes all
1054 # methods to be delegated to the Sinatra::Application class. Used primarily
1055 # at the top-level.
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1056 module Delegator #:nodoc:
64c8297f » foca 2009-01-25 Refactor Sinatra::Delegator... 1057 def self.delegate(*methods)
1058 methods.each do |method_name|
1059 eval <<-RUBY, binding, '(__DELEGATE__)', 1
1060 def #{method_name}(*args, &b)
c9b2f6b7 » nakajima 2009-03-04 Fixed extension method dele... 1061 ::Sinatra::Application.send(#{method_name.inspect}, *args, &b)
64c8297f » foca 2009-01-25 Refactor Sinatra::Delegator... 1062 end
c9b2f6b7 » nakajima 2009-03-04 Fixed extension method dele... 1063 private #{method_name.inspect}
64c8297f » foca 2009-01-25 Refactor Sinatra::Delegator... 1064 RUBY
1065 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1066 end
64c8297f » foca 2009-01-25 Refactor Sinatra::Delegator... 1067
1068 delegate :get, :put, :post, :delete, :head, :template, :layout, :before,
1069 :error, :not_found, :configures, :configure, :set, :set_option,
1070 :set_options, :enable, :disable, :use, :development?, :test?,
c094757b » foca 2009-01-26 Add #helpers into Sinatra::... Comment 1071 :production?, :use_in_file_templates!, :helpers
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1072 end
1073
9dec74e3 » rtomayko 2009-03-24 Even more API docs 1074 # Create a new Sinatra application. The block is evaluated in the new app's
1075 # class scope.
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1076 def self.new(base=Base, options={}, &block)
1077 base = Class.new(base)
1078 base.send :class_eval, &block if block_given?
1079 base
1080 end
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 1081
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1082 # Extend the top-level DSL with the modules provided.
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 1083 def self.register(*extensions, &block)
1084 Default.register(*extensions, &block)
1085 end
1086
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1087 # Include the helper modules provided in Sinatra's request context.
57183bc0 » foca 2009-01-28 Added Sinatra.register and ... 1088 def self.helpers(*extensions, &block)
1089 Default.helpers(*extensions, &block)
1090 end
a734cf38 » rtomayko 2008-12-13 I knew I shoulda taken that... 1091 end
b9afcd57 » rtomayko 2009-01-30 Add String#each to make Rac... 1092
3f513fa1 » rtomayko 2009-02-05 Tidy up for RDoc a bit 1093 class String #:nodoc:
c7dfca86 » rtomayko 2009-02-03 Calculate Content-Length us... 1094 # Define String#each under 1.9 for Rack compatibility. This should be
1095 # removed once Rack is fully 1.9 compatible.
1096 alias_method :each, :each_line unless ''.respond_to? :each
1097
1098 # Define String#bytesize as an alias to String#length for Ruby 1.8.6 and
1099 # earlier.
1100 alias_method :bytesize, :length unless ''.respond_to? :bytesize
b9afcd57 » rtomayko 2009-01-30 Add String#each to make Rac... 1101 end