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 |
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 962 962: def call(env) 963: synchronize { prototype.call(env) } 964: 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 1012 1012: def caller_files 1013: caller_locations. 1014: map { |file,line| file } 1015: end
# File lib/sinatra/base.rb, line 1017 1017: def caller_locations 1018: caller(1). 1019: map { |line| line.split(/:(?=\d|in )/)[0,2] }. 1020: reject { |file,line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } } 1021: end
# File lib/sinatra/base.rb, line 830 830: 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 820 820: def get(path, opts={}, &block) 821: conditions = @conditions.dup 822: route('GET', path, opts, &block) 823: 824: @conditions = conditions 825: route('HEAD', path, opts, &block) 826: end
# File lib/sinatra/base.rb, line 831 831: 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 892 892: def helpers(*extensions, &block) 893: class_eval(&block) if block_given? 894: include(*extensions) if extensions.any? 895: 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 950 950: def new(*args, &bk) 951: builder = Rack::Builder.new 952: builder.use Rack::Session::Cookie if sessions? 953: builder.use Rack::CommonLogger if logging? 954: builder.use Rack::MethodOverride if methodoverride? 955: builder.use ShowExceptions if show_exceptions? 956: middleware.each { |c,a,b| builder.use(c, *a, &b) } 957: 958: builder.run super 959: builder.to_app 960: end
# File lib/sinatra/base.rb, line 372 372: def initialize(app=nil) 373: @app = app 374: @template_cache = Tilt::Cache.new 375: yield self if block_given? 376: end
# File lib/sinatra/base.rb, line 829 829: def post(path, opts={}, &bk); route 'POST', path, opts, &bk end
# File lib/sinatra/base.rb, line 828 828: def put(path, opts={}, &bk); route 'PUT', path, opts, &bk end
# File lib/sinatra/base.rb, line 897 897: def register(*extensions, &block) 898: extensions << Module.new(&block) if block_given? 899: @extensions += extensions 900: extensions.each do |extension| 901: extend extension 902: extension.registered(self) if extension.respond_to?(:registered) 903: end 904: end
Run the Sinatra app as a self-hosted server using Thin, Mongrel or WEBrick (in that order)
# File lib/sinatra/base.rb, line 924 924: def run!(options={}) 925: set options 926: handler = detect_rack_handler 927: handler_name = handler.name.gsub(/.*::/, '') 928: puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " + 929: "on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i 930: handler.run self, :Host => host, :Port => port do |server| 931: trap(:INT) do 932: ## Use thins' hard #stop! if available, otherwise just #stop 933: server.respond_to?(:stop!) ? server.stop! : server.stop 934: puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i 935: end 936: set :running, true 937: end 938: rescue Errno::EADDRINUSE => e 939: puts "== Someone is already performing on port #{port}!" 940: end
Use the specified Rack middleware
# File lib/sinatra/base.rb, line 917 917: def use(middleware, *args, &block) 918: @prototype = nil 919: @middleware << [middleware, args, block] 920: end
# File lib/sinatra/base.rb, line 862 862: def compile(path) 863: keys = [] 864: if path.respond_to? :to_str 865: special_chars = %w{. + ( )} 866: pattern = 867: path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match| 868: case match 869: when "*" 870: keys << 'splat' 871: "(.*?)" 872: when *special_chars 873: Regexp.escape(match) 874: else 875: keys << $2[1..-1] 876: "([^/?&#]+)" 877: end 878: end 879: [/^#{pattern}$/, keys] 880: elsif path.respond_to?(:keys) && path.respond_to?(:match) 881: [path, path.keys] 882: elsif path.respond_to? :match 883: [path, keys] 884: else 885: raise TypeError, path 886: end 887: end
# File lib/sinatra/base.rb, line 967 967: def detect_rack_handler 968: servers = Array(self.server) 969: servers.each do |server_name| 970: begin 971: return Rack::Handler.get(server_name.downcase) 972: rescue LoadError 973: rescue NameError 974: end 975: end 976: fail "Server handler (#{servers.join(',')}) not found." 977: end
Extension modules registered on this class and all superclasses.
# File lib/sinatra/base.rb, line 663 663: def extensions 664: if superclass.respond_to?(:extensions) 665: (@extensions + superclass.extensions).uniq 666: else 667: @extensions 668: end 669: end
# File lib/sinatra/base.rb, line 786 786: def host_name(pattern) 787: condition { pattern === request.host } 788: end
# File lib/sinatra/base.rb, line 979 979: def inherited(subclass) 980: subclass.reset! 981: super 982: end
Load embeded templates from the file; uses the caller‘s FILE when no file is specified.
# File lib/sinatra/base.rb, line 732 732: def inline_templates=(file=nil) 733: file = (file.nil? || file == true) ? caller_files.first : file 734: 735: begin 736: app, data = 737: ::IO.read(file).gsub("\r\n", "\n").split(/^__END__$/, 2) 738: rescue Errno::ENOENT 739: app, data = nil 740: end 741: 742: if data 743: lines = app.count("\n") + 1 744: template = nil 745: data.each_line do |line| 746: lines += 1 747: if line =~ /^@@\s*(.*)/ 748: template = '' 749: templates[$1.to_sym] = [template, file, lines] 750: elsif template 751: template << line 752: end 753: end 754: end 755: end
# File lib/sinatra/base.rb, line 858 858: def invoke_hook(name, *args) 859: extensions.each { |e| e.send(name, *args) if e.respond_to?(name) } 860: end
# File lib/sinatra/base.rb, line 993 993: def metadef(message, &block) 994: (class << self; self; end). 995: send :define_method, message, &block 996: end
Middleware used in this class and all superclasses.
# File lib/sinatra/base.rb, line 672 672: def middleware 673: if superclass.respond_to?(:middleware) 674: superclass.middleware + @middleware 675: else 676: @middleware 677: end 678: end
Lookup or register a mime type in Rack‘s mime registry.
# File lib/sinatra/base.rb, line 758 758: def mime_type(type, value=nil) 759: return type if type.nil? || type.to_s.include?('/') 760: type = ".#{type}" unless type.to_s[0] == ?. 761: return Rack::Mime.mime_type(type, nil) unless value 762: Rack::Mime::MIME_TYPES[type] = value 763: end
# File lib/sinatra/base.rb, line 802 802: def provides(*types) 803: types = [types] unless types.kind_of? Array 804: types.map!{|t| mime_type(t)} 805: 806: condition { 807: matching_types = (request.accept & types) 808: unless matching_types.empty? 809: response.headers['Content-Type'] = matching_types.first 810: true 811: else 812: false 813: end 814: } 815: end
# File lib/sinatra/base.rb, line 645 645: def reset! 646: @conditions = [] 647: @routes = {} 648: @before_filters = [] 649: @after_filters = [] 650: @errors = {} 651: @middleware = [] 652: @prototype = nil 653: @extensions = [] 654: 655: if superclass.respond_to?(:templates) 656: @templates = Hash.new { |hash,key| superclass.templates[key] } 657: else 658: @templates = {} 659: end 660: end
# File lib/sinatra/base.rb, line 834 834: def route(verb, path, options={}, &block) 835: # Because of self.options.host 836: host_name(options.delete(:host)) if options.key?(:host) 837: 838: options.each {|option, args| send(option, *args)} 839: 840: pattern, keys = compile(path) 841: conditions, @conditions = @conditions, [] 842: 843: define_method "#{verb} #{path}", &block 844: unbound_method = instance_method("#{verb} #{path}") 845: block = 846: if block.arity != 0 847: proc { unbound_method.bind(self).call(*@block_params) } 848: else 849: proc { unbound_method.bind(self).call } 850: end 851: 852: invoke_hook(:route_added, verb, path, block) 853: 854: (@routes[verb] ||= []). 855: push([pattern, keys, conditions, block]).last 856: 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 682 682: def set(option, value=self) 683: if value.kind_of?(Proc) 684: metadef(option, &value) 685: metadef("#{option}?") { !!__send__(option) } 686: metadef("#{option}=") { |val| set(option, Proc.new{val}) } 687: elsif value == self && option.respond_to?(:to_hash) 688: option.to_hash.each { |k,v| set(k, v) } 689: elsif respond_to?("#{option}=") 690: __send__ "#{option}=", value 691: else 692: set option, Proc.new{value} 693: end 694: self 695: end
# File lib/sinatra/base.rb, line 985 985: def synchronize(&block) 986: if lock? 987: @@mutex.synchronize(&block) 988: else 989: yield 990: end 991: end
# File lib/sinatra/base.rb, line 790 790: def user_agent(pattern) 791: condition { 792: if request.user_agent =~ pattern 793: @params[:agent] = $~[1..-1] 794: true 795: else 796: false 797: end 798: } 799: end
# File lib/sinatra/base.rb, line 385 385: def call!(env) 386: @env = env 387: @request = Request.new(env) 388: @response = Response.new 389: @params = indifferent_params(@request.params) 390: @template_cache.clear if settings.reload_templates 391: 392: invoke { dispatch! } 393: invoke { error_block!(response.status) } 394: 395: status, header, body = @response.finish 396: 397: # Never produce a body on HEAD requests. Do retain the Content-Length 398: # unless it's "0", in which case we assume it was calculated erroneously 399: # for a manual HEAD response and remove it entirely. 400: if @env['REQUEST_METHOD'] == 'HEAD' 401: body = [] 402: header.delete('Content-Length') if header['Content-Length'] == '0' 403: end 404: 405: [status, header, body] 406: end
Forward the request to the downstream app — middleware only.
# File lib/sinatra/base.rb, line 429 429: def forward 430: fail "downstream app not set" unless @app.respond_to? :call 431: status, headers, body = @app.call(@request.env) 432: @response.status = status 433: @response.body = body 434: @response.headers.merge! headers 435: nil 436: end
Exit the current block, halts any further processing of the request, and returns the specified response.
# File lib/sinatra/base.rb, line 416 416: def halt(*response) 417: response = response.first if response.length == 1 418: throw :halt, response 419: end
Run after filters defined on the class and all superclasses.
# File lib/sinatra/base.rb, line 446 446: def after_filter!(base=self.class) 447: after_filter!(base.superclass) if base.superclass.respond_to?(:after_filters) 448: base.after_filters.each { |block| instance_eval(&block) } 449: end
Run before filters defined on the class and all superclasses.
# File lib/sinatra/base.rb, line 440 440: def before_filter!(base=self.class) 441: before_filter!(base.superclass) if base.superclass.respond_to?(:before_filters) 442: base.before_filters.each { |block| instance_eval(&block) } 443: end
# File lib/sinatra/base.rb, line 633 633: def clean_backtrace(trace) 634: return trace unless settings.clean_trace? 635: 636: trace.reject { |line| 637: line =~ /lib\/sinatra.*\.rb/ || 638: (defined?(Gem) && line.include?(Gem.dir)) 639: }.map! { |line| line.gsub(/^\.\//, '') } 640: end
Dispatch a request with error handling.
# File lib/sinatra/base.rb, line 579 579: def dispatch! 580: static! if settings.static? && (request.get? || request.head?) 581: before_filter! 582: route! 583: rescue NotFound => boom 584: handle_not_found!(boom) 585: rescue ::Exception => boom 586: handle_exception!(boom) 587: ensure 588: after_filter! unless env['sinatra.static_file'] 589: end
# File lib/sinatra/base.rb, line 626 626: def dump_errors!(boom) 627: backtrace = clean_backtrace(boom.backtrace) 628: msg = ["#{boom.class} - #{boom.message}:", 629: *backtrace].join("\n ") 630: @env['rack.errors'].puts(msg) 631: end
Find an custom error block for the key(s) specified.
# File lib/sinatra/base.rb, line 610 610: def error_block!(*keys) 611: keys.each do |key| 612: base = self.class 613: while base.respond_to?(:errors) 614: if block = base.errors[key] 615: # found a handler, eval and return result 616: res = instance_eval(&block) 617: return res 618: else 619: base = base.superclass 620: end 621: end 622: end 623: nil 624: end
# File lib/sinatra/base.rb, line 599 599: def handle_exception!(boom) 600: @env['sinatra.error'] = boom 601: 602: dump_errors!(boom) if settings.dump_errors? 603: raise boom if settings.raise_errors? || settings.show_exceptions? 604: 605: @response.status = 500 606: error_block! boom.class, Exception 607: end
# File lib/sinatra/base.rb, line 591 591: def handle_not_found!(boom) 592: @env['sinatra.error'] = boom 593: @response.status = 404 594: @response.headers['X-Cascade'] = 'pass' 595: @response.body = ['<h1>Not Found</h1>'] 596: error_block! boom.class, NotFound 597: end
# File lib/sinatra/base.rb, line 541 541: def indifferent_hash 542: Hash.new {|hash,key| hash[key.to_s] if Symbol === key } 543: end
Enable string or symbol key access to the nested params hash.
# File lib/sinatra/base.rb, line 533 533: def indifferent_params(params) 534: params = indifferent_hash.merge(params) 535: params.each do |key, value| 536: next unless value.is_a?(Hash) 537: params[key] = indifferent_params(value) 538: end 539: end
Run the block with ‘throw :halt’ support and apply result to the response.
# File lib/sinatra/base.rb, line 546 546: def invoke(&block) 547: res = catch(:halt) { instance_eval(&block) } 548: return if res.nil? 549: 550: case 551: when res.respond_to?(:to_str) 552: @response.body = [res] 553: when res.respond_to?(:to_ary) 554: res = res.to_ary 555: if Fixnum === res.first 556: if res.length == 3 557: @response.status, headers, body = res 558: @response.body = body if body 559: headers.each { |k, v| @response.headers[k] = v } if headers 560: elsif res.length == 2 561: @response.status = res.first 562: @response.body = res.last 563: else 564: raise TypeError, "#{res.inspect} not supported" 565: end 566: else 567: @response.body = res 568: end 569: when res.respond_to?(:each) 570: @response.body = res 571: when (100...599) === res 572: @response.status = res 573: end 574: 575: res 576: end
Run routes defined on the class and all superclasses.
# File lib/sinatra/base.rb, line 452 452: def route!(base=self.class, pass_block=nil) 453: if routes = base.routes[@request.request_method] 454: original_params = @params 455: path = unescape(@request.path_info) 456: 457: routes.each do |pattern, keys, conditions, block| 458: if match = pattern.match(path) 459: values = match.captures.to_a 460: params = 461: if keys.any? 462: keys.zip(values).inject({}) do |hash,(k,v)| 463: if k == 'splat' 464: (hash[k] ||= []) << v 465: else 466: hash[k] = v 467: end 468: hash 469: end 470: elsif values.any? 471: {'captures' => values} 472: else 473: {} 474: end 475: @params = original_params.merge(params) 476: @block_params = values 477: 478: pass_block = catch(:pass) do 479: conditions.each { |cond| 480: throw :pass if instance_eval(&cond) == false } 481: route_eval(&block) 482: end 483: end 484: end 485: 486: @params = original_params 487: end 488: 489: # Run routes defined in superclass. 490: if base.superclass.respond_to?(:routes) 491: route! base.superclass, pass_block 492: return 493: end 494: 495: route_eval(&pass_block) if pass_block 496: 497: route_missing 498: 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 510 510: def route_missing 511: if @app 512: forward 513: else 514: raise NotFound 515: end 516: 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 520 520: def static! 521: return if (public_dir = settings.public).nil? 522: public_dir = File.expand_path(public_dir) 523: 524: path = File.expand_path(public_dir + unescape(request.path_info)) 525: return if path[0, public_dir.length] != public_dir 526: return unless File.file?(path) 527: 528: env['sinatra.static_file'] = path 529: send_file path, :disposition => nil 530: end