adamwiggins / sinatra forked from bmizerany/sinatra

Classy web-development dressed in a DSL

This URL has Read+Write access

sinatra / lib / sinatra.rb
983358b3 » Blake Mizerany 2008-04-15 Freezing Rack into package ... 1 Dir[File.dirname(__FILE__) + "/../vendor/*"].each do |l|
2 $:.unshift "#{File.expand_path(l)}/lib"
3 end
4
5 require 'rack'
6
18d3c871 » Blake Mizerany 2007-11-27 more tests 7 require 'rubygems'
9a5d69a8 » rtomayko 2008-03-26 If-Modified-Since support f... 8 require 'time'
983358b3 » Blake Mizerany 2008-04-15 Freezing Rack into package ... 9 require 'ostruct'
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 10 require "uri"
703cf3b2 » Blake Mizerany 2007-11-28 now running 11
12 if ENV['SWIFT']
13 require 'swiftcore/swiftiplied_mongrel'
14 puts "Using Swiftiplied Mongrel"
15 elsif ENV['EVENT']
16 require 'swiftcore/evented_mongrel'
17 puts "Using Evented Mongrel"
18 end
19
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 20 class Class
15d360e8 » Markus Prinz 2008-04-07 Fix spelling of dslify_writer 21 def dslify_writer(*syms)
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 22 syms.each do |sym|
23 class_eval <<-end_eval
24 def #{sym}(v=nil)
25 self.send "#{sym}=", v if v
26 v
27 end
28 end_eval
29 end
30 end
31 end
32
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 33 module Rack #:nodoc:
34
35 class Request #:nodoc:
b18102be » rtomayko 2008-03-12 Fix a few minor issues with... 36
37 # Set of request method names allowed via the _method parameter hack. By default,
38 # all request methods defined in RFC2616 are included, with the exception of
39 # TRACE and CONNECT.
40 POST_TUNNEL_METHODS_ALLOWED = %w( PUT DELETE OPTIONS HEAD )
41
42 # Return the HTTP request method with support for method tunneling using the POST
43 # _method parameter hack. If the real request method is POST and a _method param is
44 # given and the value is one defined in +POST_TUNNEL_METHODS_ALLOWED+, return the value
45 # of the _method param instead.
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 46 def request_method
b18102be » rtomayko 2008-03-12 Fix a few minor issues with... 47 if post_tunnel_method_hack?
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 48 params['_method'].upcase
49 else
50 @env['REQUEST_METHOD']
51 end
52 end
b18102be » rtomayko 2008-03-12 Fix a few minor issues with... 53
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 54 def user_agent
9c875ffd » Blake Mizerany 2008-04-20 Session testing made easy 55 @env['HTTP_USER_AGENT']
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 56 end
b18102be » rtomayko 2008-03-12 Fix a few minor issues with... 57
9c413351 » adamwiggins 2008-06-08 :accept option to filter on... 58 def accept
59 @env['HTTP_ACCEPT'].split(',').map { |a| a.strip }
60 end
61
b18102be » rtomayko 2008-03-12 Fix a few minor issues with... 62 private
63
64 # Return truthfully if and only if the following conditions are met: 1.) the
65 # *actual* request method is POST, 2.) the request content-type is one of
66 # 'application/x-www-form-urlencoded' or 'multipart/form-data', 3.) there is a
67 # "_method" parameter in the POST body (not in the query string), and 4.) the
68 # method parameter is one of the verbs listed in the POST_TUNNEL_METHODS_ALLOWED
69 # list.
70 def post_tunnel_method_hack?
71 @env['REQUEST_METHOD'] == 'POST' &&
72 POST_TUNNEL_METHODS_ALLOWED.include?(self.POST.fetch('_method', '').upcase)
73 end
74
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 75 end
76
327c6ece » rtomayko 2008-03-08 Fix that built-in error mes... 77 module Utils
78 extend self
79 end
80
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 81 end
82
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 83 module Sinatra
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 84 extend self
095d95e3 » Blake Mizerany 2007-11-27 refactored 85
1776a80d » Blake Mizerany 2008-03-24 Added Version and Docs 86 module Version
87 MAJOR = '0'
88 MINOR = '2'
08b1452a » bmizerany 2008-05-07 getting ready for 0.2.3 89 REVISION = '3'
1776a80d » Blake Mizerany 2008-03-24 Added Version and Docs 90 def self.combined
91 [MAJOR, MINOR, REVISION].join('.')
92 end
93 end
94
1f07ba27 » Blake Mizerany 2008-02-24 No more Error codes. Map t... 95 class NotFound < RuntimeError; end
96 class ServerError < RuntimeError; end
97
4a246966 » Blake Mizerany 2007-11-29 Custom 404 98 Result = Struct.new(:block, :params, :status) unless defined?(Result)
e0c7f978 » Blake Mizerany 2008-03-27 easy access to options 99
100 def options
101 application.options
102 end
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 103
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 104 def application
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 105 @app ||= Application.new
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 106 end
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 107
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 108 def application=(app)
109 @app = app
110 end
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 111
703cf3b2 » Blake Mizerany 2007-11-28 now running 112 def port
113 application.options.port
114 end
115
116 def env
117 application.options.env
118 end
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 119
120 # Deprecated: use application instead of build_application.
121 alias :build_application :application
122
f747bfe4 » Blake Mizerany 2008-04-14 pick a server, any server! 123 def server
7526dacf » cypher 2008-05-20 Simplify the Sinatra.server... 124 options.server ||= defined?(Rack::Handler::Thin) ? "thin" : "mongrel"
125 # Convert the server into the actual handler name
126 handler = options.server.capitalize.sub(/cgi$/, 'CGI')
127 @server ||= eval("Rack::Handler::#{handler}")
f747bfe4 » Blake Mizerany 2008-04-14 pick a server, any server! 128 end
129
703cf3b2 » Blake Mizerany 2007-11-28 now running 130 def run
131 begin
f747bfe4 » Blake Mizerany 2008-04-14 pick a server, any server! 132 puts "== Sinatra has taken the stage on port #{port} for #{env} with backup by #{server.name}"
703cf3b2 » Blake Mizerany 2007-11-28 now running 133 require 'pp'
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 134 server.run(application, :Port => port) do |server|
703cf3b2 » Blake Mizerany 2007-11-28 now running 135 trap(:INT) do
136 server.stop
137 puts "\n== Sinatra has ended his set (crowd applauds)"
138 end
139 end
140 rescue Errno::EADDRINUSE => e
141 puts "== Someone is already performing on port #{port}!"
142 end
143 end
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 144
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 145 class Event
18d3c871 » Blake Mizerany 2007-11-27 more tests 146
9ee50b30 » Blake Mizerany 2007-11-29 * Default error messages 147 URI_CHAR = '[^/?:,&#\.]'.freeze unless defined?(URI_CHAR)
9c85e99c » vic 2008-04-24 Specs, documentation and fi... 148 PARAM = /(:(#{URI_CHAR}+)|\*)/.freeze unless defined?(PARAM)
bf896df2 » Blake Mizerany 2008-01-11 * Splat works 149 SPLAT = /(.*?)/
150 attr_reader :path, :block, :param_keys, :pattern, :options
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 151
bf896df2 » Blake Mizerany 2008-01-11 * Splat works 152 def initialize(path, options = {}, &b)
53c6609f » Markus Prinz 2008-04-11 Deal correctly with paths t... 153 @path = URI.encode(path)
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 154 @block = b
18d3c871 » Blake Mizerany 2007-11-27 more tests 155 @param_keys = []
bf896df2 » Blake Mizerany 2008-01-11 * Splat works 156 @options = options
9c85e99c » vic 2008-04-24 Specs, documentation and fi... 157 splats = 0
158 regex = @path.to_s.gsub(PARAM) do |match|
159 if match == "*"
160 @param_keys << "_splat_#{splats}"
161 splats += 1
162 SPLAT.to_s
163 else
164 @param_keys << $2
165 "(#{URI_CHAR}+)"
166 end
18d3c871 » Blake Mizerany 2007-11-27 more tests 167 end
9c85e99c » vic 2008-04-24 Specs, documentation and fi... 168
18d3c871 » Blake Mizerany 2007-11-27 more tests 169 @pattern = /^#{regex}$/
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 170 end
18d3c871 » Blake Mizerany 2007-11-27 more tests 171
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 172 def invoke(request)
0b485a96 » Blake Mizerany 2008-03-24 capture agent captures in p... 173 params = {}
d71fe830 » Blake Mizerany 2008-04-01 filter on host 174 if agent = options[:agent]
175 return unless request.user_agent =~ agent
0b485a96 » Blake Mizerany 2008-03-24 capture agent captures in p... 176 params[:agent] = $~[1..-1]
bf896df2 » Blake Mizerany 2008-01-11 * Splat works 177 end
d71fe830 » Blake Mizerany 2008-04-01 filter on host 178 if host = options[:host]
179 return unless host === request.host
180 end
9c413351 » adamwiggins 2008-06-08 :accept option to filter on... 181 if accept = options[:accept]
182 return unless request.accept.include? accept
183 end
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 184 return unless pattern =~ request.path_info.squeeze('/')
0b485a96 » Blake Mizerany 2008-03-24 capture agent captures in p... 185 params.merge!(param_keys.zip($~.captures.map(&:from_param)).to_hash)
9c85e99c » vic 2008-04-24 Specs, documentation and fi... 186 splats = params.select { |k, v| k =~ /^_splat_\d+$/ }.sort.map(&:last)
187 unless splats.empty?
188 params.delete_if { |k, v| k =~ /^_splat_\d+$/ }
189 params["splat"] = splats
190 end
4a246966 » Blake Mizerany 2007-11-29 Custom 404 191 Result.new(block, params, 200)
095d95e3 » Blake Mizerany 2007-11-27 refactored 192 end
193
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 194 end
195
4a246966 » Blake Mizerany 2007-11-29 Custom 404 196 class Error
197
198 attr_reader :code, :block
199
200 def initialize(code, &b)
201 @code, @block = code, b
202 end
203
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 204 def invoke(request)
4a246966 » Blake Mizerany 2007-11-29 Custom 404 205 Result.new(block, {}, 404)
206 end
53856731 » Blake Mizerany 2007-11-27 in context 207
4a246966 » Blake Mizerany 2007-11-29 Custom 404 208 end
209
13d13b21 » Blake Mizerany 2007-12-02 static files 210 class Static
211
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 212 def invoke(request)
13d13b21 » Blake Mizerany 2007-12-02 static files 213 return unless File.file?(
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 214 Sinatra.application.options.public + request.path_info.http_unescape
13d13b21 » Blake Mizerany 2007-12-02 static files 215 )
216 Result.new(block, {}, 200)
217 end
218
219 def block
220 Proc.new do
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 221 send_file Sinatra.application.options.public + request.path_info.http_unescape,
bacfa20d » rtomayko 2008-03-26 Make Static omit Content-Di... 222 :disposition => nil
13d13b21 » Blake Mizerany 2007-12-02 static files 223 end
224 end
225
226 end
227
fbdf982f » Blake Mizerany 2008-02-24 Streaming 228 # Adapted from actionpack
229 # Methods for sending files and streams to the browser instead of rendering.
230 module Streaming
231 DEFAULT_SEND_FILE_OPTIONS = {
232 :type => 'application/octet-stream'.freeze,
233 :disposition => 'attachment'.freeze,
234 :stream => true,
235 :buffer_size => 4096
236 }.freeze
237
1bcd28fa » Blake Mizerany 2008-03-31 merge in env to test methods 238 class MissingFile < RuntimeError; end
239
fbdf982f » Blake Mizerany 2008-02-24 Streaming 240 class FileStreamer
241
242 attr_reader :path, :options
243
244 def initialize(path, options)
245 @path, @options = path, options
246 end
247
1f204991 » Blake Mizerany 2008-03-24 getting mare advanced with ... 248 def to_result(cx, *args)
249 self
fbdf982f » Blake Mizerany 2008-02-24 Streaming 250 end
251
252 def each
253 File.open(path, 'rb') do |file|
254 while buf = file.read(options[:buffer_size])
255 yield buf
256 end
257 end
258 end
259
260 end
261
262 protected
263 # Sends the file by streaming it 4096 bytes at a time. This way the
264 # whole file doesn't need to be read into memory at once. This makes
265 # it feasible to send even large files.
266 #
267 # Be careful to sanitize the path parameter if it coming from a web
268 # page. send_file(params[:path]) allows a malicious user to
269 # download any file on your server.
270 #
271 # Options:
272 # * <tt>:filename</tt> - suggests a filename for the browser to use.
273 # Defaults to File.basename(path).
274 # * <tt>:type</tt> - specifies an HTTP content type.
275 # Defaults to 'application/octet-stream'.
276 # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
bacfa20d » rtomayko 2008-03-26 Make Static omit Content-Di... 277 # Valid values are 'inline' and 'attachment' (default). When set to nil, the
278 # Content-Disposition and Content-Transfer-Encoding headers are omitted entirely.
fbdf982f » Blake Mizerany 2008-02-24 Streaming 279 # * <tt>:stream</tt> - whether to send the file to the user agent as it is read (true)
280 # or to read the entire file before sending (false). Defaults to true.
281 # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
282 # Defaults to 4096.
283 # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
9a5d69a8 » rtomayko 2008-03-26 If-Modified-Since support f... 284 # * <tt>:last_modified</tt> - an optional RFC 2616 formatted date value (See Time#httpdate)
285 # indicating the last modified time of the file. If the request includes an
286 # If-Modified-Since header that matches this value exactly, a 304 Not Modified response
287 # is sent instead of the file. Defaults to the file's last modified
288 # time.
fbdf982f » Blake Mizerany 2008-02-24 Streaming 289 #
290 # The default Content-Type and Content-Disposition headers are
291 # set to download arbitrary binary files in as many browsers as
292 # possible. IE versions 4, 5, 5.5, and 6 are all known to have
293 # a variety of quirks (especially when downloading over SSL).
294 #
295 # Simple download:
296 # send_file '/path/to.zip'
297 #
298 # Show a JPEG in the browser:
299 # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
300 #
301 # Show a 404 page in the browser:
302 # send_file '/path/to/404.html, :type => 'text/html; charset=utf-8', :status => 404
303 #
304 # Read about the other Content-* HTTP headers if you'd like to
305 # provide the user with more information (such as Content-Description).
306 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
307 #
308 # Also be aware that the document may be cached by proxies and browsers.
309 # The Pragma and Cache-Control headers declare how the file may be cached
310 # by intermediaries. They default to require clients to validate with
311 # the server before releasing cached responses. See
312 # http://www.mnot.net/cache_docs/ for an overview of web caching and
313 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
314 # for the Cache-Control header spec.
315 def send_file(path, options = {}) #:doc:
316 raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
317
318 options[:length] ||= File.size(path)
319 options[:filename] ||= File.basename(path)
320 options[:type] ||= Rack::File::MIME_TYPES[File.extname(options[:filename])[1..-1]] || 'text/plain'
9a5d69a8 » rtomayko 2008-03-26 If-Modified-Since support f... 321 options[:last_modified] ||= File.mtime(path).httpdate
fbdf982f » Blake Mizerany 2008-02-24 Streaming 322 send_file_headers! options
323
324 if options[:stream]
325 throw :halt, [options[:status] || 200, FileStreamer.new(path, options)]
326 else
327 File.open(path, 'rb') { |file| throw :halt, [options[:status] || 200, file.read] }
328 end
329 end
330
331 # Send binary data to the user as a file download. May set content type, apparent file name,
332 # and specify whether to show data inline or download as an attachment.
333 #
334 # Options:
335 # * <tt>:filename</tt> - Suggests a filename for the browser to use.
336 # * <tt>:type</tt> - specifies an HTTP content type.
337 # Defaults to 'application/octet-stream'.
338 # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
339 # Valid values are 'inline' and 'attachment' (default).
340 # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
9a5d69a8 » rtomayko 2008-03-26 If-Modified-Since support f... 341 # * <tt>:last_modified</tt> - an optional RFC 2616 formatted date value (See Time#httpdate)
342 # indicating the last modified time of the response entity. If the request includes an
343 # If-Modified-Since header that matches this value exactly, a 304 Not Modified response
344 # is sent instead of the data.
fbdf982f » Blake Mizerany 2008-02-24 Streaming 345 #
346 # Generic data download:
347 # send_data buffer
348 #
349 # Download a dynamically-generated tarball:
350 # send_data generate_tgz('dir'), :filename => 'dir.tgz'
351 #
352 # Display an image Active Record in the browser:
353 # send_data image.data, :type => image.content_type, :disposition => 'inline'
354 #
355 # See +send_file+ for more information on HTTP Content-* headers and caching.
356 def send_data(data, options = {}) #:doc:
357 send_file_headers! options.merge(:length => data.size)
358 throw :halt, [options[:status] || 200, data]
359 end
360
361 private
362 def send_file_headers!(options)
bacfa20d » rtomayko 2008-03-26 Make Static omit Content-Di... 363 options = DEFAULT_SEND_FILE_OPTIONS.merge(options)
fbdf982f » Blake Mizerany 2008-02-24 Streaming 364 [:length, :type, :disposition].each do |arg|
bacfa20d » rtomayko 2008-03-26 Make Static omit Content-Di... 365 raise ArgumentError, ":#{arg} option required" unless options.key?(arg)
fbdf982f » Blake Mizerany 2008-02-24 Streaming 366 end
367
9a5d69a8 » rtomayko 2008-03-26 If-Modified-Since support f... 368 # Send a "304 Not Modified" if the last_modified option is provided and matches
369 # the If-Modified-Since request header value.
370 if last_modified = options[:last_modified]
371 header 'Last-Modified' => last_modified
372 throw :halt, [ 304, '' ] if last_modified == request.env['HTTP_IF_MODIFIED_SINCE']
373 end
374
fbdf982f » Blake Mizerany 2008-02-24 Streaming 375 headers(
376 'Content-Length' => options[:length].to_s,
bacfa20d » rtomayko 2008-03-26 Make Static omit Content-Di... 377 'Content-Type' => options[:type].strip # fixes a problem with extra '\r' with some browsers
fbdf982f » Blake Mizerany 2008-02-24 Streaming 378 )
379
bacfa20d » rtomayko 2008-03-26 Make Static omit Content-Di... 380 # Omit Content-Disposition and Content-Transfer-Encoding headers if
381 # the :disposition option set to nil.
382 if !options[:disposition].nil?
383 disposition = options[:disposition].dup || 'attachment'
384 disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
385 headers 'Content-Disposition' => disposition, 'Content-Transfer-Encoding' => 'binary'
386 end
387
fbdf982f » Blake Mizerany 2008-02-24 Streaming 388 # Fix a problem with IE 6.0 on opening downloaded files:
389 # If Cache-Control: no-cache is set (which Rails does by default),
390 # IE removes the file it just downloaded from its cache immediately
391 # after it displays the "open/save" dialog, which means that if you
392 # hit "open" the file isn't there anymore when the application that
393 # is called for handling the download is run, so let's workaround that
394 header('Cache-Control' => 'private') if headers['Cache-Control'] == 'no-cache'
395 end
396 end
d343d27d » rtomayko 2008-04-13 document and further spec o... 397
398
399 # Helper methods for building various aspects of the HTTP response.
4a246966 » Blake Mizerany 2007-11-29 Custom 404 400 module ResponseHelpers
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 401
d343d27d » rtomayko 2008-04-13 document and further spec o... 402 # Immediately halt response execution by redirecting to the resource
403 # specified. The +path+ argument may be an absolute URL or a path
404 # relative to the site root. Additional arguments are passed to the
405 # halt.
406 #
407 # With no integer status code, a '302 Temporary Redirect' response is
408 # sent. To send a permanent redirect, pass an explicit status code of
409 # 301:
410 #
411 # redirect '/somewhere/else', 301
412 #
413 # NOTE: No attempt is made to rewrite the path based on application
414 # context. The 'Location' response header is set verbatim to the value
415 # provided.
d41cccae » Blake Mizerany 2008-02-20 less code = great code 416 def redirect(path, *args)
417 status(302)
d343d27d » rtomayko 2008-04-13 document and further spec o... 418 header 'Location' => path
d41cccae » Blake Mizerany 2008-02-20 less code = great code 419 throw :halt, *args
4a246966 » Blake Mizerany 2007-11-29 Custom 404 420 end
d343d27d » rtomayko 2008-04-13 document and further spec o... 421
422 # Access or modify response headers. With no argument, return the
423 # underlying headers Hash. With a Hash argument, add or overwrite
424 # existing response headers with the values provided:
425 #
ccc19b04 » rtomayko 2008-04-13 content_type response helpe... 426 # headers 'Content-Type' => "text/html;charset=utf-8",
d343d27d » rtomayko 2008-04-13 document and further spec o... 427 # 'Last-Modified' => Time.now.httpdate,
428 # 'X-UA-Compatible' => 'IE=edge'
429 #
430 # This method also available in singular form (#header).
038b6b7d » Blake Mizerany 2008-02-17 Quick changes 431 def headers(header = nil)
432 @response.headers.merge!(header) if header
433 @response.headers
434 end
435 alias :header :headers
436
ccc19b04 » rtomayko 2008-04-13 content_type response helpe... 437 # Set the content type of the response body (HTTP 'Content-Type' header).
438 #
439 # The +type+ argument may be an internet media type (e.g., 'text/html',
440 # 'application/xml+atom', 'image/png') or a Symbol key into the
441 # Rack::File::MIME_TYPES table.
442 #
443 # Media type parameters, such as "charset", may also be specified using the
444 # optional hash argument:
445 #
446 # get '/foo.html' do
447 # content_type 'text/html', :charset => 'utf-8'
448 # "<h1>Hello World</h1>"
449 # end
450 #
451 def content_type(type, params={})
452 type = Rack::File::MIME_TYPES[type.to_s] if type.kind_of?(Symbol)
453 fail "Invalid or undefined media_type: #{type}" if type.nil?
454 if params.any?
540aa5fa » Blake Mizerany 2008-04-15 Revert "refactored content_... 455 params = params.collect { |kv| "%s=%s" % kv }.join(', ')
ccc19b04 » rtomayko 2008-04-13 content_type response helpe... 456 type = [ type, params ].join(";")
457 end
458 response.header['Content-Type'] = type
459 end
460
302a03cc » rtomayko 2008-03-31 last_modified response help... 461 # Set the last modified time of the resource (HTTP 'Last-Modified' header)
462 # and halt if conditional GET matches. The +time+ argument is a Time,
463 # DateTime, or other object that responds to +to_time+.
464 #
465 # When the current request includes an 'If-Modified-Since' header that
466 # matches the time specified, execution is immediately halted with a
467 # '304 Not Modified' response.
468 #
469 # Calling this method before perfoming heavy processing (e.g., lengthy
470 # database queries, template rendering, complex logic) can dramatically
471 # increase overall throughput with caching clients.
472 def last_modified(time)
473 time = time.to_time if time.respond_to?(:to_time)
474 time = time.httpdate if time.respond_to?(:httpdate)
475 response.header['Last-Modified'] = time
476 throw :halt, 304 if time == request.env['HTTP_IF_MODIFIED_SINCE']
477 time
478 end
479
ce4cf34e » rtomayko 2008-04-13 entity_tag response helper ... 480 # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
481 # GET matches. The +value+ argument is an identifier that uniquely
482 # identifies the current version of the resource. The +strength+ argument
483 # indicates whether the etag should be used as a :strong (default) or :weak
484 # cache validator.
485 #
486 # When the current request includes an 'If-None-Match' header with a
487 # matching etag, execution is immediately halted. If the request method is
488 # GET or HEAD, a '304 Not Modified' response is sent. For all other request
489 # methods, a '412 Precondition Failed' response is sent.
490 #
491 # Calling this method before perfoming heavy processing (e.g., lengthy
492 # database queries, template rendering, complex logic) can dramatically
493 # increase overall throughput with caching clients.
494 #
418db1ed » rtomayko 2008-05-19 minor rdoc formatting fixes 495 # ==== See Also
496 # {RFC2616: ETag}[http://w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19],
ce4cf34e » rtomayko 2008-04-13 entity_tag response helper ... 497 # ResponseHelpers#last_modified
498 def entity_tag(value, strength=:strong)
499 value =
500 case strength
501 when :strong then '"%s"' % value
502 when :weak then 'W/"%s"' % value
503 else raise TypeError, "strength must be one of :strong or :weak"
504 end
505 response.header['ETag'] = value
506
507 # Check for If-None-Match request header and halt if match is found.
508 etags = (request.env['HTTP_IF_NONE_MATCH'] || '').split(/\s*,\s*/)
509 if etags.include?(value) || etags.include?('*')
510 # GET/HEAD requests: send Not Modified response
511 throw :halt, 304 if request.get? || request.head?
512 # Other requests: send Precondition Failed response
513 throw :halt, 412
514 end
515 end
516
517 alias :etag :entity_tag
518
4a246966 » Blake Mizerany 2007-11-29 Custom 404 519 end
ccc19b04 » rtomayko 2008-04-13 content_type response helpe... 520
4a246966 » Blake Mizerany 2007-11-29 Custom 404 521 module RenderingHelpers
becd6d8a » Blake Mizerany 2008-02-20 ERB in place 522
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 523 def render(renderer, template, options={})
524 m = method("render_#{renderer}")
3d92391a » Blake Mizerany 2008-03-22 pass locals to haml 525 result = m.call(resolve_template(renderer, template, options), options)
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 526 if layout = determine_layout(renderer, template, options)
3d92391a » Blake Mizerany 2008-03-22 pass locals to haml 527 result = m.call(resolve_template(renderer, layout, options), options) { result }
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 528 end
529 result
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 530 end
531
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 532 def determine_layout(renderer, template, options)
898b36ea » Blake Mizerany 2008-03-15 allow user to prevent layou... 533 return if options[:layout] == false
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 534 layout_from_options = options[:layout] || :layout
5e857124 » Blake Mizerany 2008-03-28 tests for use_in_file_templ... 535 resolve_template(renderer, layout_from_options, options, false)
520ac2e9 » Blake Mizerany 2008-02-24 Haml 536 end
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 537
520ac2e9 » Blake Mizerany 2008-02-24 Haml 538 private
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 539
ccbb0703 » Blake Mizerany 2008-02-28 FIX: Render without layout 540 def resolve_template(renderer, template, options, scream = true)
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 541 case template
4a246966 » Blake Mizerany 2007-11-29 Custom 404 542 when String
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 543 template
4a246966 » Blake Mizerany 2007-11-29 Custom 404 544 when Proc
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 545 template.call
fbdf982f » Blake Mizerany 2008-02-24 Streaming 546 when Symbol
6bda16b9 » Blake Mizerany 2008-03-26 added `template` to DSL 547 if proc = templates[template]
d874133b » Blake Mizerany 2008-03-26 inline templates! 548 resolve_template(renderer, proc, options, scream)
6bda16b9 » Blake Mizerany 2008-03-26 added `template` to DSL 549 else
d874133b » Blake Mizerany 2008-03-26 inline templates! 550 read_template_file(renderer, template, options, scream)
ccbb0703 » Blake Mizerany 2008-02-28 FIX: Render without layout 551 end
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 552 else
553 nil
2f6269f6 » Blake Mizerany 2007-11-28 named partials 554 end
4a246966 » Blake Mizerany 2007-11-29 Custom 404 555 end
2f6269f6 » Blake Mizerany 2007-11-28 named partials 556
d874133b » Blake Mizerany 2008-03-26 inline templates! 557 def read_template_file(renderer, template, options, scream = true)
558 path = File.join(
559 options[:views_directory] || Sinatra.application.options.views,
560 "#{template}.#{renderer}"
561 )
562 unless File.exists?(path)
563 raise Errno::ENOENT.new(path) if scream
564 nil
565 else
566 File.read(path)
567 end
568 end
569
6bda16b9 » Blake Mizerany 2008-03-26 added `template` to DSL 570 def templates
571 Sinatra.application.templates
4a246966 » Blake Mizerany 2007-11-29 Custom 404 572 end
573
574 end
575
520ac2e9 » Blake Mizerany 2008-02-24 Haml 576 module Erb
577
578 def erb(content, options={})
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 579 require 'erb'
580 render(:erb, content, options)
520ac2e9 » Blake Mizerany 2008-02-24 Haml 581 end
582
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 583 private
584
3d92391a » Blake Mizerany 2008-03-22 pass locals to haml 585 def render_erb(content, options = {})
c2827e33 » cschneid 2008-05-27 Add :locals option for erb.... 586 locals_opt = options.delete(:locals) || {}
587
588 locals_code = ""
589 locals_hash = {}
590 locals_opt.each do |key, value|
591 locals_code << "#{key} = locals_hash[:#{key}]\n"
592 locals_hash[:"#{key}"] = value
593 end
594
595 body = ::ERB.new(content).src
596 eval("#{locals_code}#{body}", binding)
520ac2e9 » Blake Mizerany 2008-02-24 Haml 597 end
c2827e33 » cschneid 2008-05-27 Add :locals option for erb.... 598
520ac2e9 » Blake Mizerany 2008-02-24 Haml 599 end
600
601 module Haml
602
603 def haml(content, options={})
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 604 require 'haml'
605 render(:haml, content, options)
520ac2e9 » Blake Mizerany 2008-02-24 Haml 606 end
607
608 private
609
3d92391a » Blake Mizerany 2008-03-22 pass locals to haml 610 def render_haml(content, options = {}, &b)
f80203ad » Blake Mizerany 2008-04-19 Let's keep default_options ... 611 haml_options = (options[:options] || {}).merge(Sinatra.options.haml || {})
23999054 » sr 2008-04-19 Add the possibility to conf... 612 ::Haml::Engine.new(content, haml_options).render(options[:scope] || self, options[:locals] || {}, &b)
7d5bc1f0 » Blake Mizerany 2008-02-27 Haml & Erb redo 613 end
614
520ac2e9 » Blake Mizerany 2008-02-24 Haml 615 end
caf48570 » rtomayko 2008-03-08 Builder Rendering Helper (.... 616
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 617 # Generate valid CSS using Sass (part of Haml)
618 #
95ca3f0c » nmeans 2008-04-08 Modified sass method to all... 619 # Sass templates can be in external files with <tt>.sass</tt> extension or can use Sinatra's
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 620 # in_file_templates. In either case, the file can be rendered by passing the name of
621 # the template to the +sass+ method as a symbol.
622 #
95ca3f0c » nmeans 2008-04-08 Modified sass method to all... 623 # Unlike Haml, Sass does not support a layout file, so the +sass+ method will ignore both
624 # the default <tt>layout.sass</tt> file and any parameters passed in as <tt>:layout</tt> in
625 # the options hash.
626 #
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 627 # === Sass Template Files
628 #
95ca3f0c » nmeans 2008-04-08 Modified sass method to all... 629 # Sass templates can be stored in separate files with a <tt>.sass</tt>
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 630 # extension under the view path.
631 #
632 # Example:
633 # get '/stylesheet.css' do
634 # header 'Content-Type' => 'text/css; charset=utf-8'
635 # sass :stylesheet
636 # end
637 #
638 # The "views/stylesheet.sass" file might contain the following:
639 #
640 # body
641 # #admin
642 # :background-color #CCC
643 # #main
644 # :background-color #000
645 # #form
646 # :border-color #AAA
647 # :border-width 10px
648 #
649 # And yields the following output:
650 #
651 # body #admin {
652 # background-color: #CCC; }
653 # body #main {
654 # background-color: #000; }
655 #
656 # #form {
657 # border-color: #AAA;
658 # border-width: 10px; }
659 #
660 #
661 # NOTE: Haml must be installed or a LoadError will be raised the first time an
662 # attempt is made to render a Sass template.
663 #
664 # See http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html for comprehensive documentation on Sass.
665
666
667 module Sass
668
95ca3f0c » nmeans 2008-04-08 Modified sass method to all... 669 def sass(content, options = {})
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 670 require 'sass'
95ca3f0c » nmeans 2008-04-08 Modified sass method to all... 671
672 # Sass doesn't support a layout, so we override any possible layout here
673 options[:layout] = false
674
675 render(:sass, content, options)
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 676 end
677
678 private
679
680 def render_sass(content, options = {})
681 ::Sass::Engine.new(content).render
682 end
683 end
684
caf48570 » rtomayko 2008-03-08 Builder Rendering Helper (.... 685 # Generating conservative XML content using Builder templates.
686 #
687 # Builder templates can be inline by passing a block to the builder method, or in
688 # external files with +.builder+ extension by passing the name of the template
689 # to the +builder+ method as a Symbol.
690 #
691 # === Inline Rendering
692 #
693 # If the builder method is given a block, the block is called directly with an
694 # +XmlMarkup+ instance and the result is returned as String:
695 # get '/who.xml' do
696 # builder do |xml|
697 # xml.instruct!
698 # xml.person do
699 # xml.name "Francis Albert Sinatra",
700 # :aka => "Frank Sinatra"
701 # xml.email 'frank@capitolrecords.com'
702 # end
703 # end
704 # end
705 #
706 # Yields the following XML:
707 # <?xml version='1.0' encoding='UTF-8'?>
708 # <person>
709 # <name aka='Frank Sinatra'>Francis Albert Sinatra</name>
710 # <email>Frank Sinatra</email>
711 # </person>
712 #
713 # === Builder Template Files
714 #
715 # Builder templates can be stored in separate files with a +.builder+
716 # extension under the view path. An +XmlMarkup+ object named +xml+ is automatically
717 # made available to template.
718 #
719 # Example:
720 # get '/bio.xml' do
721 # builder :bio
722 # end
723 #
724 # The "views/bio.builder" file might contain the following:
725 # xml.instruct! :xml, :version => '1.1'
726 # xml.person do
727 # xml.name "Francis Albert Sinatra"
728 # xml.aka "Frank Sinatra"
729 # xml.aka "Ol' Blue Eyes"
730 # xml.aka "The Chairman of the Board"
731 # xml.born 'date' => '1915-12-12' do
732 # xml.text! "Hoboken, New Jersey, U.S.A."
733 # end
734 # xml.died 'age' => 82
735 # end
736 #
737 # And yields the following output:
738 # <?xml version='1.1' encoding='UTF-8'?>
739 # <person>
740 # <name>Francis Albert Sinatra</name>
741 # <aka>Frank Sinatra</aka>
742 # <aka>Ol&apos; Blue Eyes</aka>
743 # <aka>The Chairman of the Board</aka>
744 # <born date='1915-12-12'>Hoboken, New Jersey, U.S.A.</born>
745 # <died age='82' />
746 # </person>
747 #
748 # NOTE: Builder must be installed or a LoadError will be raised the first time an
749 # attempt is made to render a builder template.
750 #
751 # See http://builder.rubyforge.org/ for comprehensive documentation on Builder.
752 module Builder
753
754 def builder(content=nil, options={}, &block)
755 options, content = content, nil if content.is_a?(Hash)
756 content = Proc.new { block } if content.nil?
757 render(:builder, content, options)
758 end
759
760 private
761
3d92391a » Blake Mizerany 2008-03-22 pass locals to haml 762 def render_builder(content, options = {}, &b)
caf48570 » rtomayko 2008-03-08 Builder Rendering Helper (.... 763 require 'builder'
764 xml = ::Builder::XmlMarkup.new(:indent => 2)
765 case content
766 when String
767 eval(content, binding, '<BUILDER>', 1)
768 when Proc
769 content.call(xml)
770 end
771 xml.target!
772 end
773
774 end
775
4a246966 » Blake Mizerany 2007-11-29 Custom 404 776 class EventContext
777
778 include ResponseHelpers
fbdf982f » Blake Mizerany 2008-02-24 Streaming 779 include Streaming
2f6269f6 » Blake Mizerany 2007-11-28 named partials 780 include RenderingHelpers
520ac2e9 » Blake Mizerany 2008-02-24 Haml 781 include Erb
782 include Haml
caf48570 » rtomayko 2008-03-08 Builder Rendering Helper (.... 783 include Builder
2baa78d9 » nmeans 2008-04-08 Added Sass module and 'sass... 784 include Sass
2f6269f6 » Blake Mizerany 2007-11-28 named partials 785
53856731 » Blake Mizerany 2007-11-27 in context 786 attr_accessor :request, :response
787
15d360e8 » Markus Prinz 2008-04-07 Fix spelling of dslify_writer 788 dslify_writer :status, :body
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 789
53856731 » Blake Mizerany 2007-11-27 in context 790 def initialize(request, response, route_params)
791 @request = request
792 @response = response
793 @route_params = route_params
794 @response.body = nil
795 end
796
797 def params
98572b4d » Blake Mizerany 2008-04-08 Symbol params back in thank... 798 @params ||= begin
799 h = Hash.new {|h,k| h[k.to_s] if Symbol === k}
800 h.merge(@route_params.merge(@request.params))
801 end
53856731 » Blake Mizerany 2007-11-27 in context 802 end
307f93c6 » Blake Mizerany 2008-04-09 RESTful testing 803
804 def data
805 @data ||= params.keys.first
806 end
98572b4d » Blake Mizerany 2008-04-08 Symbol params back in thank... 807
1f204991 » Blake Mizerany 2008-03-24 getting mare advanced with ... 808 def stop(*args)
809 throw :halt, args
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 810 end
811
de6f9818 » Blake Mizerany 2007-11-28 Using :halt to set body 812 def complete(returned)
9040375d » Blake Mizerany 2007-11-28 render defaults to body or ... 813 @response.body || returned
53856731 » Blake Mizerany 2007-11-27 in context 814 end
815
4ce62316 » Markus Prinz 2008-04-07 Add session support, which ... 816 def session
9c875ffd » Blake Mizerany 2008-04-20 Session testing made easy 817 request.env['rack.session'] ||= {}
4ce62316 » Markus Prinz 2008-04-07 Add session support, which ... 818 end
819
9040375d » Blake Mizerany 2007-11-28 render defaults to body or ... 820 private
2f6269f6 » Blake Mizerany 2007-11-28 named partials 821
de6f9818 » Blake Mizerany 2007-11-28 Using :halt to set body 822 def method_missing(name, *args, &b)
823 @response.send(name, *args, &b)
824 end
825
414c80a3 » Blake Mizerany 2007-11-27 evaluate in context 826 end
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 827
828
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 829 # The Application class represents the top-level working area of a
830 # Sinatra app. It provides the DSL for defining various aspects of the
831 # application and implements a Rack compatible interface for dispatching
832 # requests.
833 #
834 # Many of the instance methods defined in this class (#get, #post,
835 # #put, #delete, #layout, #before, #error, #not_found, etc.) are
836 # available at top-level scope. When invoked from top-level, the
837 # messages are forwarded to the "default application" (accessible
838 # at Sinatra::application).
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 839 class Application
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 840
841 # Hash of event handlers with request method keys and
842 # arrays of potential handlers as values.
843 attr_reader :events
844
845 # Hash of error handlers with error status codes as keys and
846 # handlers as values.
847 attr_reader :errors
848
849 # Hash of template name mappings.
850 attr_reader :templates
851
852 # Hash of filters with event name keys (:before) and arrays of
853 # handlers as values.
854 attr_reader :filters
855
856 # Array of objects to clear during reload. The objects in this array
857 # must respond to :clear.
858 attr_reader :clearables
859
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 860 # Object including open attribute methods for modifying Application
861 # configuration.
862 attr_reader :options
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 863
864 # List of methods available from top-level scope. When invoked from
865 # top-level the method is forwarded to the default application
866 # (Sinatra::application).
867 FORWARD_METHODS = %w[
64a2dee4 » rtomayko 2008-04-16 Add enable/disable methods ... 868 get put post delete head template layout before error not_found
1ba1ceb7 » bmizerany 2008-05-20 added 'set' to FORWARD_METHODS 869 configures configure set set_options set_option enable disable use
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 870 ]
871
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 872 # Create a new Application with a default configuration taken
873 # from the default_options Hash.
874 #
875 # NOTE: A default Application is automatically created the first
876 # time any of Sinatra's DSL related methods is invoked so there
877 # is typically no need to create an instance explicitly. See
878 # Sinatra::application for more information.
879 def initialize
880 @reloading = false
881 @clearables = [
882 @events = Hash.new { |hash, key| hash[key] = [] },
883 @errors = Hash.new,
884 @filters = Hash.new { |hash, key| hash[key] = [] },
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 885 @templates = Hash.new,
886 @middleware = []
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 887 ]
888 @options = OpenStruct.new(self.class.default_options)
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 889 load_default_configuration!
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 890 end
891
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 892 # Hash of default application configuration options. When a new
893 # Application is created, the #options object takes its initial values
894 # from here.
895 #
896 # Changes to the default_options Hash effect only Application objects
897 # created after the changes are made. For this reason, modifications to
898 # the default_options Hash typically occur at the very beginning of a
899 # file, before any DSL related functions are invoked.
06e2a38f » Blake Mizerany 2007-11-29 options 900 def self.default_options
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 901 return @default_options unless @default_options.nil?
5b3440e2 » JackDanger 2008-04-19 Using a more accurate root ... 902 root = File.expand_path(File.dirname($0))
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 903 @default_options = {
06e2a38f » Blake Mizerany 2007-11-29 options 904 :run => true,
905 :port => 4567,
13d13b21 » Blake Mizerany 2007-12-02 static files 906 :env => :development,
5b3440e2 » JackDanger 2008-04-19 Using a more accurate root ... 907 :root => root,
908 :views => root + '/views',
909 :public => root + '/public',
4ce62316 » Markus Prinz 2008-04-07 Add session support, which ... 910 :sessions => false,
174d9c8a » cschneid 2008-05-18 Kernel.load $0 breaks when ... 911 :logging => true,
1753aa97 » cschneid 2008-05-20 Merge from bmizerany 912 :app_file => $0,
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 913 :raise_errors => false
06e2a38f » Blake Mizerany 2007-11-29 options 914 }
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 915 load_default_options_from_command_line!
916 @default_options
06e2a38f » Blake Mizerany 2007-11-29 options 917 end
9b06a2d5 » Blake Mizerany 2008-01-15 simple options 918
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 919 # Search ARGV for command line arguments and update the
920 # Sinatra::default_options Hash accordingly. This method is
921 # invoked the first time the default_options Hash is accessed.
ed063848 » Blake Mizerany 2008-03-28 explain'n 922 # NOTE: Ignores --name so unit/spec tests can run individually
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 923 def self.load_default_options_from_command_line! #:nodoc:
9b06a2d5 » Blake Mizerany 2008-01-15 simple options 924 require 'optparse'
925 OptionParser.new do |op|
926 op.on('-p port') { |port| default_options[:port] = port }
927 op.on('-e env') { |env| default_options[:env] = env }
f747bfe4 » Blake Mizerany 2008-04-14 pick a server, any server! 928 op.on('-x') { default_options[:mutex] = true }
929 op.on('-s server') { |server| default_options[:server] = server }
44d2b66d » Blake Mizerany 2008-02-20 Setup for custom renderers 930 end.parse!(ARGV.dup.select { |o| o !~ /--name/ })
9b06a2d5 » Blake Mizerany 2008-01-15 simple options 931 end
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 932
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 933 # Determine whether the application is in the process of being
934 # reloaded.
935 def reloading?
936 @reloading == true
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 937 end
938
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 939 # Yield to the block for configuration if the current environment
940 # matches any included in the +envs+ list. Always yield to the block
941 # when no environment is specified.
942 #
943 # NOTE: configuration blocks are not executed during reloads.
944 def configures(*envs, &b)
945 return if reloading?
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 946 yield self if envs.empty? || envs.include?(options.env)
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 947 end
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 948
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 949 alias :configure :configures
950
fd0150da » rtomayko 2008-04-15 Move set_option(s) to Appli... 951 # When both +option+ and +value+ arguments are provided, set the option
952 # specified. With a single Hash argument, set all options specified in
953 # Hash. Options are available via the Application#options object.
954 #
955 # Setting individual options:
956 # set :port, 80
957 # set :env, :production
958 # set :views, '/path/to/views'
959 #
960 # Setting multiple options:
961 # set :port => 80,
962 # :env => :production,
963 # :views => '/path/to/views'
964 #
965 def set(option, value=self)
966 if value == self && option.kind_of?(Hash)
967 option.each { |key,val| set(key, val) }
968 else
969 options.send("#{option}=", value)
970 end
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 971 end
fd0150da » rtomayko 2008-04-15 Move set_option(s) to Appli... 972
973 alias :set_option :set
974 alias :set_options :set
975
64a2dee4 » rtomayko 2008-04-16 Add enable/disable methods ... 976 # Enable the options specified by setting their values to true. For
977 # example, to enable sessions and logging:
978 # enable :sessions, :logging
979 def enable(*opts)
980 opts.each { |key| set(key, true) }
60130707 » Blake Mizerany 2007-11-28 layouts 981 end
64a2dee4 » rtomayko 2008-04-16 Add enable/disable methods ... 982
983 # Disable the options specified by setting their values to false. For
984 # example, to disable logging and automatic run:
985 # disable :logging, :run
986 def disable(*opts)
987 opts.each { |key| set(key, false) }
4a246966 » Blake Mizerany 2007-11-29 Custom 404 988 end
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 989
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 990 # Define an event handler for the given request method and path
991 # spec. The block is executed when a request matches the method
992 # and spec.
993 #
994 # NOTE: The #get, #post, #put, and #delete helper methods should
995 # be used to define events when possible.
996 def event(method, path, options = {}, &b)
997 events[method].push(Event.new(path, options, &b)).last
b17f5f70 » Blake Mizerany 2007-11-27 simple event lookup 998 end
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 999
1000 # Define an event handler for GET requests.
1001 def get(path, options={}, &b)
1002 event(:get, path, options, &b)
1003 end
1004
1005 # Define an event handler for POST requests.
1006 def post(path, options={}, &b)
1007 event(:post, path, options, &b)
1008 end
1009
1010 # Define an event handler for HEAD requests.
1011 def head(path, options={}, &b)
1012 event(:head, path, options, &b)
1013 end
1014
1015 # Define an event handler for PUT requests.
1016 #
1017 # NOTE: PUT events are triggered when the HTTP request method is
1018 # PUT and also when the request method is POST and the body includes a
1019 # "_method" parameter set to "PUT".
1020 def put(path, options={}, &b)
1021 event(:put, path, options, &b)
1022 end
1023
1024 # Define an event handler for DELETE requests.
1025 #
1026 # NOTE: DELETE events are triggered when the HTTP request method is
1027 # DELETE and also when the request method is POST and the body includes a
1028 # "_method" parameter set to "DELETE".
1029 def delete(path, options={}, &b)
1030 event(:delete, path, options, &b)
47aa6753 » Blake Mizerany 2008-01-16 Setting the env in test 1031 end
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 1032
1033 # Visits and invokes each handler registered for the +request_method+ in
1034 # definition order until a Result response is produced. If no handler
1035 # responds with a Result, the NotFound error handler is invoked.
1036 #
1037 # When the request_method is "HEAD" and no valid Result is produced by
1038 # the set of handlers registered for HEAD requests, an attempt is made to
1039 # invoke the GET handlers to generate the response before resorting to the
1040 # default error handler.
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 1041 def lookup(request)
1042 method = request.request_method.downcase.to_sym
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 1043 events[method].eject(&[:invoke, request]) ||
1044 (events[:get].eject(&[:invoke, request]) if method == :head) ||
1045 errors[NotFound].invoke(request)
4a246966 » Blake Mizerany 2007-11-29 Custom 404 1046 end
06e2a38f » Blake Mizerany 2007-11-29 options 1047
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1048
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 1049 # Define a named template. The template may be referenced from
1050 # event handlers by passing the name as a Symbol to rendering
1051 # methods. The block is executed each time the template is rendered
1052 # and the resulting object is passed to the template handler.
1053 #
1054 # The following example defines a HAML template named hello and
1055 # invokes it from an event handler:
1056 #
1057 # template :hello do
1058 # "h1 Hello World!"
1059 # end
1060 #
1061 # get '/' do
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1062 # haml :hello
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 1063 # end
1064 #
1065 def template(name, &b)
6bda16b9 » Blake Mizerany 2008-03-26 added `template` to DSL 1066 templates[name] = b
703cf3b2 » Blake Mizerany 2007-11-28 now running 1067 end
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 1068
1069 # Define a layout template.
1070 def layout(name=:layout, &b)
1071 template(name, &b)
4a246966 » Blake Mizerany 2007-11-29 Custom 404 1072 end
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 1073
1074 # Define a custom error handler for the exception class +type+. The block
1075 # is invoked when the specified exception type is raised from an error
1076 # handler and can manipulate the response as needed:
1077 #
1078 # error MyCustomError do
1079 # status 500
1080 # 'So what happened was...' + request.env['sinatra.error'].message
1081 # end
1082 #
1083 # The Sinatra::ServerError handler is used by default when an exception
1084 # occurs and no matching error handler is found.
1085 def error(type=ServerError, options = {}, &b)
1086 errors[type] = Error.new(type, &b)
1087 end
1088
1089 # Define a custom error handler for '404 Not Found' responses. This is a
1090 # shorthand for:
1091 # error NotFound do
1092 # ..
1093 # end
1094 def not_found(options={}, &b)
1095 error NotFound, options, &b
1096 end
1097
418db1ed » rtomayko 2008-05-19 minor rdoc formatting fixes 1098 # Define a request filter. When <tt>type</tt> is <tt>:before</tt>, execute the
1099 # block in the context of each request before matching event handlers.
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 1100 def filter(type, &b)
1101 filters[type] << b
1102 end
1103
1104 # Invoke the block in the context of each request before invoking
1105 # matching event handlers.
1106 def before(&b)
1107 filter :before, &b
47aa6753 » Blake Mizerany 2008-01-16 Setting the env in test 1108 end
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 1109
682c7de1 » Blake Mizerany 2008-02-23 reload! 1110 def development?
1111 options.env == :development
1112 end
1113
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1114 # Clear all events, templates, filters, and error handlers
1115 # and then reload the application source file. This occurs
1116 # automatically before each request is processed in development.
682c7de1 » Blake Mizerany 2008-02-23 reload! 1117 def reload!
1118 clearables.each(&:clear)
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 1119 load_default_configuration!
ce673fa4 » bmizerany 2008-05-18 Fixes problem in developmen... 1120 @pipeline = nil
1121 @reloading = true
174d9c8a » cschneid 2008-05-18 Kernel.load $0 breaks when ... 1122 Kernel.load Sinatra.options.app_file
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1123 @reloading = false
682c7de1 » Blake Mizerany 2008-02-23 reload! 1124 end
38588af1 » rtomayko 2008-04-14 Refactor options / configur... 1125
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1126 # Determine whether the application is in the process of being
1127 # reloaded.
1128 def reloading?
1129 @reloading == true
1130 end
1131
1132 # Mutex instance used for thread synchronization.
09ac1757 » Blake Mizerany 2008-03-15 Mutex 1133 def mutex
1134 @@mutex ||= Mutex.new
1135 end
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1136
1137 # Yield to the block with thread synchronization
09ac1757 » Blake Mizerany 2008-03-15 Mutex 1138 def run_safely
1139 if options.mutex
1140 mutex.synchronize { yield }
1141 else
1142 yield
1143 end
1144 end
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1145
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 1146 # Add a piece of Rack middleware to the pipeline leading to the
1147 # application.
1148 def use(klass, *args, &block)
1149 fail "#{klass} must respond to 'new'" unless klass.respond_to?(:new)
1150 @pipeline = nil
1151 @middleware.push([ klass, args, block ]).last
1152 end
1153
1154 private
1155
1156 # Rack middleware derived from current state of application options.
1157 # These components are plumbed in at the very beginning of the
1158 # pipeline.
1159 def optional_middleware
1160 [
1161 ([ Rack::CommonLogger, [], nil ] if options.logging),
1162 ([ Rack::Session::Cookie, [], nil ] if options.sessions)
1163 ].compact
1164 end
1165
1166 # Rack middleware explicitly added to the application with #use. These
1167 # components are plumbed into the pipeline downstream from
1168 # #optional_middle.
1169 def explicit_middleware
1170 @middleware
1171 end
1172
1173 # All Rack middleware used to construct the pipeline.
1174 def middleware
1175 optional_middleware + explicit_middleware
1176 end
1177
1178 public
1179
1180 # An assembled pipeline of Rack middleware that leads eventually to
1181 # the Application#invoke method. The pipeline is built upon first
1182 # access. Defining new middleware with Application#use or manipulating
1183 # application options may cause the pipeline to be rebuilt.
1184 def pipeline
1185 @pipeline ||=
1186 middleware.inject(method(:dispatch)) do |app,(klass,args,block)|
1187 klass.new(app, *args, &block)
1188 end
1189 end
1190
1191 # Rack compatible request invocation interface.
d4548542 » Blake Mizerany 2007-11-27 serving simple events 1192 def call(env)
682c7de1 » Blake Mizerany 2008-02-23 reload! 1193 reload! if development?
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 1194 pipeline.call(env)
1195 end
1196
1197 # Request invocation handler - called at the end of the Rack pipeline
1198 # for each request.
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1199 #
1200 # 1. Create Rack::Request, Rack::Response helper objects.
1201 # 2. Lookup event handler based on request method and path.
1202 # 3. Create new EventContext to house event handler evaluation.
1203 # 4. Invoke each #before filter in context of EventContext object.
1204 # 5. Invoke event handler in context of EventContext object.
1205 # 6. Return response to Rack.
1206 #
1207 # See the Rack specification for detailed information on the
1208 # +env+ argument and return value.
c1aa6663 » rtomayko 2008-04-15 Rack pipelines leading to a... 1209 def dispatch(env)
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 1210 request = Rack::Request.new(env)
1211 result = lookup(request)
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1212 context = EventContext.new(request, Rack::Response.new, result.params)
aaed7522 » Blake Mizerany 2008-02-23 clean-up 1213 context.status(result.status)
95287a1b » Blake Mizerany 2007-12-02 not needed 1214 begin
09ac1757 » Blake Mizerany 2008-03-15 Mutex 1215 returned = run_safely do
1216 catch(:halt) do
1217 filters[:before].each { |f| context.instance_eval(&f) }
1218 [:complete, context.instance_eval(&result.block)]
1219 end
525d99f1 » Blake Mizerany 2007-11-29 Custom 500's 1220 end
1221 body = returned.to_result(context)
1222 rescue => e
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 1223 request.env['sinatra.error'] = e
ba2d234c » Blake Mizerany 2008-02-23 i can has error map? 1224 context.status(500)
0fa5de74 » Blake Mizerany 2008-03-04 making better use of Rack::... 1225 result = (errors[e.class] || errors[ServerError]).invoke(request)
09ac1757 » Blake Mizerany 2008-03-15 Mutex 1226 returned = run_safely do
1227 catch(:halt) do
1228 [:complete, context.instance_eval(&result.block)]
1229 end
525d99f1 » Blake Mizerany 2007-11-29 Custom 500's 1230 end
1231 body = returned.to_result(context)
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1232 end
aaed7522 » Blake Mizerany 2008-02-23 clean-up 1233 body = '' unless body.respond_to?(:each)
b47ffd39 » rtomayko 2008-03-10 HEAD support and Static as ... 1234 body = '' if request.request_method.upcase == 'HEAD'
aaed7522 » Blake Mizerany 2008-02-23 clean-up 1235 context.body = body.kind_of?(String) ? [*body] : body
1236 context.finish
d4548542 » Blake Mizerany 2007-11-27 serving simple events 1237 end
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1238
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 1239 # Called immediately after the application is initialized or reloaded to
1240 # register default events, templates, and error handlers.
1241 def load_default_configuration!
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1242
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 1243 # The static event is always executed first.
1244 events[:get] << Static.new
24139878 » rtomayko 2008-04-15 Finish Sinatra::Application... 1245
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 1246 # Default configuration for all environments.
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1247 configure do
0eca3f46 » Blake Mizerany 2008-04-01 raise errors from error 1248 error do
1249 raise request.env['sinatra.error'] if Sinatra.options.raise_errors
1250 '<h1>Internal Server Error</h1>'
1251 end
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1252 not_found { '<h1>Not Found</h1>'}
1253 end
1254
1255 configures :development do
1256
1257 get '/sinatra_custom_images/:image.png' do
1258 File.read(File.dirname(__FILE__) + "/../images/#{params[:image]}.png")
1259 end
1260
1261 not_found do
1262 %Q(
713b66a3 » Blake Mizerany 2008-03-15 friendly helper 1263 <style>
1264 body {
1265 text-align: center;
1266 color: #888;
1267 font-family: Arial;
1268 font-size: 22px;
1269 margin: 20px;
1270 }
1271 #content {
1272 margin: 0 auto;
1273 width: 500px;
1274 text-align: left;
1275 }
1276 </style>
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1277 <html>
713b66a3 » Blake Mizerany 2008-03-15 friendly helper 1278 <body>
1279 <h2>Sinatra doesn't know this diddy.</h2>
1280 <img src='/sinatra_custom_images/404.png'></img>
1281 <div id="content">
1282 Try this:
1283 <pre>#{request.request_method.downcase} "#{request.path_info}" do
1284 .. do something ..
1285 end<pre>
1286 </div>
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1287 </body>
1288 </html>
1289 )
1290 end
1291
1292 error do
1293 @error = request.env['sinatra.error']
1294 %Q(
1295 <html>
1296 <body>
1297 <style type="text/css" media="screen">
1298 body {
1299 font-family: Verdana;
1300 color: #333;
1301 }
1302
1303 #content {
1304 width: 700px;
1305 margin-left: 20px;
1306 }
1307
1308 #content h1 {
1309 width: 99%;
1310 color: #1D6B8D;
1311 font-weight: bold;
1312 }
1313
1314 #stacktrace {
1315 margin-top: -20px;
1316 }
1317
1318 #stacktrace pre {
1319 font-size: 12px;
1320 border-left: 2px solid #ddd;
1321 padding-left: 10px;
1322 }
1323
1324 #stacktrace img {
1325 margin-top: 10px;
1326 }
1327 </style>
1328 <div id="content">
1329 <img src="/sinatra_custom_images/500.png" />
5f3eac03 » Blake Mizerany 2008-03-15 Better errors 1330 <div class="info">
1331 Params: <pre>#{params.inspect}
1332 </div>
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1333 <div id="stacktrace">
5f3eac03 » Blake Mizerany 2008-03-15 Better errors 1334 <h1>#{Rack::Utils.escape_html(@error.class.name + ' - ' + @error.message)}</h1>
327c6ece » rtomayko 2008-03-08 Fix that built-in error mes... 1335 <pre><code>#{Rack::Utils.escape_html(@error.backtrace.join("\n"))}</code></pre>
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1336 </div>
1337 </body>
1338 </html>
1339 )
1340 end
1341 end
1342 end
18d3c871 » Blake Mizerany 2007-11-27 more tests 1343
a54d7cc3 » rtomayko 2008-04-15 Move Environment.setup! int... 1344 private :load_default_configuration!
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1345
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1346 end
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1347
1348 end
1349
da3439af » rtomayko 2008-04-14 Sinatra::Application as a s... 1350 # Delegate DSLish methods to the currently active Sinatra::Application
1351 # instance.
1352 Sinatra::Application::FORWARD_METHODS.each do |method|
1353 eval(<<-EOS, binding, '(__DSL__)', 1)
1354 def #{method}(*args, &b)
1355 Sinatra.application.#{method}(*args, &b)
1356 end
1357 EOS
47aa6753 » Blake Mizerany 2008-01-16 Setting the env in test 1358 end
1359
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1360 def helpers(&b)
1361 Sinatra::EventContext.class_eval(&b)
1362 end
1363
d874133b » Blake Mizerany 2008-03-26 inline templates! 1364 def use_in_file_templates!
1365 require 'stringio'
1366 templates = IO.read(caller.first.split(':').first).split('__FILE__').last
1367 data = StringIO.new(templates)
1368 current_template = nil
1369 data.each do |line|
1987e550 » Blake Mizerany 2008-04-26 New standard: use_in_file_... 1370 if line =~ /^@@\s?(.*)/
d874133b » Blake Mizerany 2008-03-26 inline templates! 1371 current_template = $1.to_sym
1372 Sinatra.application.templates[current_template] = ''
1373 elsif current_template
1374 Sinatra.application.templates[current_template] << line
1375 end
1376 end
1377 end
1378
fbdf982f » Blake Mizerany 2008-02-24 Streaming 1379 def mime(ext, type)
1380 Rack::File::MIME_TYPES[ext.to_s] = type
1381 end
1382
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1383 ### Misc Core Extensions
1384
18d3c871 » Blake Mizerany 2007-11-27 more tests 1385 module Kernel
1386
1387 def silence_warnings
1388 old_verbose, $VERBOSE = $VERBOSE, nil
1389 yield
1390 ensure
1391 $VERBOSE = old_verbose
1392 end
1393
1394 end
1395
1396 class String
1397
1398 # Converts +self+ to an escaped URI parameter value
1399 # 'Foo Bar'.to_param # => 'Foo%20Bar'
1400 def to_param
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 1401 Rack::Utils.escape(self)
18d3c871 » Blake Mizerany 2007-11-27 more tests 1402 end
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 1403 alias :http_escape :to_param
18d3c871 » Blake Mizerany 2007-11-27 more tests 1404
1405 # Converts +self+ from an escaped URI parameter value
1406 # 'Foo%20Bar'.from_param # => 'Foo Bar'
1407 def from_param
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 1408 Rack::Utils.unescape(self)
18d3c871 » Blake Mizerany 2007-11-27 more tests 1409 end
d16ee653 » Blake Mizerany 2008-04-26 fixed escaped paths not res... 1410 alias :http_unescape :from_param
18d3c871 » Blake Mizerany 2007-11-27 more tests 1411
1412 end
1413
1414 class Hash
1415
1416 def to_params
1417 map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
1418 end
1419
1420 def symbolize_keys
1421 self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
1422 end
1423
1424 def pass(*keys)
1425 reject { |k,v| !keys.include?(k) }
1426 end
1427
1428 end
1429
1430 class Symbol
1431
1432 def to_proc
1433 Proc.new { |*args| args.shift.__send__(self, *args) }
1434 end
1435
1436 end
1437
1438 class Array
1439
1440 def to_hash
1441 self.inject({}) { |h, (k, v)| h[k] = v; h }
1442 end
1443
1444 def to_proc
1445 Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
1446 end
1447
1448 end
1449
1450 module Enumerable
1451
1452 def eject(&block)
1453 find { |e| result = block[e] and break result }
1454 end
1455
1456 end
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1457
1458 ### Core Extension results for throw :halt
1459
1460 class Proc
1461 def to_result(cx, *args)
1462 cx.instance_eval(&self)
1f204991 » Blake Mizerany 2008-03-24 getting mare advanced with ... 1463 args.shift.to_result(cx, *args)
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1464 end
1465 end
1466
1467 class String
1468 def to_result(cx, *args)
1f204991 » Blake Mizerany 2008-03-24 getting mare advanced with ... 1469 args.shift.to_result(cx, *args)
1470 self
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1471 end
1472 end
1473
1474 class Array
1475 def to_result(cx, *args)
1476 self.shift.to_result(cx, *self)
1477 end
1478 end
1479
1480 class Symbol
1481 def to_result(cx, *args)
1482 cx.send(self, *args)
1483 end
1484 end
1485
1486 class Fixnum
1487 def to_result(cx, *args)
1488 cx.status self
1f204991 » Blake Mizerany 2008-03-24 getting mare advanced with ... 1489 args.shift.to_result(cx, *args)
d9cad760 » Blake Mizerany 2007-11-28 throw :halt 1490 end
1491 end
b72f00cc » Blake Mizerany 2007-11-28 this should be stop and bod... 1492
1493 class NilClass
1494 def to_result(cx, *args)
1f204991 » Blake Mizerany 2008-03-24 getting mare advanced with ... 1495 ''
b72f00cc » Blake Mizerany 2007-11-28 this should be stop and bod... 1496 end
1497 end
1498
703cf3b2 » Blake Mizerany 2007-11-28 now running 1499 at_exit do
bd8515bb » Blake Mizerany 2007-11-29 Notify user of error if DSL... 1500 raise $! if $!
5a61c3be » Blake Mizerany 2008-02-24 FIX: reloading configs 1501 if Sinatra.application.options.run
1502 Sinatra.run
1503 end
703cf3b2 » Blake Mizerany 2007-11-28 now running 1504 end
9ee50b30 » Blake Mizerany 2007-11-29 * Default error messages 1505
fbdf982f » Blake Mizerany 2008-02-24 Streaming 1506 mime :xml, 'application/xml'
1507 mime :js, 'application/javascript'