From 739b6ba01244ed56d14fbd00ce5e85dcbb786b1c Mon Sep 17 00:00:00 2001 From: Konstantin Haase Date: Fri, 3 Sep 2010 23:19:20 +0200 Subject: [PATCH] make URL params available in filters with pattern. Previously this was the only way to access parameters from pattern: before /:name do |name| ... end Now it is also possible to use params: before /:name do do_something params[:name] end --- lib/sinatra/base.rb | 100 ++++++++++++++++++++++++-------------------- test/filter_test.rb | 13 ++++++ 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index 272dbe04..76b47c63 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -494,51 +494,19 @@ module Sinatra # Run routes defined on the class and all superclasses. def route!(base=self.class, pass_block=nil) if routes = base.routes[@request.request_method] - original_params = @params - - path = unescape(@request.path_info) - path = "/" if path.empty? - routes.each do |pattern, keys, conditions, block| - if match = pattern.match(path) - values = match.captures.to_a - 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) - @block_params = values - - pass_block = catch(:pass) do - conditions.each { |cond| - throw :pass if instance_eval(&cond) == false } - route_eval(&block) - end + pass_block = process_route(pattern, keys, conditions) do + route_eval(&block) end end - - @params = original_params end # Run routes defined in superclass. if base.superclass.respond_to?(:routes) - route! base.superclass, pass_block - return + return route!(base.superclass, pass_block) end route_eval(&pass_block) if pass_block - route_missing end @@ -547,6 +515,46 @@ module Sinatra throw :halt, instance_eval(&block) end + # If the current request matches pattern and conditions, fill params + # with keys and call the given block. + # Revert params afterwards. + # + # Returns pass block. + def process_route(pattern, keys, conditions) + @original_params ||= @params + @path ||= begin + path = unescape(@request.path_info) + path.empty? ? "/" : path + end + if match = pattern.match(@path) + values = match.captures.to_a + 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) + @block_params = values + catch(:pass) do + conditions.each { |cond| + throw :pass if instance_eval(&cond) == false } + yield + end + end + ensure + @params = @original_params + 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 @@ -828,11 +836,9 @@ module Sinatra # add a filter def add_filter(type, path = nil, &block) return filters[type] << block unless path - block, pattern = compile!(type, path, block) + block, *arguments = compile!(type, path, block) add_filter(type) do - next unless match = pattern.match(request.path_info) - @block_params = match.captures.to_a - instance_eval(&block) + process_route(*arguments) { instance_eval(&block) } end end @@ -896,8 +902,7 @@ module Sinatra host_name(options.delete(:host)) if options.key?(:host) options.each { |option, args| send(option, *args) } - block, pattern, keys = compile! verb, path, block - conditions, @conditions = @conditions, [] + block, pattern, keys, conditions = compile! verb, path, block invoke_hook(:route_added, verb, path, block) (@routes[verb] ||= []). @@ -910,12 +915,17 @@ module Sinatra def compile!(verb, path, block) method_name = "#{verb} #{path}" + define_method(method_name, &block) - unbound_method = instance_method method_name + unbound_method = instance_method method_name + pattern, keys = compile(path) + conditions, @conditions = @conditions, [] remove_method method_name - [block.arity != 0 ? - proc { unbound_method.bind(self).call(*@block_params) } : - proc { unbound_method.bind(self).call }, *compile(path)] + + [ block.arity != 0 ? + proc { unbound_method.bind(self).call(*@block_params) } : + proc { unbound_method.bind(self).call }, + pattern, keys, conditions ] end def compile(path) diff --git a/test/filter_test.rb b/test/filter_test.rb index 11a3d8e4..d9adb10e 100644 --- a/test/filter_test.rb +++ b/test/filter_test.rb @@ -264,4 +264,17 @@ class AfterFilterTest < Test::Unit::TestCase get '/foo/bar' assert_equal subpath, 'bar' end + + it 'is possible to access url params from the route param' do + ran = false + mock_app do + get('/foo/*') { } + before('/foo/:sub') do + assert_equal params[:sub], 'bar' + ran = true + end + end + get '/foo/bar' + assert ran + end end