Controller callbacks in Ruby on Rails
What is a callback and how to use them effectively in a controller? Let's find out together :)
What is a callback?
A controller callback allows Ruby on Rails to execute a method before and/or after a specific controller action (Index, Show, Create, Update, etc.). Here is a non-exhaustive list of available callbacks in Ruby on Rails:
- after_action: Executes a method after an action (Index, Show, Edit, etc.).
- around_action: Executes a method before and after an action.
- skip_before_action: Skips executing a method before an action. This implies that this method would normally be called before the action.
You can find the complete list of all possible callbacks in the Ruby on Rails documentation.
Note: The callbacks presented in this article are only available and usable in controllers. They cannot be called from a model or a PORO.
How to use a callback?
To illustrate the use of a callback, let’s take a very simple example: this blog. All users, whether logged in or not, can access the list of articles and view an article (which you are doing right now). However, only authors can edit and delete their articles. Without using a callback, the code would look 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
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
def edit
@post = Post.find(params[:id])
redirect_to root_url if current_user != @post.author
end
def update
@post = Post.find(params[:id])
redirect_to root_url if current_user != @post.author
@post.update!(post_params)
end
def destroy
@post = Post.find(params[:id])
redirect_to root_url if current_user != @post.author
@post.destroy!
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
As we can see, there is redundant code in multiple controller actions. Fortunately, Ruby on Rails provides before_action, which allows us to execute code before one or more actions. Let’s rewrite our controller using the before_action callback:
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
class PostsController < ApplicationController
before_action :set_post, except: :index
before_action :redirect_unless_author, except: %i[index show]
def index
@posts = Post.all
end
def show; end
def edit; end
def update
@post.update!(post_params)
end
def destroy
@post.destroy!
end
private
def post_params
params.require(:post).permit(:title, :body)
end
# This method is called before all actions EXCEPT index
def set_post
@post = Post.find(params[:id])
end
# This method is called before all actions EXCEPT index and show
def redirect_unless_author
redirect_to root_url if current_user != @post.author
end
end
In this new version, the controller follows the DRY principle by moving the repetitive logic into separate methods and calling them via callbacks. Creating methods that perform only one action (set_post
finds the post by the ID parameter, and redirect_unless_author
redirects if the user is not the author) aligns with the (Single Responsibility Principle).
By separating the logic into multiple methods and using callbacks, we ensure better readability and simpler code throughout the controller.