Rails Best Practices 6: Filters and helpers file

With the today’s post I shall conclude my series on Ruby On Rails Best Practices . This is not because the required topics are completed, but rather because after the release of Rails 3.0, some constructs have been changed and should be reviewed.
Probably in future we will return to talk about best practices, but starting from the new features introduced in the current Rails version.
But now I will describe two techniques that are valid and which are mainly related to code organization.

1. Use filters
In order to remove duplicate code within the controller is good practice to use the filters to perform tasks that are common to most if not to all methods.
A classic case in which you use filters is user authentication.
Another example may be a function that makes the log of user activity.
I show here a very simple example for demonstration purposes only

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class ApplicationController < ActionController::Base

def add_log
#create new log
log = ActivityLog.new
# read data from request
log.session_id = request.session_options[ :id]
log.user_id = current_user.id
log.browser = request.env['HTTP_USER_AGENT']
log.ip_address = request.env['REMOTE_ADDR']
log.controller = controller_name
log.action = action_name
log.request_at = Time.now
# Save the log
log.save
end

#other methods here

end

We have defined our function add_log that saves data request in a log on db.
Now in our controller we will do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

class PostController < ApplicationController

def index
add_log
@posts = Post.all
end

def new
add_log
@post = Post.new
end

def show
add_log
@post = Post.find(params[ :id]
end

def edit
add_log
@post = Post.find(params[ :id]
end

def create
add_log
@post = Post.create(params[:post]
end

def update
add_log
@post = Post.update(params[:post]
end

def destroy
add_log
@post = Post.find(params[ :id]
@post.destroy
end
end

The PostController can be optimized using a before_filter in the following way

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

class PostController < ApplicationController

before_filter :add_log

def index
@posts = Post.all
end

def new
@post = Post.new
end

def show
@post = Post.find(params[ :id]
end

def edit
@post = Post.find(params[ :id]
end

def create
@post = Post.create(params[:post]
end

def update
@post = Post.update(params[:post]
end

def destroy
@post = Post.find(params[ :id]
@post.destroy
end
end

Then if we want the calls to some methods (eg show and index) can not be traced we can simply define the filter as follows:

1
2
3
4
5

class PostController < ApplicationController

before_filter :add_log, :except => [:show, :index]

# methods

Obviously the use of filters should be done in a careful way because excessive use of before_filter or after_filter for operations not so general can make the code less immediate to understand.

2. Organize helpers by functionality
The second technique that I introduce today is a better system (in my opinion) to organize helper files.

Rails typically generates an helper file for each controller. So it’s easy that in a short time you have a large number of helpers and often you use only a few of them.
For example:

1
2
3
4
5

app/helpers/application_helper.rb
app/helpers/comments_helper.rb
app/helpers/posts_helper.rb
app/helpers/users_helper.rb
# ...

And in the ApplicationController:

1
2
3

class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
end

A system I find very convenient is rather to organize helpers for functionality, removing all unnecessary and empty files.

1
2
3

app/helpers/application_helper.rb
app/helpers/buttons_helper.rb
app/helpers/treeviews_helper.rb

In the ApplicationController you not need to change anything. As before it loads all the helpers in the helpers folder. The advantage of this solution is to have fewer helper files and know immediately where to enter or search for a method when you have to add or change it.