prepend_before_action in Rails

Rails encourages usage of callbacks in controllers to execute common pieces of  code before or after an action. A very simple example of this is calling the authenticate_user! method from Devise before every action to make sure that user is authenticated.

class ApplicationController < ActionController::Base
  before_action :authenticate_user!
end

But what if you want to execute a different piece of code before calling authenticate_user!? I wanted to set a cookie based on whether the request was coming from mobile app or from web before calling authenticate_user!. The most important thing was I wanted to set the cookie only for a particular controller action.

class MagicAuthController < ApplicationController
  # Set cookie before authenticate_user! before auth
  
  def auth
  end
end

This can be solved by multiple approaches. We can create a separate base controller for MagicAuthController and make sure the cookie is set before calling authenticate_user!

class MagicBaseController < ActionController::Base
  before_action :set_cookie, only: [:auth]
  before_action :autheticate_user!
end

Though Rails also provides a nifty callback called prepend_before_action. It inserts the callback that is passed to it to the start of the callback chain.

class MagicAuthController < ApplicationController
  def auth
  end
end

As the MagicAuthController inherits from the ApplicationController the callback chain consists of authenticate_user! as of now.

class MagicAuthController < ApplicationController
  prepend_before_action :set_cookie, only: [:auth]
  
  def auth
  end
end

This changes the callback chain and now set_cookie is the first callback in the callback chain for the auth action.

Rails also provides other friends of prepend_before_action such as prepend_after_action and prepend_around_action.

It also accepts a block argument.

prepend_before_action only: [:auth] { cookies[:clid] = params[:clid] }

Want to know more about such tricks and tips from Rails? Subscribe to my newsletter and keep up with Ruby and Rails.