lib/sinatra/base.rb in bmizerany-sinatra-0.9.0 vs lib/sinatra/base.rb in bmizerany-sinatra-0.9.0.2

- old
+ new

@@ -2,20 +2,27 @@ require 'uri' require 'rack' require 'rack/builder' module Sinatra - VERSION = '0.9.0' + VERSION = '0.9.0.2' class Request < Rack::Request def user_agent @env['HTTP_USER_AGENT'] end def accept @env['HTTP_ACCEPT'].split(',').map { |a| a.strip } end + + # Override Rack 0.9.x's #params implementation (see #72 in lighthouse) + def params + self.GET.update(self.POST) + rescue EOFError => boom + self.GET + end end class Response < Rack::Response def initialize @status, @body = 200, [] @@ -68,11 +75,11 @@ # Halt processing and redirect to the URI provided. def redirect(uri, *args) status 302 response['Location'] = uri - halt *args + halt(*args) end # Halt processing and return the error status provided. def error(code, body=nil) code, body = 500, code.to_str if code.respond_to? :to_str @@ -133,11 +140,11 @@ end class StaticFile < ::File #:nodoc: alias_method :to_path, :path def each - while buf = read(8196) + while buf = read(8192) yield buf end end end @@ -208,10 +215,11 @@ end end def lookup_layout(engine, options) return if options[:layout] == false + options.delete(:layout) if options[:layout] == true template = options[:layout] || :layout data = lookup_template(engine, template, options) [template, data] rescue Errno::ENOENT nil @@ -296,14 +304,14 @@ end attr_accessor :env, :request, :response, :params def call!(env) - @env = env - @request = Request.new(env) + @env = env + @request = Request.new(env) @response = Response.new - @params = nil + @params = nil error_detection { dispatch! } @response.finish end def options @@ -318,17 +326,20 @@ throw :pass end private def dispatch! - self.class.filters.each {|block| instance_eval(&block)} + 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 = Hash.new{ |hash,k| hash[k.to_s] if Symbol === k } - original_params.merge! @request.params + original_params = nested_params(@request.params) - routes.each do |pattern, keys, conditions, block| + 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)| @@ -342,26 +353,43 @@ elsif values.any? {'captures' => values} else {} end - @params = original_params.dup - @params.merge!(params) + @params = original_params.merge(params) catch(:pass) { conditions.each { |cond| throw :pass if instance_eval(&cond) == false } - return invoke(block) + return invoke(method_name) } end end end raise NotFound end + 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 indifferent_hash + Hash.new {|hash,key| hash[key.to_s] if Symbol === key } + end + 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 @@ -377,21 +405,16 @@ raise TypeError, "#{res.inspect} not supported" end else @response.body = res end - when res.kind_of?(Symbol) # TODO: deprecate this. - @response.body = __send__(res) when res.respond_to?(:each) @response.body = res when (100...599) === res @response.status = res - when res.nil? - @response.body = [] - else - raise TypeError, "#{res.inspect} not supported" end + res end def error_detection errmap = self.class.errors @@ -402,10 +425,16 @@ @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) @@ -454,24 +483,33 @@ else @errors[codes] = block end end + def not_found(&block) + error 404, &block + end + def template(name, &block) templates[name] = block end def layout(name=:layout, &block) template name, &block end def use_in_file_templates! - line = caller.detect { |s| s !~ /lib\/sinatra.*\.rb/ && - s !~ /\(.*\)/ } + 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" + data.gsub!(/\r\n/, "\n") template = nil data.each_line do |line| if line =~ /^@@\s*(.*)/ template = templates[$1.to_sym] = '' elsif template @@ -526,11 +564,11 @@ } end def get(path, opts={}, &block) conditions = @conditions.dup - route 'GET', path, opts, &block + route('GET', path, opts, &block) @conditions = conditions head(path, opts) { invoke(block) ; [] } end @@ -538,19 +576,24 @@ def post(path, opts={}, &bk); route 'POST', path, opts, &bk; end def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk; end def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk; end private - def route(method, 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, [] - (routes[method] ||= []). - push [pattern, keys, conditions, block] + + 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 compile(path) keys = [] if path.respond_to? :to_str @@ -645,10 +688,11 @@ send :define_method, message, &block end end set :raise_errors, true + set :dump_errors, false set :sessions, false set :logging, false set :methodoverride, false set :static, false set :environment, (ENV['RACK_ENV'] || :development).to_sym @@ -738,10 +782,11 @@ end end class Default < Base set :raise_errors, false + set :dump_errors, true set :sessions, false set :logging, true set :methodoverride, true set :static, true set :run, false @@ -761,9 +806,10 @@ end def self.reload! @reloading = true superclass.send :inherited, self + $LOADED_FEATURES.delete("sinatra.rb") ::Kernel.load app_file @reloading = false end end