Module: ActionController::Filters::ClassMethods
- Defined in:
- lib/action_controller/filters.rb
Overview
Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do authentication, caching, or auditing before the intended action is performed. Or to do localization or output compression after the action has been performed.
Filters have access to the request, response, and all the instance variables set by other filters in the chain or by the action (in the case of after filters). Additionally, it’s possible for a pre-processing before_filter
to halt the processing before the intended action is processed by returning false or performing a redirect or render. This is especially useful for filters like authentication where you’re not interested in allowing the action to be performed if the proper credentials are not in order.
Filter inheritance
Controller inheritance hierarchies share filters downwards, but subclasses can also add new filters without affecting the superclass. For example:
class BankController < ActionController::Base
before_filter :audit
private
def audit
# record the action and parameters in an audit log
end
end
class VaultController < BankController
before_filter :verify_credentials
private
def verify_credentials
# make sure the user is allowed into the vault
end
end
Now any actions performed on the BankController will have the audit method called before. On the VaultController, first the audit method is called, then the verify_credentials method. If the audit method returns false, then verify_credentials and the intended action are never called.
Filter types
A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form.
Using an external class makes for more easily reused generic filters, such as output compression. External filter classes are implemented by having a static filter
method on any class and then passing this class to the filter method. Example:
class OutputCompressionFilter
def self.filter(controller)
controller.response.body = compress(controller.response.body)
end
end
class NewspaperController < ActionController::Base
after_filter OutputCompressionFilter
end
The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can manipulate them as it sees fit.
The inline method (using a proc) can be used to quickly do something small that doesn’t require a lot of explanation. Or just as a quick test. It works like this:
class WeblogController < ActionController::Base
before_filter { |controller| false if controller.params["stop_action"] }
end
As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables. This means that the block has access to both the request and response objects complete with convenience methods for params, session, template, and assigns. Note: The inline method doesn’t strictly have to be a block; any object that responds to call and returns 1 or -1 on arity will do (such as a Proc or an Method object).
Filter chain ordering
Using before_filter
and after_filter
appends the specified filters to the existing chain. That’s usually just fine, but some times you care more about the order in which the filters are executed. When that’s the case, you can use prepend_before_filter
and prepend_after_filter
. Filters added by these methods will be put at the beginning of their respective chain and executed before the rest. For example:
class ShoppingController
before_filter :verify_open_shop
class CheckoutController
prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
The filter chain for the CheckoutController is now :ensure_items_in_cart, :ensure_items_in_stock,
:verify_open_shop
. So if either of the ensure filters return false, we’ll never get around to see if the shop is open or not.
You may pass multiple filter arguments of each type as well as a filter block. If a block is given, it is treated as the last argument.
Around filters
In addition to the individual before and after filters, it’s also possible to specify that a single object should handle both the before and after call. That’s especially useful when you need to keep state active between the before and after, such as the example of a benchmark filter below:
class WeblogController < ActionController::Base
around_filter BenchmarkingFilter.new
# Before this action is performed, BenchmarkingFilter#before(controller) is executed
def index
end
# After this action has been performed, BenchmarkingFilter#after(controller) is executed
end
class BenchmarkingFilter
def initialize
@runtime
end
def before
start_timer
end
def after
stop_timer
report_result
end
end
Filter chain skipping
Some times its convenient to specify a filter chain in a superclass that’ll hold true for the majority of the subclasses, but not necessarily all of them. The subclasses that behave in exception can then specify which filters they would like to be relieved of. Examples
class ApplicationController < ActionController::Base
before_filter :authenticate
end
class WeblogController < ApplicationController
# will run the :authenticate filter
end
class SignupController < ApplicationController
# will not run the :authenticate filter
skip_before_filter :authenticate
end
Filter conditions
Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to exclude or the actions to include when executing the filter. Available conditions are :only
or :except
, both of which accept an arbitrary number of method references. For example:
class Journal < ActionController::Base
# only require authentication if the current action is edit or delete
before_filter :authorize, :only => [ :edit, :delete ]
private
def
# redirect to login unless authenticated
end
end
When setting conditions on inline method (proc) filters the condition must come first and be placed in parentheses.
class UserPreferences < ActionController::Base
before_filter(:except => :new) { # some proc ... }
# ...
end
Instance Method Summary collapse
-
#after_filters ⇒ Object
Returns all the after filters for this class and all its ancestors.
-
#append_after_filter(*filters, &block) ⇒ Object
(also: #after_filter)
The passed
filters
will be appended to the array of filters that’s run after actions on this controller are performed. -
#append_around_filter(*filters) ⇒ Object
(also: #around_filter)
The passed
filters
will have theirbefore
method appended to the array of filters that’s run both before actions on this controller are performed and have theirafter
method prepended to the after actions. -
#append_before_filter(*filters, &block) ⇒ Object
(also: #before_filter)
The passed
filters
will be appended to the array of filters that’s run before actions on this controller are performed. -
#before_filters ⇒ Object
Returns all the before filters for this class and all its ancestors.
-
#excluded_actions ⇒ Object
Returns a mapping between filters and actions that may not run them.
-
#included_actions ⇒ Object
Returns a mapping between filters and the actions that may run them.
-
#prepend_after_filter(*filters, &block) ⇒ Object
The passed
filters
will be prepended to the array of filters that’s run after actions on this controller are performed. -
#prepend_around_filter(*filters) ⇒ Object
The passed
filters
will have theirbefore
method prepended to the array of filters that’s run both before actions on this controller are performed and have theirafter
method appended to the after actions. -
#prepend_before_filter(*filters, &block) ⇒ Object
The passed
filters
will be prepended to the array of filters that’s run before actions on this controller are performed. -
#skip_after_filter(*filters) ⇒ Object
Removes the specified filters from the
after
filter chain. -
#skip_before_filter(*filters) ⇒ Object
Removes the specified filters from the
before
filter chain.
Instance Method Details
#after_filters ⇒ Object
Returns all the after filters for this class and all its ancestors.
292 293 294 |
# File 'lib/action_controller/filters.rb', line 292 def after_filters #:nodoc: @after_filters ||= read_inheritable_attribute("after_filters") || [] end |
#append_after_filter(*filters, &block) ⇒ Object Also known as: after_filter
The passed filters
will be appended to the array of filters that’s run after actions on this controller are performed.
195 196 197 198 199 200 |
# File 'lib/action_controller/filters.rb', line 195 def append_after_filter(*filters, &block) conditions = extract_conditions!(filters) filters << block if block_given? add_action_conditions(filters, conditions) append_filter_to_chain('after', filters) end |
#append_around_filter(*filters) ⇒ Object Also known as: around_filter
The passed filters
will have their before
method appended to the array of filters that’s run both before actions on this controller are performed and have their after
method prepended to the after actions. The filter objects must all respond to both before
and after
. So if you do append_around_filter A.new, B.new, the callstack will look like:
B#before
A#before
A#after
B#after
222 223 224 225 226 227 228 229 |
# File 'lib/action_controller/filters.rb', line 222 def append_around_filter(*filters) conditions = extract_conditions!(filters) for filter in filters.flatten ensure_filter_responds_to_before_and_after(filter) append_before_filter(conditions || {}) { |c| filter.before(c) } prepend_after_filter(conditions || {}) { |c| filter.after(c) } end end |
#append_before_filter(*filters, &block) ⇒ Object Also known as: before_filter
The passed filters
will be appended to the array of filters that’s run before actions on this controller are performed.
174 175 176 177 178 179 |
# File 'lib/action_controller/filters.rb', line 174 def append_before_filter(*filters, &block) conditions = extract_conditions!(filters) filters << block if block_given? add_action_conditions(filters, conditions) append_filter_to_chain('before', filters) end |
#before_filters ⇒ Object
Returns all the before filters for this class and all its ancestors.
287 288 289 |
# File 'lib/action_controller/filters.rb', line 287 def before_filters #:nodoc: @before_filters ||= read_inheritable_attribute("before_filters") || [] end |
#excluded_actions ⇒ Object
Returns a mapping between filters and actions that may not run them.
302 303 304 |
# File 'lib/action_controller/filters.rb', line 302 def excluded_actions #:nodoc: @excluded_actions ||= read_inheritable_attribute("excluded_actions") || {} end |
#included_actions ⇒ Object
Returns a mapping between filters and the actions that may run them.
297 298 299 |
# File 'lib/action_controller/filters.rb', line 297 def included_actions #:nodoc: @included_actions ||= read_inheritable_attribute("included_actions") || {} end |
#prepend_after_filter(*filters, &block) ⇒ Object
The passed filters
will be prepended to the array of filters that’s run after actions on this controller are performed.
204 205 206 207 208 209 |
# File 'lib/action_controller/filters.rb', line 204 def prepend_after_filter(*filters, &block) conditions = extract_conditions!(filters) filters << block if block_given? add_action_conditions(filters, conditions) prepend_filter_to_chain("after", filters) end |
#prepend_around_filter(*filters) ⇒ Object
The passed filters
will have their before
method prepended to the array of filters that’s run both before actions on this controller are performed and have their after
method appended to the after actions. The filter objects must all respond to both before
and after
. So if you do prepend_around_filter A.new, B.new, the callstack will look like:
A#before
B#before
B#after
A#after
239 240 241 242 243 244 245 |
# File 'lib/action_controller/filters.rb', line 239 def prepend_around_filter(*filters) for filter in filters.flatten ensure_filter_responds_to_before_and_after(filter) prepend_before_filter { |c| filter.before(c) } append_after_filter { |c| filter.after(c) } end end |
#prepend_before_filter(*filters, &block) ⇒ Object
The passed filters
will be prepended to the array of filters that’s run before actions on this controller are performed.
183 184 185 186 187 188 |
# File 'lib/action_controller/filters.rb', line 183 def prepend_before_filter(*filters, &block) conditions = extract_conditions!(filters) filters << block if block_given? add_action_conditions(filters, conditions) prepend_filter_to_chain('before', filters) end |
#skip_after_filter(*filters) ⇒ Object
Removes the specified filters from the after
filter chain. Note that this only works for skipping method-reference filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out of many sub-controllers need a different hierarchy.
You can control the actions to skip the filter for with the :only
and :except
options, just like when you apply the filters.
274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/action_controller/filters.rb', line 274 def skip_after_filter(*filters) if conditions = extract_conditions!(filters) remove_contradicting_conditions!(filters, conditions) conditions[:only], conditions[:except] = conditions[:except], conditions[:only] add_action_conditions(filters, conditions) else for filter in filters.flatten write_inheritable_attribute("after_filters", read_inheritable_attribute("after_filters") - [ filter ]) end end end |
#skip_before_filter(*filters) ⇒ Object
Removes the specified filters from the before
filter chain. Note that this only works for skipping method-reference filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out of many sub-controllers need a different hierarchy.
You can control the actions to skip the filter for with the :only
and :except
options, just like when you apply the filters.
256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/action_controller/filters.rb', line 256 def skip_before_filter(*filters) if conditions = extract_conditions!(filters) remove_contradicting_conditions!(filters, conditions) conditions[:only], conditions[:except] = conditions[:except], conditions[:only] add_action_conditions(filters, conditions) else for filter in filters.flatten write_inheritable_attribute("before_filters", read_inheritable_attribute("before_filters") - [ filter ]) end end end |