rsense-core 0.6.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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