Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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") { session[:last_slug] = slug }
  • Loading branch information...
commit 7575b96953db81b2fdffe28f7a16e3cf48c91066 1 parent d3a401e
Konstantin Haase rkh authored
Showing with 96 additions and 22 deletions.
  1. +11 −0 README.rdoc
  2. +39 −22 lib/sinatra/base.rb
  3. +46 −0 test/filter_test.rb
11 README.rdoc
View
@@ -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:
61 lib/sinatra/base.rb
View
@@ -455,16 +455,10 @@ def forward
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 @@ def invoke(&block)
# 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 @@ def dump_errors!(boom)
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 @@ def reset!
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 @@ def mime_type(type, value=nil)
# 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 @@ def route(verb, path, options={}, &block)
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 @@ def invoke_hook(name, *args)
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
46 test/filter_test.rb
View
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.