add pattern matching to before/after filters.

Filter now optionally take a pattern, causing them to be evaluated only if the request
path matches that pattern:

  before("/protected/*") { authenticate! }
  after("/create/:slug") { |slug| session[:last_slug] = slug }

Signed-off-by: Simon Rozet <simon@rozet.name>
This commit is contained in:
Konstantin Haase 2010-04-27 23:11:43 +02:00 committed by Simon Rozet
parent d3a401e66c
commit da047d3d4c
3 changed files with 96 additions and 22 deletions

View File

@ -331,6 +331,17 @@ set in before filters and routes are accessible by after filters:
puts response.status
end
Filters optionally taking a pattern, causing them to be evaluated only if the request
path matches that pattern:
before '/protected/*' do
authenticate!
end
after '/create/:slug' do |slug|
session[:last_slug] = slug
end
== Halting
To immediately stop a request within a filter or route use:

View File

@ -455,16 +455,10 @@ module Sinatra
end
private
# Run before filters defined on the class and all superclasses.
def before_filter!(base=self.class)
before_filter!(base.superclass) if base.superclass.respond_to?(:before_filters)
base.before_filters.each { |block| instance_eval(&block) }
end
# Run after filters defined on the class and all superclasses.
def after_filter!(base=self.class)
after_filter!(base.superclass) if base.superclass.respond_to?(:after_filters)
base.after_filters.each { |block| instance_eval(&block) }
# Run filters defined on the class and all superclasses.
def filter!(type, base = self.class)
filter! type, base.superclass if base.superclass.respond_to?(:filters)
base.filters[type].each { |block| instance_eval(&block) }
end
# Run routes defined on the class and all superclasses.
@ -597,14 +591,14 @@ module Sinatra
# Dispatch a request with error handling.
def dispatch!
static! if settings.static? && (request.get? || request.head?)
before_filter!
filter! :before
route!
rescue NotFound => boom
handle_not_found!(boom)
rescue ::Exception => boom
handle_exception!(boom)
ensure
after_filter! unless env['sinatra.static_file']
filter! :after unless env['sinatra.static_file']
end
def handle_not_found!(boom)
@ -654,13 +648,12 @@ module Sinatra
end
class << self
attr_reader :routes, :before_filters, :after_filters, :templates, :errors
attr_reader :routes, :filters, :templates, :errors
def reset!
@conditions = []
@routes = {}
@before_filters = []
@after_filters = []
@filters = {:before => [], :after => []}
@errors = {}
@middleware = []
@prototype = nil
@ -673,6 +666,14 @@ module Sinatra
end
end
def before_filters
@filters[:before]
end
def after_filters
@filters[:after]
end
# Extension modules registered on this class and all superclasses.
def extensions
if superclass.respond_to?(:extensions)
@ -781,15 +782,25 @@ module Sinatra
# Define a before filter; runs before all requests within the same
# context as route handlers and may access/modify the request and
# response.
def before(&block)
@before_filters << block
def before(path = nil, &block)
add_filter(:before, path, &block)
end
# Define an after filter; runs after all requests within the same
# context as route handlers and may access/modify the request and
# response.
def after(&block)
@after_filters << block
def after(path = nil, &block)
add_filter(:after, path, &block)
end
# add a filter
def add_filter(type, path = nil, &block)
return filters[type] << block unless path
unbound_method, pattern = compile!(type, path, &block)
add_filter(type) do
next unless match = pattern.match(request.path_info)
unbound_method.bind(self).call(*match.captures.to_a)
end
end
# Add a route condition. The route is considered non-matching when the
@ -853,11 +864,9 @@ module Sinatra
options.each {|option, args| send(option, *args)}
pattern, keys = compile(path)
unbound_method, pattern, keys = compile!(verb, path, &block)
conditions, @conditions = @conditions, []
define_method "#{verb} #{path}", &block
unbound_method = instance_method("#{verb} #{path}")
block =
if block.arity != 0
proc { unbound_method.bind(self).call(*@block_params) }
@ -875,6 +884,14 @@ module Sinatra
extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
end
def compile!(verb, path, &block)
method_name = "#{verb} #{path}"
define_method(method_name, &block)
unbound_method = instance_method method_name
remove_method method_name
[unbound_method, *compile(path)]
end
def compile(path)
keys = []
if path.respond_to? :to_str

View File

@ -121,6 +121,29 @@ class BeforeFilterTest < Test::Unit::TestCase
assert_equal File.read(__FILE__), body
assert !ran_filter
end
it 'takes an optional route pattern' do
ran_filter = false
mock_app do
before("/b*") { ran_filter = true }
get('/foo') { }
get('/bar') { }
end
get '/foo'
assert !ran_filter
get '/bar'
assert ran_filter
end
it 'generates block arguments from route pattern' do
subpath = nil
mock_app do
before("/foo/:sub") { |s| subpath = s }
get('/foo/*') { }
end
get '/foo/bar'
assert_equal subpath, 'bar'
end
end
class AfterFilterTest < Test::Unit::TestCase
@ -218,4 +241,27 @@ class AfterFilterTest < Test::Unit::TestCase
assert_equal File.read(__FILE__), body
assert !ran_filter
end
it 'takes an optional route pattern' do
ran_filter = false
mock_app do
after("/b*") { ran_filter = true }
get('/foo') { }
get('/bar') { }
end
get '/foo'
assert !ran_filter
get '/bar'
assert ran_filter
end
it 'generates block arguments from route pattern' do
subpath = nil
mock_app do
after("/foo/:sub") { |s| subpath = s }
get('/foo/*') { }
end
get '/foo/bar'
assert_equal subpath, 'bar'
end
end