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:
parent
d3a401e66c
commit
da047d3d4c
11
README.rdoc
11
README.rdoc
|
@ -331,6 +331,17 @@ set in before filters and routes are accessible by after filters:
|
||||||
puts response.status
|
puts response.status
|
||||||
end
|
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
|
== Halting
|
||||||
|
|
||||||
To immediately stop a request within a filter or route use:
|
To immediately stop a request within a filter or route use:
|
||||||
|
|
|
@ -455,16 +455,10 @@ module Sinatra
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
# Run before filters defined on the class and all superclasses.
|
# Run filters defined on the class and all superclasses.
|
||||||
def before_filter!(base=self.class)
|
def filter!(type, base = self.class)
|
||||||
before_filter!(base.superclass) if base.superclass.respond_to?(:before_filters)
|
filter! type, base.superclass if base.superclass.respond_to?(:filters)
|
||||||
base.before_filters.each { |block| instance_eval(&block) }
|
base.filters[type].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) }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Run routes defined on the class and all superclasses.
|
# Run routes defined on the class and all superclasses.
|
||||||
|
@ -597,14 +591,14 @@ module Sinatra
|
||||||
# Dispatch a request with error handling.
|
# Dispatch a request with error handling.
|
||||||
def dispatch!
|
def dispatch!
|
||||||
static! if settings.static? && (request.get? || request.head?)
|
static! if settings.static? && (request.get? || request.head?)
|
||||||
before_filter!
|
filter! :before
|
||||||
route!
|
route!
|
||||||
rescue NotFound => boom
|
rescue NotFound => boom
|
||||||
handle_not_found!(boom)
|
handle_not_found!(boom)
|
||||||
rescue ::Exception => boom
|
rescue ::Exception => boom
|
||||||
handle_exception!(boom)
|
handle_exception!(boom)
|
||||||
ensure
|
ensure
|
||||||
after_filter! unless env['sinatra.static_file']
|
filter! :after unless env['sinatra.static_file']
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_not_found!(boom)
|
def handle_not_found!(boom)
|
||||||
|
@ -654,13 +648,12 @@ module Sinatra
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
attr_reader :routes, :before_filters, :after_filters, :templates, :errors
|
attr_reader :routes, :filters, :templates, :errors
|
||||||
|
|
||||||
def reset!
|
def reset!
|
||||||
@conditions = []
|
@conditions = []
|
||||||
@routes = {}
|
@routes = {}
|
||||||
@before_filters = []
|
@filters = {:before => [], :after => []}
|
||||||
@after_filters = []
|
|
||||||
@errors = {}
|
@errors = {}
|
||||||
@middleware = []
|
@middleware = []
|
||||||
@prototype = nil
|
@prototype = nil
|
||||||
|
@ -673,6 +666,14 @@ module Sinatra
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def before_filters
|
||||||
|
@filters[:before]
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_filters
|
||||||
|
@filters[:after]
|
||||||
|
end
|
||||||
|
|
||||||
# Extension modules registered on this class and all superclasses.
|
# Extension modules registered on this class and all superclasses.
|
||||||
def extensions
|
def extensions
|
||||||
if superclass.respond_to?(:extensions)
|
if superclass.respond_to?(:extensions)
|
||||||
|
@ -781,15 +782,25 @@ module Sinatra
|
||||||
# Define a before filter; runs before all requests within the same
|
# Define a before filter; runs before all requests within the same
|
||||||
# context as route handlers and may access/modify the request and
|
# context as route handlers and may access/modify the request and
|
||||||
# response.
|
# response.
|
||||||
def before(&block)
|
def before(path = nil, &block)
|
||||||
@before_filters << block
|
add_filter(:before, path, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define an after filter; runs after all requests within the same
|
# Define an after filter; runs after all requests within the same
|
||||||
# context as route handlers and may access/modify the request and
|
# context as route handlers and may access/modify the request and
|
||||||
# response.
|
# response.
|
||||||
def after(&block)
|
def after(path = nil, &block)
|
||||||
@after_filters << 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
|
end
|
||||||
|
|
||||||
# Add a route condition. The route is considered non-matching when the
|
# 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)}
|
options.each {|option, args| send(option, *args)}
|
||||||
|
|
||||||
pattern, keys = compile(path)
|
unbound_method, pattern, keys = compile!(verb, path, &block)
|
||||||
conditions, @conditions = @conditions, []
|
conditions, @conditions = @conditions, []
|
||||||
|
|
||||||
define_method "#{verb} #{path}", &block
|
|
||||||
unbound_method = instance_method("#{verb} #{path}")
|
|
||||||
block =
|
block =
|
||||||
if block.arity != 0
|
if block.arity != 0
|
||||||
proc { unbound_method.bind(self).call(*@block_params) }
|
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) }
|
extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
|
||||||
end
|
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)
|
def compile(path)
|
||||||
keys = []
|
keys = []
|
||||||
if path.respond_to? :to_str
|
if path.respond_to? :to_str
|
||||||
|
|
|
@ -121,6 +121,29 @@ class BeforeFilterTest < Test::Unit::TestCase
|
||||||
assert_equal File.read(__FILE__), body
|
assert_equal File.read(__FILE__), body
|
||||||
assert !ran_filter
|
assert !ran_filter
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
class AfterFilterTest < Test::Unit::TestCase
|
class AfterFilterTest < Test::Unit::TestCase
|
||||||
|
@ -218,4 +241,27 @@ class AfterFilterTest < Test::Unit::TestCase
|
||||||
assert_equal File.read(__FILE__), body
|
assert_equal File.read(__FILE__), body
|
||||||
assert !ran_filter
|
assert !ran_filter
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in New Issue