class Sinatra::Base
def accept_mime_types(types)
def accept_mime_types(types) types = [types] unless types.kind_of? Array types.map!{|t| media_type(t)} condition { matching_types = (request.accept & types) unless matching_types.empty? response.headers['Content-Type'] = matching_types.first true else false end } end
def before(&block)
def before(&block) @filters << block end
def call(env)
def call(env) dup.call!(env) end
def call(env)
def call(env) construct_middleware if @callsite.nil? @callsite.call(env) end
def call!(env)
def call!(env) @env = env @request = Request.new(env) @response = Response.new @params = nil error_detection { dispatch! } @response.finish end
def compile(path)
def compile(path) keys = [] if path.respond_to? :to_str pattern = URI.encode(path).gsub(/((:\w+)|\*)/) do |match| if match == "*" keys << 'splat' "(.*?)" else keys << $2[1..-1] "([^/?&#\.]+)" end end [/^#{pattern}$/, keys] elsif path.respond_to? :=~ [path, keys] else raise TypeError, path end end
def condition(&block)
def condition(&block) @conditions << block end
def configure(*envs, &block)
def configure(*envs, &block) yield if envs.empty? || envs.include?(environment.to_sym) end
def construct_middleware(builder=Rack::Builder.new)
def construct_middleware(builder=Rack::Builder.new) builder.use Rack::Session::Cookie if sessions? builder.use Rack::CommonLogger if logging? builder.use Rack::MethodOverride if methodoverride? @middleware.each { |c, args, bk| builder.use(c, *args, &bk) } builder.run new @callsite = builder.to_app end
def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk; end
def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk; end
def development? ; environment == :development ; end
def development? ; environment == :development ; end
def disable(*opts)
def disable(*opts) opts.each { |key| set(key, false) } end
def dispatch!
def dispatch! self.class.filters.each do |block| res = catch(:halt) { instance_eval(&block) ; :continue } return unless res == :continue end if routes = self.class.routes[@request.request_method] path = @request.path_info original_params = nested_params(@request.params) routes.each do |pattern, keys, conditions, method_name| if pattern =~ path values = $~.captures.map{|val| val && unescape(val) } params = if keys.any? keys.zip(values).inject({}) do |hash,(k,v)| if k == 'splat' (hash[k] ||= []) << v else hash[k] = v end hash end elsif values.any? {'captures' => values} else {} end @params = original_params.merge(params) catch(:pass) { conditions.each { |cond| throw :pass if instance_eval(&cond) == false } return invoke(method_name) } end end end raise NotFound end
def dupe_routes
def dupe_routes routes.inject({}) do |hash,(request_method,routes)| hash[request_method] = routes.dup hash end end
def enable(*opts)
def enable(*opts) opts.each { |key| set(key, true) } end
def error(codes=Exception, &block)
def error(codes=Exception, &block) if codes.respond_to? :each codes.each { |err| error(err, &block) } else @errors[codes] = block end end
def error_detection
def error_detection errmap = self.class.errors yield rescue NotFound => boom @env['sinatra.error'] = boom @response.status = 404 @response.body = ['<h1>Not Found</h1>'] handler = errmap[boom.class] || errmap[NotFound] invoke handler unless handler.nil? rescue ::Exception => boom @env['sinatra.error'] = boom if options.dump_errors? msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ") @env['rack.errors'] << msg end raise boom if options.raise_errors? @response.status = 500 invoke errmap[boom.class] || errmap[Exception] ensure if @response.status >= 400 && errmap.key?(response.status) invoke errmap[response.status] end end
def get(path, opts={}, &block)
def get(path, opts={}, &block) conditions = @conditions.dup route('GET', path, opts, &block) @conditions = conditions head(path, opts) { invoke(block) ; [] } end
def halt(*response)
def halt(*response) throw :halt, *response end
def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk; end
def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk; end
def host_name(pattern)
def host_name(pattern) condition { pattern === request.host } end
def indifferent_hash
def indifferent_hash Hash.new {|hash,key| hash[key.to_s] if Symbol === key } end
def inherited(subclass)
def inherited(subclass) subclass.routes = dupe_routes subclass.templates = templates.dup subclass.conditions = [] subclass.filters = filters.dup subclass.errors = errors.dup subclass.middleware = middleware.dup subclass.send :reset_middleware super end
def initialize(app=nil)
def initialize(app=nil) @app = app yield self if block_given? end
def invoke(block)
def invoke(block) res = catch(:halt) { instance_eval(&block) } return if res.nil? case when res.respond_to?(:to_str) @response.body = [res] when res.respond_to?(:to_ary) res = res.to_ary if Fixnum === res.first if res.length == 3 @response.status, headers, body = res @response.body = body if body headers.each { |k, v| @response.headers[k] = v } if headers elsif res.length == 2 @response.status = res.first @response.body = res.last else raise TypeError, "#{res.inspect} not supported" end else @response.body = res end when res.respond_to?(:each) @response.body = res when (100...599) === res @response.status = res end res end
def layout(name=:layout, &block)
def layout(name=:layout, &block) template name, &block end
def media_type(type)
def media_type(type) return type if type.nil? || type.to_s.include?('/') type = ".#{type}" unless type.to_s[0] == ?. Rack::Mime.mime_type(type, nil) end
def metadef(message, &block)
def metadef(message, &block) (class << self; self; end). send :define_method, message, &block end
def nested_params(params)
def nested_params(params) return indifferent_hash.merge(params) if !params.keys.join.include?('[') params.inject indifferent_hash do |res, (key,val)| if key =~ /\[.*\]/ splat = key.scan(/(^[^\[]+)|\[([^\]]+)\]/).flatten.compact head, last = splat[0..-2], splat[-1] head.inject(res){ |s,v| s[v] ||= indifferent_hash }[last] = val end res end end
def not_found(&block)
def not_found(&block) error 404, &block end
def options
def options self.class end
def pass
def pass throw :pass end
def post(path, opts={}, &bk); route 'POST', path, opts, &bk; end
def post(path, opts={}, &bk); route 'POST', path, opts, &bk; end
def production? ; environment == :production ; end
def production? ; environment == :production ; end
def put(path, opts={}, &bk); route 'PUT', path, opts, &bk; end
def put(path, opts={}, &bk); route 'PUT', path, opts, &bk; end
def reset_middleware
def reset_middleware @callsite = nil end
def route(verb, path, opts={}, &block)
def route(verb, path, opts={}, &block) host_name opts[:host] if opts.key?(:host) user_agent opts[:agent] if opts.key?(:agent) accept_mime_types opts[:provides] if opts.key?(:provides) pattern, keys = compile(path) conditions, @conditions = @conditions, [] define_method "#{verb} #{path}", &block unbound_method = instance_method("#{verb} #{path}") block = lambda { unbound_method.bind(self).call } (routes[verb] ||= []). push([pattern, keys, conditions, block]).last end
def run!(options={})
def run!(options={}) set(options) handler = Rack::Handler.get(server) handler_name = handler.name.gsub(/.*::/, '') puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " + "on #{port} for #{environment} with backup from #{handler_name}" handler.run self, :Host => host, :Port => port do |server| trap(:INT) do ## Use thins' hard #stop! if available, otherwise just #stop server.respond_to?(:stop!) ? server.stop! : server.stop puts "\n== Sinatra has ended his set (crowd applauds)" end end rescue Errno::EADDRINUSE => e puts "== Someone is already performing on port #{port}!" end
def set(option, value=self)
def set(option, value=self) if value.kind_of?(Proc) metadef(option, &value) metadef("#{option}?") { !!__send__(option) } metadef("#{option}=") { |val| set(option, Proc.new{val}) } elsif value == self && option.respond_to?(:to_hash) option.to_hash.each(&method(:set)) elsif respond_to?("#{option}=") __send__ "#{option}=", value else set option, Proc.new{value} end self end
def template(name, &block)
def template(name, &block) templates[name] = block end
def test? ; environment == :test ; end
def test? ; environment == :test ; end
def use(middleware, *args, &block)
def use(middleware, *args, &block) reset_middleware @middleware << [middleware, args, block] end
def use_in_file_templates!
def use_in_file_templates! line = caller.detect do |s| [ /lib\/sinatra.*\.rb/, /\(.*\)/, /rubygems\/custom_require\.rb/ ].all? { |x| s !~ x } end file = line.sub(/:\d+.*$/, '') if data = ::IO.read(file).split('__END__')[1] data.gsub!(/\r\n/, "\n") template = nil data.each_line do |line| if line =~ /^@@\s*(.*)/ template = templates[$1.to_sym] = '' elsif template template << line end end end end
def user_agent(pattern)
def user_agent(pattern) condition { if request.user_agent =~ pattern @params[:agent] = $~[1..-1] true else false end } end