rsense-core 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2023 @@
1
+ # external dependencies
2
+ require 'rack'
3
+ require 'tilt'
4
+ require 'rack/protection'
5
+
6
+ # stdlib dependencies
7
+ require 'thread'
8
+ require 'time'
9
+ require 'uri'
10
+
11
+ # other files we need
12
+ require 'sinatra/show_exceptions'
13
+ require 'sinatra/version'
14
+
15
+ module Sinatra
16
+ # The request object. See Rack::Request for more info:
17
+ # http://rack.rubyforge.org/doc/classes/Rack/Request.html
18
+ class Request < Rack::Request
19
+ HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/
20
+ HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/
21
+
22
+ # Returns an array of acceptable media types for the response
23
+ def accept
24
+ @env['sinatra.accept'] ||= begin
25
+ if @env.include? 'HTTP_ACCEPT' and @env['HTTP_ACCEPT'].to_s != ''
26
+ @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS).
27
+ map! { |e| AcceptEntry.new(e) }.sort
28
+ else
29
+ [AcceptEntry.new('*/*')]
30
+ end
31
+ end
32
+ end
33
+
34
+ def accept?(type)
35
+ preferred_type(type).to_s.include?(type)
36
+ end
37
+
38
+ def preferred_type(*types)
39
+ accepts = accept # just evaluate once
40
+ return accepts.first if types.empty?
41
+ types.flatten!
42
+ return types.first if accepts.empty?
43
+ accepts.detect do |pattern|
44
+ type = types.detect { |t| File.fnmatch(pattern, t) }
45
+ return type if type
46
+ end
47
+ end
48
+
49
+ alias secure? ssl?
50
+
51
+ def forwarded?
52
+ @env.include? "HTTP_X_FORWARDED_HOST"
53
+ end
54
+
55
+ def safe?
56
+ get? or head? or options? or trace?
57
+ end
58
+
59
+ def idempotent?
60
+ safe? or put? or delete? or link? or unlink?
61
+ end
62
+
63
+ def link?
64
+ request_method == "LINK"
65
+ end
66
+
67
+ def unlink?
68
+ request_method == "UNLINK"
69
+ end
70
+
71
+ private
72
+
73
+ class AcceptEntry
74
+ attr_accessor :params
75
+ attr_reader :entry
76
+
77
+ def initialize(entry)
78
+ params = entry.scan(HEADER_PARAM).map! do |s|
79
+ key, value = s.strip.split('=', 2)
80
+ value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"')
81
+ [key, value]
82
+ end
83
+
84
+ @entry = entry
85
+ @type = entry[/[^;]+/].delete(' ')
86
+ @params = Hash[params]
87
+ @q = @params.delete('q') { 1.0 }.to_f
88
+ end
89
+
90
+ def <=>(other)
91
+ other.priority <=> self.priority
92
+ end
93
+
94
+ def priority
95
+ # We sort in descending order; better matches should be higher.
96
+ [ @q, -@type.count('*'), @params.size ]
97
+ end
98
+
99
+ def to_str
100
+ @type
101
+ end
102
+
103
+ def to_s(full = false)
104
+ full ? entry : to_str
105
+ end
106
+
107
+ def respond_to?(*args)
108
+ super or to_str.respond_to?(*args)
109
+ end
110
+
111
+ def method_missing(*args, &block)
112
+ to_str.send(*args, &block)
113
+ end
114
+ end
115
+ end
116
+
117
+ # The response object. See Rack::Response and Rack::Response::Helpers for
118
+ # more info:
119
+ # http://rack.rubyforge.org/doc/classes/Rack/Response.html
120
+ # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
121
+ class Response < Rack::Response
122
+ DROP_BODY_RESPONSES = [204, 205, 304]
123
+ def initialize(*)
124
+ super
125
+ headers['Content-Type'] ||= 'text/html'
126
+ end
127
+
128
+ def body=(value)
129
+ value = value.body while Rack::Response === value
130
+ @body = String === value ? [value.to_str] : value
131
+ end
132
+
133
+ def each
134
+ block_given? ? super : enum_for(:each)
135
+ end
136
+
137
+ def finish
138
+ result = body
139
+
140
+ if drop_content_info?
141
+ headers.delete "Content-Length"
142
+ headers.delete "Content-Type"
143
+ end
144
+
145
+ if drop_body?
146
+ close
147
+ result = []
148
+ end
149
+
150
+ if calculate_content_length?
151
+ # if some other code has already set Content-Length, don't muck with it
152
+ # currently, this would be the static file-handler
153
+ headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
154
+ end
155
+
156
+ [status.to_i, headers, result]
157
+ end
158
+
159
+ private
160
+
161
+ def calculate_content_length?
162
+ headers["Content-Type"] and not headers["Content-Length"] and Array === body
163
+ end
164
+
165
+ def drop_content_info?
166
+ status.to_i / 100 == 1 or drop_body?
167
+ end
168
+
169
+ def drop_body?
170
+ DROP_BODY_RESPONSES.include?(status.to_i)
171
+ end
172
+ end
173
+
174
+ # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
175
+ # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
176
+ # This middleware will detect an extended body object and will make sure it reaches the
177
+ # handler directly. We do this here, so our middleware and middleware set up by the app will
178
+ # still be able to run.
179
+ class ExtendedRack < Struct.new(:app)
180
+ def call(env)
181
+ result, callback = app.call(env), env['async.callback']
182
+ return result unless callback and async?(*result)
183
+ after_response { callback.call result }
184
+ setup_close(env, *result)
185
+ throw :async
186
+ end
187
+
188
+ private
189
+
190
+ def setup_close(env, status, headers, body)
191
+ return unless body.respond_to? :close and env.include? 'async.close'
192
+ env['async.close'].callback { body.close }
193
+ env['async.close'].errback { body.close }
194
+ end
195
+
196
+ def after_response(&block)
197
+ raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
198
+ EventMachine.next_tick(&block)
199
+ end
200
+
201
+ def async?(status, headers, body)
202
+ return true if status == -1
203
+ body.respond_to? :callback and body.respond_to? :errback
204
+ end
205
+ end
206
+
207
+ # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
208
+ # if another CommonLogger is already in the middleware chain.
209
+ class CommonLogger < Rack::CommonLogger
210
+ def call(env)
211
+ env['sinatra.commonlogger'] ? @app.call(env) : super
212
+ end
213
+
214
+ superclass.class_eval do
215
+ alias call_without_check call unless method_defined? :call_without_check
216
+ def call(env)
217
+ env['sinatra.commonlogger'] = true
218
+ call_without_check(env)
219
+ end
220
+ end
221
+ end
222
+
223
+ class NotFound < NameError #:nodoc:
224
+ def http_status; 404 end
225
+ end
226
+
227
+ # Methods available to routes, before/after filters, and views.
228
+ module Helpers
229
+ # Set or retrieve the response status code.
230
+ def status(value = nil)
231
+ response.status = value if value
232
+ response.status
233
+ end
234
+
235
+ # Set or retrieve the response body. When a block is given,
236
+ # evaluation is deferred until the body is read with #each.
237
+ def body(value = nil, &block)
238
+ if value
239
+ headers.delete 'Content-Length' unless request.head? || value.is_a?(Rack::File) || value.is_a?(Stream)
240
+ response.body = value
241
+ else
242
+ response.body
243
+ end
244
+ end
245
+
246
+ # Halt processing and redirect to the URI provided.
247
+ def redirect(uri, *args)
248
+ if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
249
+ status 303
250
+ else
251
+ status 302
252
+ end
253
+
254
+ # According to RFC 2616 section 14.30, "the field value consists of a
255
+ # single absolute URI"
256
+ response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?)
257
+ halt(*args)
258
+ end
259
+
260
+ # Generates the absolute URI for a given path in the app.
261
+ # Takes Rack routers and reverse proxies into account.
262
+ def uri(addr = nil, absolute = true, add_script_name = true)
263
+ return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
264
+ uri = [host = ""]
265
+ if absolute
266
+ host << "http#{'s' if request.secure?}://"
267
+ if request.forwarded? or request.port != (request.secure? ? 443 : 80)
268
+ host << request.host_with_port
269
+ else
270
+ host << request.host
271
+ end
272
+ end
273
+ uri << request.script_name.to_s if add_script_name
274
+ uri << (addr ? addr : request.path_info).to_s
275
+ File.join uri
276
+ end
277
+
278
+ alias url uri
279
+ alias to uri
280
+
281
+ # Halt processing and return the error status provided.
282
+ def error(code, body = nil)
283
+ code, body = 500, code.to_str if code.respond_to? :to_str
284
+ response.body = body unless body.nil?
285
+ halt code
286
+ end
287
+
288
+ # Halt processing and return a 404 Not Found.
289
+ def not_found(body = nil)
290
+ error 404, body
291
+ end
292
+
293
+ # Set multiple response headers with Hash.
294
+ def headers(hash = nil)
295
+ response.headers.merge! hash if hash
296
+ response.headers
297
+ end
298
+
299
+ # Access the underlying Rack session.
300
+ def session
301
+ request.session
302
+ end
303
+
304
+ # Access shared logger object.
305
+ def logger
306
+ request.logger
307
+ end
308
+
309
+ # Look up a media type by file extension in Rack's mime registry.
310
+ def mime_type(type)
311
+ Base.mime_type(type)
312
+ end
313
+
314
+ # Set the Content-Type of the response body given a media type or file
315
+ # extension.
316
+ def content_type(type = nil, params = {})
317
+ return response['Content-Type'] unless type
318
+ default = params.delete :default
319
+ mime_type = mime_type(type) || default
320
+ fail "Unknown media type: %p" % type if mime_type.nil?
321
+ mime_type = mime_type.dup
322
+ unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
323
+ params[:charset] = params.delete('charset') || settings.default_encoding
324
+ end
325
+ params.delete :charset if mime_type.include? 'charset'
326
+ unless params.empty?
327
+ mime_type << (mime_type.include?(';') ? ', ' : ';')
328
+ mime_type << params.map do |key, val|
329
+ val = val.inspect if val =~ /[";,]/
330
+ "#{key}=#{val}"
331
+ end.join(', ')
332
+ end
333
+ response['Content-Type'] = mime_type
334
+ end
335
+
336
+ # Set the Content-Disposition to "attachment" with the specified filename,
337
+ # instructing the user agents to prompt to save.
338
+ def attachment(filename = nil, disposition = 'attachment')
339
+ response['Content-Disposition'] = disposition.to_s
340
+ if filename
341
+ params = '; filename="%s"' % File.basename(filename)
342
+ response['Content-Disposition'] << params
343
+ ext = File.extname(filename)
344
+ content_type(ext) unless response['Content-Type'] or ext.empty?
345
+ end
346
+ end
347
+
348
+ # Use the contents of the file at +path+ as the response body.
349
+ def send_file(path, opts = {})
350
+ if opts[:type] or not response['Content-Type']
351
+ content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
352
+ end
353
+
354
+ disposition = opts[:disposition]
355
+ filename = opts[:filename]
356
+ disposition = 'attachment' if disposition.nil? and filename
357
+ filename = path if filename.nil?
358
+ attachment(filename, disposition) if disposition
359
+
360
+ last_modified opts[:last_modified] if opts[:last_modified]
361
+
362
+ file = Rack::File.new nil
363
+ file.path = path
364
+ result = file.serving env
365
+ result[1].each { |k,v| headers[k] ||= v }
366
+ headers['Content-Length'] = result[1]['Content-Length']
367
+ opts[:status] &&= Integer(opts[:status])
368
+ halt opts[:status] || result[0], result[2]
369
+ rescue Errno::ENOENT
370
+ not_found
371
+ end
372
+
373
+ # Class of the response body in case you use #stream.
374
+ #
375
+ # Three things really matter: The front and back block (back being the
376
+ # block generating content, front the one sending it to the client) and
377
+ # the scheduler, integrating with whatever concurrency feature the Rack
378
+ # handler is using.
379
+ #
380
+ # Scheduler has to respond to defer and schedule.
381
+ class Stream
382
+ def self.schedule(*) yield end
383
+ def self.defer(*) yield end
384
+
385
+ def initialize(scheduler = self.class, keep_open = false, &back)
386
+ @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
387
+ @callbacks, @closed = [], false
388
+ end
389
+
390
+ def close
391
+ return if closed?
392
+ @closed = true
393
+ @scheduler.schedule { @callbacks.each { |c| c.call }}
394
+ end
395
+
396
+ def each(&front)
397
+ @front = front
398
+ @scheduler.defer do
399
+ begin
400
+ @back.call(self)
401
+ rescue Exception => e
402
+ @scheduler.schedule { raise e }
403
+ end
404
+ close unless @keep_open
405
+ end
406
+ end
407
+
408
+ def <<(data)
409
+ @scheduler.schedule { @front.call(data.to_s) }
410
+ self
411
+ end
412
+
413
+ def callback(&block)
414
+ return yield if closed?
415
+ @callbacks << block
416
+ end
417
+
418
+ alias errback callback
419
+
420
+ def closed?
421
+ @closed
422
+ end
423
+ end
424
+
425
+ # Allows to start sending data to the client even though later parts of
426
+ # the response body have not yet been generated.
427
+ #
428
+ # The close parameter specifies whether Stream#close should be called
429
+ # after the block has been executed. This is only relevant for evented
430
+ # servers like Thin or Rainbows.
431
+ def stream(keep_open = false)
432
+ scheduler = env['async.callback'] ? EventMachine : Stream
433
+ current = @params.dup
434
+ body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
435
+ end
436
+
437
+ # Specify response freshness policy for HTTP caches (Cache-Control header).
438
+ # Any number of non-value directives (:public, :private, :no_cache,
439
+ # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
440
+ # a Hash of value directives (:max_age, :min_stale, :s_max_age).
441
+ #
442
+ # cache_control :public, :must_revalidate, :max_age => 60
443
+ # => Cache-Control: public, must-revalidate, max-age=60
444
+ #
445
+ # See RFC 2616 / 14.9 for more on standard cache control directives:
446
+ # http://tools.ietf.org/html/rfc2616#section-14.9.1
447
+ def cache_control(*values)
448
+ if values.last.kind_of?(Hash)
449
+ hash = values.pop
450
+ hash.reject! { |k,v| v == false }
451
+ hash.reject! { |k,v| values << k if v == true }
452
+ else
453
+ hash = {}
454
+ end
455
+
456
+ values.map! { |value| value.to_s.tr('_','-') }
457
+ hash.each do |key, value|
458
+ key = key.to_s.tr('_', '-')
459
+ value = value.to_i if key == "max-age"
460
+ values << "#{key}=#{value}"
461
+ end
462
+
463
+ response['Cache-Control'] = values.join(', ') if values.any?
464
+ end
465
+
466
+ # Set the Expires header and Cache-Control/max-age directive. Amount
467
+ # can be an integer number of seconds in the future or a Time object
468
+ # indicating when the response should be considered "stale". The remaining
469
+ # "values" arguments are passed to the #cache_control helper:
470
+ #
471
+ # expires 500, :public, :must_revalidate
472
+ # => Cache-Control: public, must-revalidate, max-age=60
473
+ # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
474
+ #
475
+ def expires(amount, *values)
476
+ values << {} unless values.last.kind_of?(Hash)
477
+
478
+ if amount.is_a? Integer
479
+ time = Time.now + amount.to_i
480
+ max_age = amount
481
+ else
482
+ time = time_for amount
483
+ max_age = time - Time.now
484
+ end
485
+
486
+ values.last.merge!(:max_age => max_age)
487
+ cache_control(*values)
488
+
489
+ response['Expires'] = time.httpdate
490
+ end
491
+
492
+ # Set the last modified time of the resource (HTTP 'Last-Modified' header)
493
+ # and halt if conditional GET matches. The +time+ argument is a Time,
494
+ # DateTime, or other object that responds to +to_time+.
495
+ #
496
+ # When the current request includes an 'If-Modified-Since' header that is
497
+ # equal or later than the time specified, execution is immediately halted
498
+ # with a '304 Not Modified' response.
499
+ def last_modified(time)
500
+ return unless time
501
+ time = time_for time
502
+ response['Last-Modified'] = time.httpdate
503
+ return if env['HTTP_IF_NONE_MATCH']
504
+
505
+ if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
506
+ # compare based on seconds since epoch
507
+ since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
508
+ halt 304 if since >= time.to_i
509
+ end
510
+
511
+ if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
512
+ # compare based on seconds since epoch
513
+ since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
514
+ halt 412 if since < time.to_i
515
+ end
516
+ rescue ArgumentError
517
+ end
518
+
519
+ ETAG_KINDS = [:strong, :weak]
520
+ # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
521
+ # GET matches. The +value+ argument is an identifier that uniquely
522
+ # identifies the current version of the resource. The +kind+ argument
523
+ # indicates whether the etag should be used as a :strong (default) or :weak
524
+ # cache validator.
525
+ #
526
+ # When the current request includes an 'If-None-Match' header with a
527
+ # matching etag, execution is immediately halted. If the request method is
528
+ # GET or HEAD, a '304 Not Modified' response is sent.
529
+ def etag(value, options = {})
530
+ # Before touching this code, please double check RFC 2616 14.24 and 14.26.
531
+ options = {:kind => options} unless Hash === options
532
+ kind = options[:kind] || :strong
533
+ new_resource = options.fetch(:new_resource) { request.post? }
534
+
535
+ unless ETAG_KINDS.include?(kind)
536
+ raise ArgumentError, ":strong or :weak expected"
537
+ end
538
+
539
+ value = '"%s"' % value
540
+ value = "W/#{value}" if kind == :weak
541
+ response['ETag'] = value
542
+
543
+ if success? or status == 304
544
+ if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
545
+ halt(request.safe? ? 304 : 412)
546
+ end
547
+
548
+ if env['HTTP_IF_MATCH']
549
+ halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
550
+ end
551
+ end
552
+ end
553
+
554
+ # Sugar for redirect (example: redirect back)
555
+ def back
556
+ request.referer
557
+ end
558
+
559
+ # whether or not the status is set to 1xx
560
+ def informational?
561
+ status.between? 100, 199
562
+ end
563
+
564
+ # whether or not the status is set to 2xx
565
+ def success?
566
+ status.between? 200, 299
567
+ end
568
+
569
+ # whether or not the status is set to 3xx
570
+ def redirect?
571
+ status.between? 300, 399
572
+ end
573
+
574
+ # whether or not the status is set to 4xx
575
+ def client_error?
576
+ status.between? 400, 499
577
+ end
578
+
579
+ # whether or not the status is set to 5xx
580
+ def server_error?
581
+ status.between? 500, 599
582
+ end
583
+
584
+ # whether or not the status is set to 404
585
+ def not_found?
586
+ status == 404
587
+ end
588
+
589
+ # Generates a Time object from the given value.
590
+ # Used by #expires and #last_modified.
591
+ def time_for(value)
592
+ if value.respond_to? :to_time
593
+ value.to_time
594
+ elsif value.is_a? Time
595
+ value
596
+ elsif value.respond_to? :new_offset
597
+ # DateTime#to_time does the same on 1.9
598
+ d = value.new_offset 0
599
+ t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
600
+ t.getlocal
601
+ elsif value.respond_to? :mday
602
+ # Date#to_time does the same on 1.9
603
+ Time.local(value.year, value.mon, value.mday)
604
+ elsif value.is_a? Numeric
605
+ Time.at value
606
+ else
607
+ Time.parse value.to_s
608
+ end
609
+ rescue ArgumentError => boom
610
+ raise boom
611
+ rescue Exception
612
+ raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
613
+ end
614
+
615
+ private
616
+
617
+ # Helper method checking if a ETag value list includes the current ETag.
618
+ def etag_matches?(list, new_resource = request.post?)
619
+ return !new_resource if list == '*'
620
+ list.to_s.split(/\s*,\s*/).include? response['ETag']
621
+ end
622
+
623
+ def with_params(temp_params)
624
+ original, @params = @params, temp_params
625
+ yield
626
+ ensure
627
+ @params = original if original
628
+ end
629
+ end
630
+
631
+ private
632
+
633
+ # Template rendering methods. Each method takes the name of a template
634
+ # to render as a Symbol and returns a String with the rendered output,
635
+ # as well as an optional hash with additional options.
636
+ #
637
+ # `template` is either the name or path of the template as symbol
638
+ # (Use `:'subdir/myview'` for views in subdirectories), or a string
639
+ # that will be rendered.
640
+ #
641
+ # Possible options are:
642
+ # :content_type The content type to use, same arguments as content_type.
643
+ # :layout If set to something falsy, no layout is rendered, otherwise
644
+ # the specified layout is used (Ignored for `sass` and `less`)
645
+ # :layout_engine Engine to use for rendering the layout.
646
+ # :locals A hash with local variables that should be available
647
+ # in the template
648
+ # :scope If set, template is evaluate with the binding of the given
649
+ # object rather than the application instance.
650
+ # :views Views directory to use.
651
+ module Templates
652
+ module ContentTyped
653
+ attr_accessor :content_type
654
+ end
655
+
656
+ def initialize
657
+ super
658
+ @default_layout = :layout
659
+ @preferred_extension = nil
660
+ end
661
+
662
+ def erb(template, options = {}, locals = {}, &block)
663
+ render(:erb, template, options, locals, &block)
664
+ end
665
+
666
+ def erubis(template, options = {}, locals = {})
667
+ warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
668
+ "If you have Erubis installed, it will be used automatically."
669
+ render :erubis, template, options, locals
670
+ end
671
+
672
+ def haml(template, options = {}, locals = {}, &block)
673
+ render(:haml, template, options, locals, &block)
674
+ end
675
+
676
+ def sass(template, options = {}, locals = {})
677
+ options.merge! :layout => false, :default_content_type => :css
678
+ render :sass, template, options, locals
679
+ end
680
+
681
+ def scss(template, options = {}, locals = {})
682
+ options.merge! :layout => false, :default_content_type => :css
683
+ render :scss, template, options, locals
684
+ end
685
+
686
+ def less(template, options = {}, locals = {})
687
+ options.merge! :layout => false, :default_content_type => :css
688
+ render :less, template, options, locals
689
+ end
690
+
691
+ def stylus(template, options={}, locals={})
692
+ options.merge! :layout => false, :default_content_type => :css
693
+ render :styl, template, options, locals
694
+ end
695
+
696
+ def builder(template = nil, options = {}, locals = {}, &block)
697
+ options[:default_content_type] = :xml
698
+ render_ruby(:builder, template, options, locals, &block)
699
+ end
700
+
701
+ def liquid(template, options = {}, locals = {}, &block)
702
+ render(:liquid, template, options, locals, &block)
703
+ end
704
+
705
+ def markdown(template, options = {}, locals = {})
706
+ render :markdown, template, options, locals
707
+ end
708
+
709
+ def textile(template, options = {}, locals = {})
710
+ render :textile, template, options, locals
711
+ end
712
+
713
+ def rdoc(template, options = {}, locals = {})
714
+ render :rdoc, template, options, locals
715
+ end
716
+
717
+ def asciidoc(template, options = {}, locals = {})
718
+ render :asciidoc, template, options, locals
719
+ end
720
+
721
+ def radius(template, options = {}, locals = {})
722
+ render :radius, template, options, locals
723
+ end
724
+
725
+ def markaby(template = nil, options = {}, locals = {}, &block)
726
+ render_ruby(:mab, template, options, locals, &block)
727
+ end
728
+
729
+ def coffee(template, options = {}, locals = {})
730
+ options.merge! :layout => false, :default_content_type => :js
731
+ render :coffee, template, options, locals
732
+ end
733
+
734
+ def nokogiri(template = nil, options = {}, locals = {}, &block)
735
+ options[:default_content_type] = :xml
736
+ render_ruby(:nokogiri, template, options, locals, &block)
737
+ end
738
+
739
+ def slim(template, options = {}, locals = {}, &block)
740
+ render(:slim, template, options, locals, &block)
741
+ end
742
+
743
+ def creole(template, options = {}, locals = {})
744
+ render :creole, template, options, locals
745
+ end
746
+
747
+ def mediawiki(template, options = {}, locals = {})
748
+ render :mediawiki, template, options, locals
749
+ end
750
+
751
+ def wlang(template, options = {}, locals = {}, &block)
752
+ render(:wlang, template, options, locals, &block)
753
+ end
754
+
755
+ def yajl(template, options = {}, locals = {})
756
+ options[:default_content_type] = :json
757
+ render :yajl, template, options, locals
758
+ end
759
+
760
+ def rabl(template, options = {}, locals = {})
761
+ Rabl.register!
762
+ render :rabl, template, options, locals
763
+ end
764
+
765
+ # Calls the given block for every possible template file in views,
766
+ # named name.ext, where ext is registered on engine.
767
+ def find_template(views, name, engine)
768
+ yield ::File.join(views, "#{name}.#{@preferred_extension}")
769
+ Tilt.mappings.each do |ext, engines|
770
+ next unless ext != @preferred_extension and engines.include? engine
771
+ yield ::File.join(views, "#{name}.#{ext}")
772
+ end
773
+ end
774
+
775
+ private
776
+
777
+ # logic shared between builder and nokogiri
778
+ def render_ruby(engine, template, options = {}, locals = {}, &block)
779
+ options, template = template, nil if template.is_a?(Hash)
780
+ template = Proc.new { block } if template.nil?
781
+ render engine, template, options, locals
782
+ end
783
+
784
+ def render(engine, data, options = {}, locals = {}, &block)
785
+ # merge app-level options
786
+ engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
787
+ options.merge!(engine_options) { |key, v1, v2| v1 }
788
+
789
+ # extract generic options
790
+ locals = options.delete(:locals) || locals || {}
791
+ views = options.delete(:views) || settings.views || "./views"
792
+ layout = options[:layout]
793
+ layout = false if layout.nil? && options.include?(:layout)
794
+ eat_errors = layout.nil?
795
+ layout = engine_options[:layout] if layout.nil? or (layout == true && engine_options[:layout] != false)
796
+ layout = @default_layout if layout.nil? or layout == true
797
+ layout_options = options.delete(:layout_options) || {}
798
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
799
+ layout_engine = options.delete(:layout_engine) || engine
800
+ scope = options.delete(:scope) || self
801
+ options.delete(:layout)
802
+
803
+ # set some defaults
804
+ options[:outvar] ||= '@_out_buf'
805
+ options[:default_encoding] ||= settings.default_encoding
806
+
807
+ # compile and render template
808
+ begin
809
+ layout_was = @default_layout
810
+ @default_layout = false
811
+ template = compile_template(engine, data, options, views)
812
+ output = template.render(scope, locals, &block)
813
+ ensure
814
+ @default_layout = layout_was
815
+ end
816
+
817
+ # render layout
818
+ if layout
819
+ options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope).
820
+ merge!(layout_options)
821
+ catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
822
+ end
823
+
824
+ output.extend(ContentTyped).content_type = content_type if content_type
825
+ output
826
+ end
827
+
828
+ def compile_template(engine, data, options, views)
829
+ eat_errors = options.delete :eat_errors
830
+ template_cache.fetch engine, data, options, views do
831
+ template = Tilt[engine]
832
+ raise "Template engine not found: #{engine}" if template.nil?
833
+
834
+ case data
835
+ when Symbol
836
+ body, path, line = settings.templates[data]
837
+ if body
838
+ body = body.call if body.respond_to?(:call)
839
+ template.new(path, line.to_i, options) { body }
840
+ else
841
+ found = false
842
+ @preferred_extension = engine.to_s
843
+ find_template(views, data, template) do |file|
844
+ path ||= file # keep the initial path rather than the last one
845
+ if found = File.exist?(file)
846
+ path = file
847
+ break
848
+ end
849
+ end
850
+ throw :layout_missing if eat_errors and not found
851
+ template.new(path, 1, options)
852
+ end
853
+ when Proc, String
854
+ body = data.is_a?(String) ? Proc.new { data } : data
855
+ path, line = settings.caller_locations.first
856
+ template.new(path, line.to_i, options, &body)
857
+ else
858
+ raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
859
+ end
860
+ end
861
+ end
862
+ end
863
+
864
+ # Base class for all Sinatra applications and middleware.
865
+ class Base
866
+ include Rack::Utils
867
+ include Helpers
868
+ include Templates
869
+
870
+ URI_INSTANCE = URI.const_defined?(:Parser) ? URI::Parser.new : URI
871
+
872
+ attr_accessor :app, :env, :request, :response, :params
873
+ attr_reader :template_cache
874
+
875
+ def initialize(app = nil)
876
+ super()
877
+ @app = app
878
+ @template_cache = Tilt::Cache.new
879
+ yield self if block_given?
880
+ end
881
+
882
+ # Rack call interface.
883
+ def call(env)
884
+ dup.call!(env)
885
+ end
886
+
887
+ def call!(env) # :nodoc:
888
+ @env = env
889
+ @request = Request.new(env)
890
+ @response = Response.new
891
+ @params = indifferent_params(@request.params)
892
+ template_cache.clear if settings.reload_templates
893
+ force_encoding(@params)
894
+
895
+ @response['Content-Type'] = nil
896
+ invoke { dispatch! }
897
+ invoke { error_block!(response.status) } unless @env['sinatra.error']
898
+
899
+ unless @response['Content-Type']
900
+ if Array === body and body[0].respond_to? :content_type
901
+ content_type body[0].content_type
902
+ else
903
+ content_type :html
904
+ end
905
+ end
906
+
907
+ @response.finish
908
+ end
909
+
910
+ # Access settings defined with Base.set.
911
+ def self.settings
912
+ self
913
+ end
914
+
915
+ # Access settings defined with Base.set.
916
+ def settings
917
+ self.class.settings
918
+ end
919
+
920
+ def options
921
+ warn "Sinatra::Base#options is deprecated and will be removed, " \
922
+ "use #settings instead."
923
+ settings
924
+ end
925
+
926
+ # Exit the current block, halts any further processing
927
+ # of the request, and returns the specified response.
928
+ def halt(*response)
929
+ response = response.first if response.length == 1
930
+ throw :halt, response
931
+ end
932
+
933
+ # Pass control to the next matching route.
934
+ # If there are no more matching routes, Sinatra will
935
+ # return a 404 response.
936
+ def pass(&block)
937
+ throw :pass, block
938
+ end
939
+
940
+ # Forward the request to the downstream app -- middleware only.
941
+ def forward
942
+ fail "downstream app not set" unless @app.respond_to? :call
943
+ status, headers, body = @app.call env
944
+ @response.status = status
945
+ @response.body = body
946
+ @response.headers.merge! headers
947
+ nil
948
+ end
949
+
950
+ private
951
+
952
+ # Run filters defined on the class and all superclasses.
953
+ def filter!(tpe, base = settings)
954
+ filter! tpe, base.superclass if base.superclass.respond_to?(:filters)
955
+ base.filters[tpe].each { |args| process_route(*args) }
956
+ end
957
+
958
+ # Run routes defined on the class and all superclasses.
959
+ def route!(base = settings, pass_block = nil)
960
+ if routes = base.routes[@request.request_method]
961
+ routes.each do |pattern, keys, conditions, block|
962
+ returned_pass_block = process_route(pattern, keys, conditions) do |*args|
963
+ env['sinatra.route'] = block.instance_variable_get(:@route_name)
964
+ route_eval { block[*args] }
965
+ end
966
+
967
+ # don't wipe out pass_block in superclass
968
+ pass_block = returned_pass_block if returned_pass_block
969
+ end
970
+ end
971
+
972
+ # Run routes defined in superclass.
973
+ if base.superclass.respond_to?(:routes)
974
+ return route!(base.superclass, pass_block)
975
+ end
976
+
977
+ route_eval(&pass_block) if pass_block
978
+ route_missing
979
+ end
980
+
981
+ # Run a route block and throw :halt with the result.
982
+ def route_eval
983
+ throw :halt, yield
984
+ end
985
+
986
+ # If the current request matches pattern and conditions, fill params
987
+ # with keys and call the given block.
988
+ # Revert params afterwards.
989
+ #
990
+ # Returns pass block.
991
+ def process_route(pattern, keys, conditions, block = nil, values = [])
992
+ route = @request.path_info
993
+ route = '/' if route.empty? and not settings.empty_path_info?
994
+ return unless match = pattern.match(route)
995
+ values += match.captures.map! { |v| force_encoding URI_INSTANCE.unescape(v) if v }
996
+
997
+ if values.any?
998
+ original, @params = params, params.merge('splat' => [], 'captures' => values)
999
+ keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
1000
+ end
1001
+
1002
+ catch(:pass) do
1003
+ conditions.each { |c| throw :pass if c.bind(self).call == false }
1004
+ block ? block[self, values] : yield(self, values)
1005
+ end
1006
+ ensure
1007
+ @params = original if original
1008
+ end
1009
+
1010
+ # No matching route was found or all routes passed. The default
1011
+ # implementation is to forward the request downstream when running
1012
+ # as middleware (@app is non-nil); when no downstream app is set, raise
1013
+ # a NotFound exception. Subclasses can override this method to perform
1014
+ # custom route miss logic.
1015
+ def route_missing
1016
+ if @app
1017
+ forward
1018
+ else
1019
+ raise NotFound
1020
+ end
1021
+ end
1022
+
1023
+ # Attempt to serve static files from public directory. Throws :halt when
1024
+ # a matching file is found, returns nil otherwise.
1025
+ def static!(options = {})
1026
+ return if (public_dir = settings.public_folder).nil?
1027
+ path = File.expand_path("#{public_dir}#{unescape(request.path_info)}" )
1028
+ return unless File.file?(path)
1029
+
1030
+ env['sinatra.static_file'] = path
1031
+ cache_control(*settings.static_cache_control) if settings.static_cache_control?
1032
+ send_file path, options.merge(:disposition => nil)
1033
+ end
1034
+
1035
+ # Enable string or symbol key access to the nested params hash.
1036
+ def indifferent_params(object)
1037
+ case object
1038
+ when Hash
1039
+ new_hash = indifferent_hash
1040
+ object.each { |key, value| new_hash[key] = indifferent_params(value) }
1041
+ new_hash
1042
+ when Array
1043
+ object.map { |item| indifferent_params(item) }
1044
+ else
1045
+ object
1046
+ end
1047
+ end
1048
+
1049
+ # Creates a Hash with indifferent access.
1050
+ def indifferent_hash
1051
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
1052
+ end
1053
+
1054
+ # Run the block with 'throw :halt' support and apply result to the response.
1055
+ def invoke
1056
+ res = catch(:halt) { yield }
1057
+ res = [res] if Fixnum === res or String === res
1058
+ if Array === res and Fixnum === res.first
1059
+ res = res.dup
1060
+ status(res.shift)
1061
+ body(res.pop)
1062
+ headers(*res)
1063
+ elsif res.respond_to? :each
1064
+ body res
1065
+ end
1066
+ nil # avoid double setting the same response tuple twice
1067
+ end
1068
+
1069
+ # Dispatch a request with error handling.
1070
+ def dispatch!
1071
+ invoke do
1072
+ static! if settings.static? && (request.get? || request.head?)
1073
+ filter! :before
1074
+ route!
1075
+ end
1076
+ rescue ::Exception => boom
1077
+ invoke { handle_exception!(boom) }
1078
+ ensure
1079
+ begin
1080
+ filter! :after unless env['sinatra.static_file']
1081
+ rescue ::Exception => boom
1082
+ invoke { handle_exception!(boom) } unless @env['sinatra.error']
1083
+ end
1084
+ end
1085
+
1086
+ # Error handling during requests.
1087
+ def handle_exception!(boom)
1088
+ @env['sinatra.error'] = boom
1089
+
1090
+ if boom.respond_to? :http_status
1091
+ status(boom.http_status)
1092
+ elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
1093
+ status(boom.code)
1094
+ else
1095
+ status(500)
1096
+ end
1097
+
1098
+ status(500) unless status.between? 400, 599
1099
+
1100
+ if server_error?
1101
+ dump_errors! boom if settings.dump_errors?
1102
+ raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
1103
+ end
1104
+
1105
+ if not_found?
1106
+ headers['X-Cascade'] = 'pass' if settings.x_cascade?
1107
+ body '<h1>Not Found</h1>'
1108
+ end
1109
+
1110
+ res = error_block!(boom.class, boom) || error_block!(status, boom)
1111
+ return res if res or not server_error?
1112
+ raise boom if settings.raise_errors? or settings.show_exceptions?
1113
+ error_block! Exception, boom
1114
+ end
1115
+
1116
+ # Find an custom error block for the key(s) specified.
1117
+ def error_block!(key, *block_params)
1118
+ base = settings
1119
+ while base.respond_to?(:errors)
1120
+ next base = base.superclass unless args_array = base.errors[key]
1121
+ args_array.reverse_each do |args|
1122
+ first = args == args_array.first
1123
+ args += [block_params]
1124
+ resp = process_route(*args)
1125
+ return resp unless resp.nil? && !first
1126
+ end
1127
+ end
1128
+ return false unless key.respond_to? :superclass and key.superclass < Exception
1129
+ error_block!(key.superclass, *block_params)
1130
+ end
1131
+
1132
+ def dump_errors!(boom)
1133
+ msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
1134
+ @env['rack.errors'].puts(msg)
1135
+ end
1136
+
1137
+ class << self
1138
+ CALLERS_TO_IGNORE = [ # :nodoc:
1139
+ /\/sinatra(\/(base|main|show_exceptions))?\.rb$/, # all sinatra code
1140
+ /lib\/tilt.*\.rb$/, # all tilt code
1141
+ /^\(.*\)$/, # generated code
1142
+ /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
1143
+ /active_support/, # active_support require hacks
1144
+ /bundler(\/runtime)?\.rb/, # bundler require hacks
1145
+ /<internal:/, # internal in ruby >= 1.9.2
1146
+ /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1147
+ ]
1148
+
1149
+ # contrary to what the comment said previously, rubinius never supported this
1150
+ if defined?(RUBY_IGNORE_CALLERS)
1151
+ warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
1152
+ CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
1153
+ end
1154
+
1155
+ attr_reader :routes, :filters, :templates, :errors
1156
+
1157
+ # Removes all routes, filters, middleware and extension hooks from the
1158
+ # current class (not routes/filters/... defined by its superclass).
1159
+ def reset!
1160
+ @conditions = []
1161
+ @routes = {}
1162
+ @filters = {:before => [], :after => []}
1163
+ @errors = {}
1164
+ @middleware = []
1165
+ @prototype = nil
1166
+ @extensions = []
1167
+
1168
+ if superclass.respond_to?(:templates)
1169
+ @templates = Hash.new { |hash,key| superclass.templates[key] }
1170
+ else
1171
+ @templates = {}
1172
+ end
1173
+ end
1174
+
1175
+ # Extension modules registered on this class and all superclasses.
1176
+ def extensions
1177
+ if superclass.respond_to?(:extensions)
1178
+ (@extensions + superclass.extensions).uniq
1179
+ else
1180
+ @extensions
1181
+ end
1182
+ end
1183
+
1184
+ # Middleware used in this class and all superclasses.
1185
+ def middleware
1186
+ if superclass.respond_to?(:middleware)
1187
+ superclass.middleware + @middleware
1188
+ else
1189
+ @middleware
1190
+ end
1191
+ end
1192
+
1193
+ # Sets an option to the given value. If the value is a proc,
1194
+ # the proc will be called every time the option is accessed.
1195
+ def set(option, value = (not_set = true), ignore_setter = false, &block)
1196
+ raise ArgumentError if block and !not_set
1197
+ value, not_set = block, false if block
1198
+
1199
+ if not_set
1200
+ raise ArgumentError unless option.respond_to?(:each)
1201
+ option.each { |k,v| set(k, v) }
1202
+ return self
1203
+ end
1204
+
1205
+ if respond_to?("#{option}=") and not ignore_setter
1206
+ return __send__("#{option}=", value)
1207
+ end
1208
+
1209
+ setter = proc { |val| set option, val, true }
1210
+ getter = proc { value }
1211
+
1212
+ case value
1213
+ when Proc
1214
+ getter = value
1215
+ when Symbol, Fixnum, FalseClass, TrueClass, NilClass
1216
+ getter = value.inspect
1217
+ when Hash
1218
+ setter = proc do |val|
1219
+ val = value.merge val if Hash === val
1220
+ set option, val, true
1221
+ end
1222
+ end
1223
+
1224
+ define_singleton("#{option}=", setter) if setter
1225
+ define_singleton(option, getter) if getter
1226
+ define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
1227
+ self
1228
+ end
1229
+
1230
+ # Same as calling `set :option, true` for each of the given options.
1231
+ def enable(*opts)
1232
+ opts.each { |key| set(key, true) }
1233
+ end
1234
+
1235
+ # Same as calling `set :option, false` for each of the given options.
1236
+ def disable(*opts)
1237
+ opts.each { |key| set(key, false) }
1238
+ end
1239
+
1240
+ # Define a custom error handler. Optionally takes either an Exception
1241
+ # class, or an HTTP status code to specify which errors should be
1242
+ # handled.
1243
+ def error(*codes, &block)
1244
+ args = compile! "ERROR", //, block
1245
+ codes = codes.map { |c| Array(c) }.flatten
1246
+ codes << Exception if codes.empty?
1247
+ codes.each { |c| (@errors[c] ||= []) << args }
1248
+ end
1249
+
1250
+ # Sugar for `error(404) { ... }`
1251
+ def not_found(&block)
1252
+ error(404, &block)
1253
+ error(Sinatra::NotFound, &block)
1254
+ end
1255
+
1256
+ # Define a named template. The block must return the template source.
1257
+ def template(name, &block)
1258
+ filename, line = caller_locations.first
1259
+ templates[name] = [block, filename, line.to_i]
1260
+ end
1261
+
1262
+ # Define the layout template. The block must return the template source.
1263
+ def layout(name = :layout, &block)
1264
+ template name, &block
1265
+ end
1266
+
1267
+ # Load embedded templates from the file; uses the caller's __FILE__
1268
+ # when no file is specified.
1269
+ def inline_templates=(file = nil)
1270
+ file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
1271
+
1272
+ begin
1273
+ io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
1274
+ app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
1275
+ rescue Errno::ENOENT
1276
+ app, data = nil
1277
+ end
1278
+
1279
+ if data
1280
+ if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
1281
+ encoding = $2
1282
+ else
1283
+ encoding = settings.default_encoding
1284
+ end
1285
+ lines = app.count("\n") + 1
1286
+ template = nil
1287
+ force_encoding data, encoding
1288
+ data.each_line do |line|
1289
+ lines += 1
1290
+ if line =~ /^@@\s*(.*\S)\s*$/
1291
+ template = force_encoding('', encoding)
1292
+ templates[$1.to_sym] = [template, file, lines]
1293
+ elsif template
1294
+ template << line
1295
+ end
1296
+ end
1297
+ end
1298
+ end
1299
+
1300
+ # Lookup or register a mime type in Rack's mime registry.
1301
+ def mime_type(type, value = nil)
1302
+ return type if type.nil?
1303
+ return type.to_s if type.to_s.include?('/')
1304
+ type = ".#{type}" unless type.to_s[0] == ?.
1305
+ return Rack::Mime.mime_type(type, nil) unless value
1306
+ Rack::Mime::MIME_TYPES[type] = value
1307
+ end
1308
+
1309
+ # provides all mime types matching type, including deprecated types:
1310
+ # mime_types :html # => ['text/html']
1311
+ # mime_types :js # => ['application/javascript', 'text/javascript']
1312
+ def mime_types(type)
1313
+ type = mime_type type
1314
+ type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
1315
+ end
1316
+
1317
+ # Define a before filter; runs before all requests within the same
1318
+ # context as route handlers and may access/modify the request and
1319
+ # response.
1320
+ def before(path = nil, options = {}, &block)
1321
+ add_filter(:before, path, options, &block)
1322
+ end
1323
+
1324
+ # Define an after filter; runs after all requests within the same
1325
+ # context as route handlers and may access/modify the request and
1326
+ # response.
1327
+ def after(path = nil, options = {}, &block)
1328
+ add_filter(:after, path, options, &block)
1329
+ end
1330
+
1331
+ # add a filter
1332
+ def add_filter(type, path = nil, options = {}, &block)
1333
+ path, options = //, path if path.respond_to?(:each_pair)
1334
+ filters[type] << compile!(type, path || //, block, options)
1335
+ end
1336
+
1337
+ # Add a route condition. The route is considered non-matching when the
1338
+ # block returns false.
1339
+ def condition(name = "#{caller.first[/`.*'/]} condition", &block)
1340
+ @conditions << generate_method(name, &block)
1341
+ end
1342
+
1343
+ def public=(value)
1344
+ warn ":public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead"
1345
+ set(:public_folder, value)
1346
+ end
1347
+
1348
+ def public_dir=(value)
1349
+ self.public_folder = value
1350
+ end
1351
+
1352
+ def public_dir
1353
+ public_folder
1354
+ end
1355
+
1356
+ # Defining a `GET` handler also automatically defines
1357
+ # a `HEAD` handler.
1358
+ def get(path, opts = {}, &block)
1359
+ conditions = @conditions.dup
1360
+ route('GET', path, opts, &block)
1361
+
1362
+ @conditions = conditions
1363
+ route('HEAD', path, opts, &block)
1364
+ end
1365
+
1366
+ def put(path, opts = {}, &bk) route 'PUT', path, opts, &bk end
1367
+ def post(path, opts = {}, &bk) route 'POST', path, opts, &bk end
1368
+ def delete(path, opts = {}, &bk) route 'DELETE', path, opts, &bk end
1369
+ def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
1370
+ def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
1371
+ def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
1372
+ def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end
1373
+ def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end
1374
+
1375
+ # Makes the methods defined in the block and in the Modules given
1376
+ # in `extensions` available to the handlers and templates
1377
+ def helpers(*extensions, &block)
1378
+ class_eval(&block) if block_given?
1379
+ include(*extensions) if extensions.any?
1380
+ end
1381
+
1382
+ # Register an extension. Alternatively take a block from which an
1383
+ # extension will be created and registered on the fly.
1384
+ def register(*extensions, &block)
1385
+ extensions << Module.new(&block) if block_given?
1386
+ @extensions += extensions
1387
+ extensions.each do |extension|
1388
+ extend extension
1389
+ extension.registered(self) if extension.respond_to?(:registered)
1390
+ end
1391
+ end
1392
+
1393
+ def development?; environment == :development end
1394
+ def production?; environment == :production end
1395
+ def test?; environment == :test end
1396
+
1397
+ # Set configuration options for Sinatra and/or the app.
1398
+ # Allows scoping of settings for certain environments.
1399
+ def configure(*envs)
1400
+ yield self if envs.empty? || envs.include?(environment.to_sym)
1401
+ end
1402
+
1403
+ # Use the specified Rack middleware
1404
+ def use(middleware, *args, &block)
1405
+ @prototype = nil
1406
+ @middleware << [middleware, args, block]
1407
+ end
1408
+
1409
+ # Stop the self-hosted server if running.
1410
+ def quit!
1411
+ return unless running?
1412
+ # Use Thin's hard #stop! if available, otherwise just #stop.
1413
+ running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
1414
+ $stderr.puts "== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
1415
+ set :running_server, nil
1416
+ set :handler_name, nil
1417
+ end
1418
+
1419
+ alias_method :stop!, :quit!
1420
+
1421
+ # Run the Sinatra app as a self-hosted server using
1422
+ # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1423
+ # with the constructed handler once we have taken the stage.
1424
+ def run!(options = {}, &block)
1425
+ return if running?
1426
+ set options
1427
+ handler = detect_rack_handler
1428
+ handler_name = handler.name.gsub(/.*::/, '')
1429
+ server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1430
+ server_settings.merge!(:Port => port, :Host => bind)
1431
+
1432
+ begin
1433
+ start_server(handler, server_settings, handler_name, &block)
1434
+ rescue Errno::EADDRINUSE
1435
+ $stderr.puts "== Someone is already performing on port #{port}!"
1436
+ raise
1437
+ ensure
1438
+ quit!
1439
+ end
1440
+ end
1441
+
1442
+ alias_method :start!, :run!
1443
+
1444
+ # Check whether the self-hosted server is running or not.
1445
+ def running?
1446
+ running_server?
1447
+ end
1448
+
1449
+ # The prototype instance used to process requests.
1450
+ def prototype
1451
+ @prototype ||= new
1452
+ end
1453
+
1454
+ # Creates a Rack::Builder instance with all the middleware set up and
1455
+ # the given +app+ as end point.
1456
+ def build(app)
1457
+ builder = Rack::Builder.new
1458
+ setup_default_middleware builder
1459
+ setup_middleware builder
1460
+ builder.run app
1461
+ builder
1462
+ end
1463
+
1464
+ def call(env)
1465
+ synchronize { prototype.call(env) }
1466
+ end
1467
+
1468
+ # Like Kernel#caller but excluding certain magic entries and without
1469
+ # line / method information; the resulting array contains filenames only.
1470
+ def caller_files
1471
+ cleaned_caller(1).flatten
1472
+ end
1473
+
1474
+ # Like caller_files, but containing Arrays rather than strings with the
1475
+ # first element being the file, and the second being the line.
1476
+ def caller_locations
1477
+ cleaned_caller 2
1478
+ end
1479
+
1480
+ private
1481
+
1482
+ # Starts the server by running the Rack Handler.
1483
+ def start_server(handler, server_settings, handler_name)
1484
+ handler.run(self, server_settings) do |server|
1485
+ unless handler_name =~ /cgi/i
1486
+ $stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
1487
+ "on #{port} for #{environment} with backup from #{handler_name}"
1488
+ end
1489
+
1490
+ setup_traps
1491
+ set :running_server, server
1492
+ set :handler_name, handler_name
1493
+ server.threaded = settings.threaded if server.respond_to? :threaded=
1494
+
1495
+ yield server if block_given?
1496
+ end
1497
+ end
1498
+
1499
+ def setup_traps
1500
+ if traps?
1501
+ at_exit { quit! }
1502
+
1503
+ [:INT, :TERM].each do |signal|
1504
+ old_handler = trap(signal) do
1505
+ quit!
1506
+ old_handler.call if old_handler.respond_to?(:call)
1507
+ end
1508
+ end
1509
+
1510
+ set :traps, false
1511
+ end
1512
+ end
1513
+
1514
+ # Dynamically defines a method on settings.
1515
+ def define_singleton(name, content = Proc.new)
1516
+ # replace with call to singleton_class once we're 1.9 only
1517
+ (class << self; self; end).class_eval do
1518
+ undef_method(name) if method_defined? name
1519
+ String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
1520
+ end
1521
+ end
1522
+
1523
+ # Condition for matching host name. Parameter might be String or Regexp.
1524
+ def host_name(pattern)
1525
+ condition { pattern === request.host }
1526
+ end
1527
+
1528
+ # Condition for matching user agent. Parameter should be Regexp.
1529
+ # Will set params[:agent].
1530
+ def user_agent(pattern)
1531
+ condition do
1532
+ if request.user_agent.to_s =~ pattern
1533
+ @params[:agent] = $~[1..-1]
1534
+ true
1535
+ else
1536
+ false
1537
+ end
1538
+ end
1539
+ end
1540
+ alias_method :agent, :user_agent
1541
+
1542
+ # Condition for matching mimetypes. Accepts file extensions.
1543
+ def provides(*types)
1544
+ types.map! { |t| mime_types(t) }
1545
+ types.flatten!
1546
+ condition do
1547
+ if type = response['Content-Type']
1548
+ types.include? type or types.include? type[/^[^;]+/]
1549
+ elsif type = request.preferred_type(types)
1550
+ params = (type.respond_to?(:params) ? type.params : {})
1551
+ content_type(type, params)
1552
+ true
1553
+ else
1554
+ false
1555
+ end
1556
+ end
1557
+ end
1558
+
1559
+ def route(verb, path, options = {}, &block)
1560
+ # Because of self.options.host
1561
+ host_name(options.delete(:host)) if options.key?(:host)
1562
+ enable :empty_path_info if path == "" and empty_path_info.nil?
1563
+ signature = compile!(verb, path, block, options)
1564
+ (@routes[verb] ||= []) << signature
1565
+ invoke_hook(:route_added, verb, path, block)
1566
+ signature
1567
+ end
1568
+
1569
+ def invoke_hook(name, *args)
1570
+ extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
1571
+ end
1572
+
1573
+ def generate_method(method_name, &block)
1574
+ method_name = method_name.to_sym
1575
+ define_method(method_name, &block)
1576
+ method = instance_method method_name
1577
+ remove_method method_name
1578
+ method
1579
+ end
1580
+
1581
+ def compile!(verb, path, block, options = {})
1582
+ options.each_pair { |option, args| send(option, *args) }
1583
+ method_name = "#{verb} #{path}"
1584
+ unbound_method = generate_method(method_name, &block)
1585
+ pattern, keys = compile path
1586
+ conditions, @conditions = @conditions, []
1587
+
1588
+ wrapper = block.arity != 0 ?
1589
+ proc { |a,p| unbound_method.bind(a).call(*p) } :
1590
+ proc { |a,p| unbound_method.bind(a).call }
1591
+ wrapper.instance_variable_set(:@route_name, method_name)
1592
+
1593
+ [ pattern, keys, conditions, wrapper ]
1594
+ end
1595
+
1596
+ def compile(path)
1597
+ if path.respond_to? :to_str
1598
+ keys = []
1599
+
1600
+ # We append a / at the end if there was one.
1601
+ # Reason: Splitting does not split off an empty
1602
+ # string at the end if the split separator
1603
+ # is at the end.
1604
+ #
1605
+ postfix = '/' if path =~ /\/\z/
1606
+
1607
+ # Split the path into pieces in between forward slashes.
1608
+ #
1609
+ segments = path.split('/').map! do |segment|
1610
+ ignore = []
1611
+
1612
+ # Special character handling.
1613
+ #
1614
+ pattern = segment.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
1615
+ ignore << escaped(c).join if c.match(/[\.@]/)
1616
+ patt = encoded(c)
1617
+ patt.gsub(/%[\da-fA-F]{2}/) do |match|
1618
+ match.split(//).map! {|char| char =~ /[A-Z]/ ? "[#{char}#{char.tr('A-Z', 'a-z')}]" : char}.join
1619
+ end
1620
+ end
1621
+
1622
+ ignore = ignore.uniq.join
1623
+
1624
+ # Key handling.
1625
+ #
1626
+ pattern.gsub(/((:\w+)|\*)/) do |match|
1627
+ if match == "*"
1628
+ keys << 'splat'
1629
+ "(.*?)"
1630
+ else
1631
+ keys << $2[1..-1]
1632
+ ignore_pattern = safe_ignore(ignore)
1633
+
1634
+ ignore_pattern
1635
+ end
1636
+ end
1637
+ end
1638
+
1639
+ # Special case handling.
1640
+ #
1641
+ if segment = segments.pop
1642
+ if segment.match(/\[\^\\\./)
1643
+ parts = segment.rpartition(/\[\^\\\./)
1644
+ parts[1] = '[^'
1645
+ segments << parts.join
1646
+ else
1647
+ segments << segment
1648
+ end
1649
+ end
1650
+ [/\A#{segments.join('/')}#{postfix}\z/, keys]
1651
+ elsif path.respond_to?(:keys) && path.respond_to?(:match)
1652
+ [path, path.keys]
1653
+ elsif path.respond_to?(:names) && path.respond_to?(:match)
1654
+ [path, path.names]
1655
+ elsif path.respond_to? :match
1656
+ [path, []]
1657
+ else
1658
+ raise TypeError, path
1659
+ end
1660
+ end
1661
+
1662
+ def encoded(char)
1663
+ enc = URI_INSTANCE.escape(char)
1664
+ enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
1665
+ enc = "(?:#{enc}|#{encoded('+')})" if char == " "
1666
+ enc
1667
+ end
1668
+
1669
+ def escaped(char, enc = URI_INSTANCE.escape(char))
1670
+ [Regexp.escape(enc), URI_INSTANCE.escape(char, /./)]
1671
+ end
1672
+
1673
+ def safe_ignore(ignore)
1674
+ unsafe_ignore = []
1675
+ ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
1676
+ unsafe_ignore << hex[1..2]
1677
+ ''
1678
+ end
1679
+ unsafe_patterns = unsafe_ignore.map! do |unsafe|
1680
+ chars = unsafe.split(//).map! do |char|
1681
+ if char =~ /[A-Z]/
1682
+ char <<= char.tr('A-Z', 'a-z')
1683
+ end
1684
+ char
1685
+ end
1686
+
1687
+ "|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
1688
+ end
1689
+ if unsafe_patterns.length > 0
1690
+ "((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
1691
+ else
1692
+ "([^#{ignore}/?#]+)"
1693
+ end
1694
+ end
1695
+
1696
+ def setup_default_middleware(builder)
1697
+ builder.use ExtendedRack
1698
+ builder.use ShowExceptions if show_exceptions?
1699
+ builder.use Rack::MethodOverride if method_override?
1700
+ builder.use Rack::Head
1701
+ setup_logging builder
1702
+ setup_sessions builder
1703
+ setup_protection builder
1704
+ end
1705
+
1706
+ def setup_middleware(builder)
1707
+ middleware.each { |c,a,b| builder.use(c, *a, &b) }
1708
+ end
1709
+
1710
+ def setup_logging(builder)
1711
+ if logging?
1712
+ setup_common_logger(builder)
1713
+ setup_custom_logger(builder)
1714
+ elsif logging == false
1715
+ setup_null_logger(builder)
1716
+ end
1717
+ end
1718
+
1719
+ def setup_null_logger(builder)
1720
+ builder.use Rack::NullLogger
1721
+ end
1722
+
1723
+ def setup_common_logger(builder)
1724
+ builder.use Sinatra::CommonLogger
1725
+ end
1726
+
1727
+ def setup_custom_logger(builder)
1728
+ if logging.respond_to? :to_int
1729
+ builder.use Rack::Logger, logging
1730
+ else
1731
+ builder.use Rack::Logger
1732
+ end
1733
+ end
1734
+
1735
+ def setup_protection(builder)
1736
+ return unless protection?
1737
+ options = Hash === protection ? protection.dup : {}
1738
+ protect_session = options.fetch(:session) { sessions? }
1739
+ options[:except] = Array options[:except]
1740
+ options[:except] += [:session_hijacking, :remote_token] unless protect_session
1741
+ options[:reaction] ||= :drop_session
1742
+ builder.use Rack::Protection, options
1743
+ end
1744
+
1745
+ def setup_sessions(builder)
1746
+ return unless sessions?
1747
+ options = {}
1748
+ options[:secret] = session_secret if session_secret?
1749
+ options.merge! sessions.to_hash if sessions.respond_to? :to_hash
1750
+ builder.use Rack::Session::Cookie, options
1751
+ end
1752
+
1753
+ def detect_rack_handler
1754
+ servers = Array(server)
1755
+ servers.each do |server_name|
1756
+ begin
1757
+ return Rack::Handler.get(server_name.to_s)
1758
+ rescue LoadError, NameError
1759
+ end
1760
+ end
1761
+ fail "Server handler (#{servers.join(',')}) not found."
1762
+ end
1763
+
1764
+ def inherited(subclass)
1765
+ subclass.reset!
1766
+ subclass.set :app_file, caller_files.first unless subclass.app_file?
1767
+ super
1768
+ end
1769
+
1770
+ @@mutex = Mutex.new
1771
+ def synchronize(&block)
1772
+ if lock?
1773
+ @@mutex.synchronize(&block)
1774
+ else
1775
+ yield
1776
+ end
1777
+ end
1778
+
1779
+ # used for deprecation warnings
1780
+ def warn(message)
1781
+ super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
1782
+ end
1783
+
1784
+ # Like Kernel#caller but excluding certain magic entries
1785
+ def cleaned_caller(keep = 3)
1786
+ caller(1).
1787
+ map! { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
1788
+ reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
1789
+ end
1790
+ end
1791
+
1792
+ # Fixes encoding issues by
1793
+ # * defaulting to UTF-8
1794
+ # * casting params to Encoding.default_external
1795
+ #
1796
+ # The latter might not be necessary if Rack handles it one day.
1797
+ # Keep an eye on Rack's LH #100.
1798
+ def force_encoding(*args) settings.force_encoding(*args) end
1799
+ if defined? Encoding
1800
+ def self.force_encoding(data, encoding = default_encoding)
1801
+ return if data == settings || data.is_a?(Tempfile)
1802
+ if data.respond_to? :force_encoding
1803
+ data.force_encoding(encoding).encode!
1804
+ elsif data.respond_to? :each_value
1805
+ data.each_value { |v| force_encoding(v, encoding) }
1806
+ elsif data.respond_to? :each
1807
+ data.each { |v| force_encoding(v, encoding) }
1808
+ end
1809
+ data
1810
+ end
1811
+ else
1812
+ def self.force_encoding(data, *) data end
1813
+ end
1814
+
1815
+ reset!
1816
+
1817
+ set :environment, (ENV['RACK_ENV'] || :development).to_sym
1818
+ set :raise_errors, Proc.new { test? }
1819
+ set :dump_errors, Proc.new { !test? }
1820
+ set :show_exceptions, Proc.new { development? }
1821
+ set :sessions, false
1822
+ set :logging, false
1823
+ set :protection, true
1824
+ set :method_override, false
1825
+ set :use_code, false
1826
+ set :default_encoding, "utf-8"
1827
+ set :x_cascade, true
1828
+ set :add_charset, %w[javascript xml xhtml+xml].map { |t| "application/#{t}" }
1829
+ settings.add_charset << /^text\//
1830
+
1831
+ # explicitly generating a session secret eagerly to play nice with preforking
1832
+ begin
1833
+ require 'securerandom'
1834
+ set :session_secret, SecureRandom.hex(64)
1835
+ rescue LoadError, NotImplementedError
1836
+ # SecureRandom raises a NotImplementedError if no random device is available
1837
+ set :session_secret, "%064x" % Kernel.rand(2**256-1)
1838
+ end
1839
+
1840
+ class << self
1841
+ alias_method :methodoverride?, :method_override?
1842
+ alias_method :methodoverride=, :method_override=
1843
+ end
1844
+
1845
+ set :run, false # start server via at-exit hook?
1846
+ set :running_server, nil
1847
+ set :handler_name, nil
1848
+ set :traps, true
1849
+ set :server, %w[HTTP webrick]
1850
+ set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
1851
+ set :port, Integer(ENV['PORT'] && !ENV['PORT'].empty? ? ENV['PORT'] : 4567)
1852
+
1853
+ ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1854
+
1855
+ if ruby_engine == 'macruby'
1856
+ server.unshift 'control_tower'
1857
+ else
1858
+ server.unshift 'reel'
1859
+ server.unshift 'mongrel' if ruby_engine.nil?
1860
+ server.unshift 'puma' if ruby_engine != 'rbx'
1861
+ server.unshift 'thin' if ruby_engine != 'jruby'
1862
+ server.unshift 'puma' if ruby_engine == 'rbx'
1863
+ server.unshift 'trinidad' if ruby_engine == 'jruby'
1864
+ end
1865
+
1866
+ set :absolute_redirects, true
1867
+ set :prefixed_redirects, false
1868
+ set :empty_path_info, nil
1869
+
1870
+ set :app_file, nil
1871
+ set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
1872
+ set :views, Proc.new { root && File.join(root, 'views') }
1873
+ set :reload_templates, Proc.new { development? }
1874
+ set :lock, false
1875
+ set :threaded, true
1876
+
1877
+ set :public_folder, Proc.new { root && File.join(root, 'public') }
1878
+ set :static, Proc.new { public_folder && File.exist?(public_folder) }
1879
+ set :static_cache_control, false
1880
+
1881
+ error ::Exception do
1882
+ response.status = 500
1883
+ content_type 'text/html'
1884
+ '<h1>Internal Server Error</h1>'
1885
+ end
1886
+
1887
+ configure :development do
1888
+ get '/__sinatra__/:image.png' do
1889
+ filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
1890
+ content_type :png
1891
+ send_file filename
1892
+ end
1893
+
1894
+ error NotFound do
1895
+ content_type 'text/html'
1896
+
1897
+ if self.class == Sinatra::Application
1898
+ code = <<-RUBY.gsub(/^ {12}/, '')
1899
+ #{request.request_method.downcase} '#{request.path_info}' do
1900
+ "Hello World"
1901
+ end
1902
+ RUBY
1903
+ else
1904
+ code = <<-RUBY.gsub(/^ {12}/, '')
1905
+ class #{self.class}
1906
+ #{request.request_method.downcase} '#{request.path_info}' do
1907
+ "Hello World"
1908
+ end
1909
+ end
1910
+ RUBY
1911
+
1912
+ file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
1913
+ code = "# in #{file}\n#{code}" unless file.empty?
1914
+ end
1915
+
1916
+ (<<-HTML).gsub(/^ {10}/, '')
1917
+ <!DOCTYPE html>
1918
+ <html>
1919
+ <head>
1920
+ <style type="text/css">
1921
+ body { text-align:center;font-family:helvetica,arial;font-size:22px;
1922
+ color:#888;margin:20px}
1923
+ #c {margin:0 auto;width:500px;text-align:left}
1924
+ </style>
1925
+ </head>
1926
+ <body>
1927
+ <h2>Sinatra doesn&rsquo;t know this ditty.</h2>
1928
+ <img src='#{uri "/__sinatra__/404.png"}'>
1929
+ <div id="c">
1930
+ Try this:
1931
+ <pre>#{code}</pre>
1932
+ </div>
1933
+ </body>
1934
+ </html>
1935
+ HTML
1936
+ end
1937
+ end
1938
+ end
1939
+
1940
+ # Execution context for classic style (top-level) applications. All
1941
+ # DSL methods executed on main are delegated to this class.
1942
+ #
1943
+ # The Application class should not be subclassed, unless you want to
1944
+ # inherit all settings, routes, handlers, and error pages from the
1945
+ # top-level. Subclassing Sinatra::Base is highly recommended for
1946
+ # modular applications.
1947
+ class Application < Base
1948
+ set :logging, Proc.new { ! test? }
1949
+ set :method_override, true
1950
+ set :run, Proc.new { ! test? }
1951
+ set :session_secret, Proc.new { super() unless development? }
1952
+ set :app_file, nil
1953
+
1954
+ def self.register(*extensions, &block) #:nodoc:
1955
+ added_methods = extensions.map {|m| m.public_instance_methods }.flatten
1956
+ Delegator.delegate(*added_methods)
1957
+ super(*extensions, &block)
1958
+ end
1959
+ end
1960
+
1961
+ # Sinatra delegation mixin. Mixing this module into an object causes all
1962
+ # methods to be delegated to the Sinatra::Application class. Used primarily
1963
+ # at the top-level.
1964
+ module Delegator #:nodoc:
1965
+ def self.delegate(*methods)
1966
+ methods.each do |method_name|
1967
+ define_method(method_name) do |*args, &block|
1968
+ return super(*args, &block) if respond_to? method_name
1969
+ Delegator.target.send(method_name, *args, &block)
1970
+ end
1971
+ private method_name
1972
+ end
1973
+ end
1974
+
1975
+ delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
1976
+ :template, :layout, :before, :after, :error, :not_found, :configure,
1977
+ :set, :mime_type, :enable, :disable, :use, :development?, :test?,
1978
+ :production?, :helpers, :settings, :register
1979
+
1980
+ class << self
1981
+ attr_accessor :target
1982
+ end
1983
+
1984
+ self.target = Application
1985
+ end
1986
+
1987
+ class Wrapper
1988
+ def initialize(stack, instance)
1989
+ @stack, @instance = stack, instance
1990
+ end
1991
+
1992
+ def settings
1993
+ @instance.settings
1994
+ end
1995
+
1996
+ def helpers
1997
+ @instance
1998
+ end
1999
+
2000
+ def call(env)
2001
+ @stack.call(env)
2002
+ end
2003
+
2004
+ def inspect
2005
+ "#<#{@instance.class} app_file=#{settings.app_file.inspect}>"
2006
+ end
2007
+ end
2008
+
2009
+ # Extend the top-level DSL with the modules provided.
2010
+ def self.register(*extensions, &block)
2011
+ Delegator.target.register(*extensions, &block)
2012
+ end
2013
+
2014
+ # Include the helper modules provided in Sinatra's request context.
2015
+ def self.helpers(*extensions, &block)
2016
+ Delegator.target.helpers(*extensions, &block)
2017
+ end
2018
+
2019
+ # Use the middleware for classic applications.
2020
+ def self.use(*args, &block)
2021
+ Delegator.target.use(*args, &block)
2022
+ end
2023
+ end