Skip to content

Commit

Permalink
make URL params available in filters with pattern.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
rkh committed Sep 3, 2010
1 parent 2233cdb commit 739b6ba
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 45 deletions.
100 changes: 55 additions & 45 deletions lib/sinatra/base.rb
Expand Up @@ -494,51 +494,19 @@ def filter!(type, base = self.class)
# 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

Expand All @@ -547,6 +515,46 @@ def route_eval(&block)
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
Expand Down Expand Up @@ -828,11 +836,9 @@ def after(path = nil, &block)
# 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

Expand Down Expand Up @@ -896,8 +902,7 @@ def route(verb, path, options={}, &block)
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] ||= []).
Expand All @@ -910,12 +915,17 @@ def invoke_hook(name, *args)

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)
Expand Down
13 changes: 13 additions & 0 deletions test/filter_test.rb
Expand Up @@ -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

0 comments on commit 739b6ba

Please sign in to comment.