Filter Types

A filter can take one of three forms: method reference (symbol), external class, or block.
The first is by far the most common and works by referencing a protected method some-
where in the inheritance hierarchy of the controller. In the bank example in my previous post,
both BankController and VaultController use this form.

 

  • Filter Classes

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, as in code below. The name of
the class method should match the type of filter desired (e.g., before, after, around).

 

class OutputCompressionFilter
def self.after(controller)
controller.response.body = compress(controller.response.body)
end
end

class NewspaperController < ActionController::Base
after_filter OutputCompressionFilter
end

 

The method of the Filter class is passed the controller instance it is filtering. It gets
full access to the controller and can manipulate it as it sees fit. The fact that it gets an
instance of the controller to play with also makes it seem like feature envy, and frankly,
I haven’t had much use for this technique.

 

  • Inline Method

The inline method (using a block parameter to the filter method) can be used to quickly
do something small that doesn’t require a lot of explanation or just as a quick test.

class WeblogController < ActionController::Base
before_filter do
redirect_to new_user_session_path unless authenticated?
end
end

 
The block is executed in the context of the controller instance, using instance_eval.
This means that the block has access to both the request and response objects complete
with convenience methods for params, session, template, and assigns.

Filter Inheritance

Controller inheritance hierarchies share filters downward. Your average Rails application
has an ApplicationController from which all other controllers inherit, so if you
wanted to add filters that are always run no matter what, that would be the place to do so.

class ApplicationController < ActionController::Base
after_filter :compress

Subclasses can also add and/or skip already defined filters without affecting the superclass.
For example, consider the two related classes below, and how they interact.

class BankController < ActionController::Base
before_filter :audit
protected
def audit
# record this controller’s actions and parameters in an audit log
end
end

class VaultController < BankController
before_filter :verify_credentials
protected
def verify_credentials
# make sure the user is allowed into the vault
end
end

 

Any actions performed on BankController (or any of its subclasses) will
cause the audit method to be called before the requested action is exe-
cuted. On the VaultController, first the audit method is called, followed by
verify_credentials, because that’s the order in which the filters were specified. (Fil-
ters are executed in the class context where they’re declared, and the BankController
has to be loaded before VaultController, since it’s the parent class.)
If the audit method happens to call render or redirect_to for whatever reason,
verify_credentials and the requested action are never called. This is called halting
the filter chain.

Filters in Rails

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. Filter declarations are macro style class methods, that is, they appear at
the top of your controller method, inside the class context, before method definitions.
We also leave off the parentheses around the method arguments, to emphasize their
declarative nature, like this:

before_filter :require_authentication

As with many other macro-style methods in Rails, you can pass as many symbols as you
want to the filter method:

before_filter :security_scan, :audit, :compress

 

Or you can break them out into separate lines, like this:

before_filter :security_scan
before_filter :audit
before_filter :compress

In contrast to the somewhat similar callback methods of Active Record, you can’t im-
plement a filter method on a controller by adding a method named before_filter or
after_filter.
You should make your filter methods protected or private; otherwise, they might
be callable as public actions on your controller (via the default route).

Importantly, filters have access to request, response, and all the instance variables
set by other filters in the chain or by the action (in the case of after filters). Filters can
set instance variables to be used by the requested action, and often do so.