Class | Sinatra::Base |
In: |
lib/sinatra/base.rb
|
Parent: | Object |
Base class for all Sinatra applications and middleware.
CALLERS_TO_IGNORE | = | [ /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code /lib\/tilt.*\.rb$/, # all tilt code /\(.*\)/, # generated code /custom_require\.rb$/, # rubygems require hacks /active_support/, # active_support require hacks ] |
user_agent | -> | agent |
method_override? | -> | methodoverride? |
method_override= | -> | methodoverride= |
after_filters | [R] | |
app | [RW] | |
before_filters | [R] | |
env | [RW] | |
errors | [R] | |
params | [RW] | |
request | [RW] | |
response | [RW] | |
routes | [R] | |
templates | [R] |
# File lib/sinatra/base.rb, line 978 978: def call(env) 979: synchronize { prototype.call(env) } 980: end
Like Kernel#caller but excluding certain magic entries and without line / method information; the resulting array contains filenames only.
# File lib/sinatra/base.rb, line 1028 1028: def caller_files 1029: caller_locations. 1030: map { |file,line| file } 1031: end
# File lib/sinatra/base.rb, line 1033 1033: def caller_locations 1034: caller(1). 1035: map { |line| line.split(/:(?=\d|in )/)[0,2] }. 1036: reject { |file,line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } } 1037: end
# File lib/sinatra/base.rb, line 846 846: def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk end
Defining a `GET` handler also automatically defines a `HEAD` handler.
# File lib/sinatra/base.rb, line 836 836: def get(path, opts={}, &block) 837: conditions = @conditions.dup 838: route('GET', path, opts, &block) 839: 840: @conditions = conditions 841: route('HEAD', path, opts, &block) 842: end
# File lib/sinatra/base.rb, line 847 847: def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk end
Makes the methods defined in the block and in the Modules given in `extensions` available to the handlers and templates
# File lib/sinatra/base.rb, line 908 908: def helpers(*extensions, &block) 909: class_eval(&block) if block_given? 910: include(*extensions) if extensions.any? 911: end
Create a new instance of the class fronted by its middleware pipeline. The object is guaranteed to respond to call but may not be an instance of the class new was called on.
# File lib/sinatra/base.rb, line 966 966: def new(*args, &bk) 967: builder = Rack::Builder.new 968: builder.use Rack::Session::Cookie if sessions? 969: builder.use Rack::CommonLogger if logging? 970: builder.use Rack::MethodOverride if method_override? 971: builder.use ShowExceptions if show_exceptions? 972: middleware.each { |c,a,b| builder.use(c, *a, &b) } 973: 974: builder.run super 975: builder.to_app 976: end
# File lib/sinatra/base.rb, line 391 391: def initialize(app=nil) 392: @app = app 393: @template_cache = Tilt::Cache.new 394: yield self if block_given? 395: end
# File lib/sinatra/base.rb, line 845 845: def post(path, opts={}, &bk); route 'POST', path, opts, &bk end
# File lib/sinatra/base.rb, line 844 844: def put(path, opts={}, &bk); route 'PUT', path, opts, &bk end
# File lib/sinatra/base.rb, line 913 913: def register(*extensions, &block) 914: extensions << Module.new(&block) if block_given? 915: @extensions += extensions 916: extensions.each do |extension| 917: extend extension 918: extension.registered(self) if extension.respond_to?(:registered) 919: end 920: end
Run the Sinatra app as a self-hosted server using Thin, Mongrel or WEBrick (in that order)
# File lib/sinatra/base.rb, line 940 940: def run!(options={}) 941: set options 942: handler = detect_rack_handler 943: handler_name = handler.name.gsub(/.*::/, '') 944: puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " + 945: "on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i 946: handler.run self, :Host => bind, :Port => port do |server| 947: trap(:INT) do 948: ## Use thins' hard #stop! if available, otherwise just #stop 949: server.respond_to?(:stop!) ? server.stop! : server.stop 950: puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i 951: end 952: set :running, true 953: end 954: rescue Errno::EADDRINUSE => e 955: puts "== Someone is already performing on port #{port}!" 956: end
Use the specified Rack middleware
# File lib/sinatra/base.rb, line 933 933: def use(middleware, *args, &block) 934: @prototype = nil 935: @middleware << [middleware, args, block] 936: end
# File lib/sinatra/base.rb, line 878 878: def compile(path) 879: keys = [] 880: if path.respond_to? :to_str 881: special_chars = %w{. + ( )} 882: pattern = 883: path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match| 884: case match 885: when "*" 886: keys << 'splat' 887: "(.*?)" 888: when *special_chars 889: Regexp.escape(match) 890: else 891: keys << $2[1..-1] 892: "([^/?&#]+)" 893: end 894: end 895: [/^#{pattern}$/, keys] 896: elsif path.respond_to?(:keys) && path.respond_to?(:match) 897: [path, path.keys] 898: elsif path.respond_to? :match 899: [path, keys] 900: else 901: raise TypeError, path 902: end 903: end
# File lib/sinatra/base.rb, line 983 983: def detect_rack_handler 984: servers = Array(self.server) 985: servers.each do |server_name| 986: begin 987: return Rack::Handler.get(server_name.downcase) 988: rescue LoadError 989: rescue NameError 990: end 991: end 992: fail "Server handler (#{servers.join(',')}) not found." 993: end
Extension modules registered on this class and all superclasses.
# File lib/sinatra/base.rb, line 677 677: def extensions 678: if superclass.respond_to?(:extensions) 679: (@extensions + superclass.extensions).uniq 680: else 681: @extensions 682: end 683: end
# File lib/sinatra/base.rb, line 802 802: def host_name(pattern) 803: condition { pattern === request.host } 804: end
# File lib/sinatra/base.rb, line 995 995: def inherited(subclass) 996: subclass.reset! 997: super 998: end
Load embeded templates from the file; uses the caller‘s FILE when no file is specified.
# File lib/sinatra/base.rb, line 748 748: def inline_templates=(file=nil) 749: file = (file.nil? || file == true) ? caller_files.first : file 750: 751: begin 752: app, data = 753: ::IO.read(file).gsub("\r\n", "\n").split(/^__END__$/, 2) 754: rescue Errno::ENOENT 755: app, data = nil 756: end 757: 758: if data 759: lines = app.count("\n") + 1 760: template = nil 761: data.each_line do |line| 762: lines += 1 763: if line =~ /^@@\s*(.*)/ 764: template = '' 765: templates[$1.to_sym] = [template, file, lines] 766: elsif template 767: template << line 768: end 769: end 770: end 771: end
# File lib/sinatra/base.rb, line 874 874: def invoke_hook(name, *args) 875: extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } 876: end
# File lib/sinatra/base.rb, line 1009 1009: def metadef(message, &block) 1010: (class << self; self; end). 1011: send :define_method, message, &block 1012: end
Middleware used in this class and all superclasses.
# File lib/sinatra/base.rb, line 686 686: def middleware 687: if superclass.respond_to?(:middleware) 688: superclass.middleware + @middleware 689: else 690: @middleware 691: end 692: end
Lookup or register a mime type in Rack‘s mime registry.
# File lib/sinatra/base.rb, line 774 774: def mime_type(type, value=nil) 775: return type if type.nil? || type.to_s.include?('/') 776: type = ".#{type}" unless type.to_s[0] == ?. 777: return Rack::Mime.mime_type(type, nil) unless value 778: Rack::Mime::MIME_TYPES[type] = value 779: end
# File lib/sinatra/base.rb, line 818 818: def provides(*types) 819: types = [types] unless types.kind_of? Array 820: types.map!{|t| mime_type(t)} 821: 822: condition { 823: matching_types = (request.accept & types) 824: unless matching_types.empty? 825: response.headers['Content-Type'] = matching_types.first 826: true 827: else 828: false 829: end 830: } 831: end
# File lib/sinatra/base.rb, line 659 659: def reset! 660: @conditions = [] 661: @routes = {} 662: @before_filters = [] 663: @after_filters = [] 664: @errors = {} 665: @middleware = [] 666: @prototype = nil 667: @extensions = [] 668: 669: if superclass.respond_to?(:templates) 670: @templates = Hash.new { |hash,key| superclass.templates[key] } 671: else 672: @templates = {} 673: end 674: end
# File lib/sinatra/base.rb, line 850 850: def route(verb, path, options={}, &block) 851: # Because of self.options.host 852: host_name(options.delete(:bind)) if options.key?(:host) 853: 854: options.each {|option, args| send(option, *args)} 855: 856: pattern, keys = compile(path) 857: conditions, @conditions = @conditions, [] 858: 859: define_method "#{verb} #{path}", &block 860: unbound_method = instance_method("#{verb} #{path}") 861: block = 862: if block.arity != 0 863: proc { unbound_method.bind(self).call(*@block_params) } 864: else 865: proc { unbound_method.bind(self).call } 866: end 867: 868: invoke_hook(:route_added, verb, path, block) 869: 870: (@routes[verb] ||= []). 871: push([pattern, keys, conditions, block]).last 872: end
Sets an option to the given value. If the value is a proc, the proc will be called every time the option is accessed.
# File lib/sinatra/base.rb, line 696 696: def set(option, value=self, &block) 697: raise ArgumentError if block && value != self 698: value = block if block 699: if value.kind_of?(Proc) 700: metadef(option, &value) 701: metadef("#{option}?") { !!__send__(option) } 702: metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) } 703: elsif value == self && option.respond_to?(:to_hash) 704: option.to_hash.each { |k,v| set(k, v) } 705: elsif respond_to?("#{option}=") 706: __send__ "#{option}=", value 707: else 708: set option, Proc.new{value} 709: end 710: self 711: end
# File lib/sinatra/base.rb, line 1001 1001: def synchronize(&block) 1002: if lock? 1003: @@mutex.synchronize(&block) 1004: else 1005: yield 1006: end 1007: end
# File lib/sinatra/base.rb, line 806 806: def user_agent(pattern) 807: condition { 808: if request.user_agent =~ pattern 809: @params[:agent] = $~[1..-1] 810: true 811: else 812: false 813: end 814: } 815: end
# File lib/sinatra/base.rb, line 404 404: def call!(env) 405: @env = env 406: @request = Request.new(env) 407: @response = Response.new 408: @params = indifferent_params(@request.params) 409: @template_cache.clear if settings.reload_templates 410: 411: invoke { dispatch! } 412: invoke { error_block!(response.status) } 413: 414: status, header, body = @response.finish 415: 416: # Never produce a body on HEAD requests. Do retain the Content-Length 417: # unless it's "0", in which case we assume it was calculated erroneously 418: # for a manual HEAD response and remove it entirely. 419: if @env['REQUEST_METHOD'] == 'HEAD' 420: body = [] 421: header.delete('Content-Length') if header['Content-Length'] == '0' 422: end 423: 424: [status, header, body] 425: end
Forward the request to the downstream app — middleware only.
# File lib/sinatra/base.rb, line 448 448: def forward 449: fail "downstream app not set" unless @app.respond_to? :call 450: status, headers, body = @app.call(@request.env) 451: @response.status = status 452: @response.body = body 453: @response.headers.merge! headers 454: nil 455: end
Exit the current block, halts any further processing of the request, and returns the specified response.
# File lib/sinatra/base.rb, line 435 435: def halt(*response) 436: response = response.first if response.length == 1 437: throw :halt, response 438: end
Run after filters defined on the class and all superclasses.
# File lib/sinatra/base.rb, line 465 465: def after_filter!(base=self.class) 466: after_filter!(base.superclass) if base.superclass.respond_to?(:after_filters) 467: base.after_filters.each { |block| instance_eval(&block) } 468: end
Run before filters defined on the class and all superclasses.
# File lib/sinatra/base.rb, line 459 459: def before_filter!(base=self.class) 460: before_filter!(base.superclass) if base.superclass.respond_to?(:before_filters) 461: base.before_filters.each { |block| instance_eval(&block) } 462: end
Dispatch a request with error handling.
# File lib/sinatra/base.rb, line 598 598: def dispatch! 599: static! if settings.static? && (request.get? || request.head?) 600: before_filter! 601: route! 602: rescue NotFound => boom 603: handle_not_found!(boom) 604: rescue ::Exception => boom 605: handle_exception!(boom) 606: ensure 607: after_filter! unless env['sinatra.static_file'] 608: end
# File lib/sinatra/base.rb, line 650 650: def dump_errors!(boom) 651: msg = ["#{boom.class} - #{boom.message}:", 652: *boom.backtrace].join("\n ") 653: @env['rack.errors'].puts(msg) 654: end
Find an custom error block for the key(s) specified.
# File lib/sinatra/base.rb, line 635 635: def error_block!(*keys) 636: keys.each do |key| 637: base = self.class 638: while base.respond_to?(:errors) 639: if block = base.errors[key] 640: # found a handler, eval and return result 641: return instance_eval(&block) 642: else 643: base = base.superclass 644: end 645: end 646: end 647: nil 648: end
# File lib/sinatra/base.rb, line 618 618: def handle_exception!(boom) 619: @env['sinatra.error'] = boom 620: 621: dump_errors!(boom) if settings.dump_errors? 622: raise boom if settings.show_exceptions? 623: 624: @response.status = 500 625: if res = error_block!(boom.class) 626: res 627: elsif settings.raise_errors? 628: raise boom 629: else 630: error_block!(Exception) 631: end 632: end
# File lib/sinatra/base.rb, line 610 610: def handle_not_found!(boom) 611: @env['sinatra.error'] = boom 612: @response.status = 404 613: @response.headers['X-Cascade'] = 'pass' 614: @response.body = ['<h1>Not Found</h1>'] 615: error_block! boom.class, NotFound 616: end
# File lib/sinatra/base.rb, line 560 560: def indifferent_hash 561: Hash.new {|hash,key| hash[key.to_s] if Symbol === key } 562: end
Enable string or symbol key access to the nested params hash.
# File lib/sinatra/base.rb, line 552 552: def indifferent_params(params) 553: params = indifferent_hash.merge(params) 554: params.each do |key, value| 555: next unless value.is_a?(Hash) 556: params[key] = indifferent_params(value) 557: end 558: end
Run the block with ‘throw :halt’ support and apply result to the response.
# File lib/sinatra/base.rb, line 565 565: def invoke(&block) 566: res = catch(:halt) { instance_eval(&block) } 567: return if res.nil? 568: 569: case 570: when res.respond_to?(:to_str) 571: @response.body = [res] 572: when res.respond_to?(:to_ary) 573: res = res.to_ary 574: if Fixnum === res.first 575: if res.length == 3 576: @response.status, headers, body = res 577: @response.body = body if body 578: headers.each { |k, v| @response.headers[k] = v } if headers 579: elsif res.length == 2 580: @response.status = res.first 581: @response.body = res.last 582: else 583: raise TypeError, "#{res.inspect} not supported" 584: end 585: else 586: @response.body = res 587: end 588: when res.respond_to?(:each) 589: @response.body = res 590: when (100...599) === res 591: @response.status = res 592: end 593: 594: res 595: end
Run routes defined on the class and all superclasses.
# File lib/sinatra/base.rb, line 471 471: def route!(base=self.class, pass_block=nil) 472: if routes = base.routes[@request.request_method] 473: original_params = @params 474: path = unescape(@request.path_info) 475: 476: routes.each do |pattern, keys, conditions, block| 477: if match = pattern.match(path) 478: values = match.captures.to_a 479: params = 480: if keys.any? 481: keys.zip(values).inject({}) do |hash,(k,v)| 482: if k == 'splat' 483: (hash[k] ||= []) << v 484: else 485: hash[k] = v 486: end 487: hash 488: end 489: elsif values.any? 490: {'captures' => values} 491: else 492: {} 493: end 494: @params = original_params.merge(params) 495: @block_params = values 496: 497: pass_block = catch(:pass) do 498: conditions.each { |cond| 499: throw :pass if instance_eval(&cond) == false } 500: route_eval(&block) 501: end 502: end 503: end 504: 505: @params = original_params 506: end 507: 508: # Run routes defined in superclass. 509: if base.superclass.respond_to?(:routes) 510: route! base.superclass, pass_block 511: return 512: end 513: 514: route_eval(&pass_block) if pass_block 515: 516: route_missing 517: end
No matching route was found or all routes passed. The default implementation is to forward the request downstream when running as middleware (@app is non-nil); when no downstream app is set, raise a NotFound exception. Subclasses can override this method to perform custom route miss logic.
# File lib/sinatra/base.rb, line 529 529: def route_missing 530: if @app 531: forward 532: else 533: raise NotFound 534: end 535: end
Attempt to serve static files from public directory. Throws :halt when a matching file is found, returns nil otherwise.
# File lib/sinatra/base.rb, line 539 539: def static! 540: return if (public_dir = settings.public).nil? 541: public_dir = File.expand_path(public_dir) 542: 543: path = File.expand_path(public_dir + unescape(request.path_info)) 544: return if path[0, public_dir.length] != public_dir 545: return unless File.file?(path) 546: 547: env['sinatra.static_file'] = path 548: send_file path, :disposition => nil 549: end