Controllers
Filters
The Rails REST implementation dictates the default seven actions for your controllers, but frequently we want to share functionality across multiple actions or even across controllers. Controller filters are one way to do that.
Before, After, and Around
There are three types of filters implemented in Rails:
before_filter
runs before the controller actionafter_filter
runs after the controller actionaround_filter
yields to the controller action wherever it chooses
Basic Usage
before_filter
is, by far, the most common. There are two ways to invoke a before filter. First, as an anonymous block:
1 2 3 4 5 |
|
As written, this before_filter
will execute before every action in the controller. We’ll look at options to scope this behavior later.
While the anonymous block style works, it’s usually cleaner to implement a named method:
1 2 3 4 5 6 7 8 9 10 |
|
Since the filter is only going to be used within the controller, and won’t be accessed directly by the router, it’s good form to make the method private.
after_filter
An after_filter
works exactly the same, just executing after the controller action.
around_filter
The most rare is the around_filter
. Here is the common example of its usage:
1 2 3 4 5 6 7 8 9 |
|
Wherever yield
is called, the action will be executed. So the functionality here could recover from any exception that occurs in the yielded action.
only
and except
All three filters accept the options :only
and :except
:
:only
: a whitelist of actions for which the filter should run:except
: a blacklist of actions for which the filter should not run.
For example, we could remove the condition from the before_filter
sample above if we scope to only those actions which will have a params[:id]
:
1 2 3 4 5 6 7 8 9 10 |
|
Or get the same effect using :except
:
1 2 3 |
|
Sharing Filters
Filters are most often about sharing code across actions in a single controller, but we can also share them across controllers.
Sharing through ApplicationController
The most common way to reuse filters across controllers is to move them to ApplicationController
. Since all controllers inherit from ApplicationController
, they’ll have access to those methods. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
But how useful will it be to look up an Article
in other controllers?
Generalizing to find_resource
With a bit of object introspection and mixing in some metaprogramming, we can write a generalized find_resource
method that will infer the model name from the current controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Exercises
Get the Blogger project from GitHub and run setup procedures:
1 2 3 4 5 |
|
All existing tests should pass. Optionally, run the tests continuously while developing by running guard
- Implement a
before_filter
inArticlesController
to remove all calls tofind
in the actions. - Implement an
after_filter
that turns the article titles to all uppercase, but does not change the data in the database. - Implement a
before_filter
for just thecreate
action onCommentsController
that replaces the word"sad"
with"happy"
in the incoming comment body. - Implement an
around_filter
that catches an exception, writes an apology into theflash[:notice]
, and redirects to the articlesindex
. If the exception was raised inarticles#index
, render the message as plain text (render text: "xyz"
). Cause an exception and make sure it works.
References
- Rails Guide on Controller Filters: http://guides.rubyonrails.org/action_controller_overview.html#filters