Rails 7.1 adds the ability to raise errors on missing callback actions

railsAugust 09, 2023Dotby Alkesh Ghorpade

Rails controllers have callbacks allowing you to run code at specific request lifecycle points. You can use the only or except options to specify which actions the callback should run for.

class UsersController < ApplicationController
  before_action :set_user, except: [:index, :new, :create]
  after_action :notify_admins, only: [:create]
end

In previous versions of Rails, the only and except options in controller callbacks did not check to see if the actions they referred to existed. This meant you could make a typo in the action name and the callback would not run, even though it was supposed to.

In the example above, if there is a typo in the only option of the after_action callback, the notify_admins method will never be executed for the create action. The admins of your system would never receive any notifications regarding new sign-ups on your platform.

class UsersController < ApplicationController
  before_action :set_user, except: [:index, :new, :create]
  after_action :notify_admins, only: [:crete] # typo crete => create
end

Consider another scenario, if you refactor your codebase and change the name of a controller action referenced in a callback. You could accidentally deploy a bug to production.

class UsersController < ApplicationController
  before_action :authorize_user, only: [:dashboard]

  # many lines of code of other actions

  # method name changed from +dashboard+ to +admin_dashboard+
  def admin_dashboard
  end
end

In the above example, a normal user of your system could exploit the bug in your code to access your admin dashboard and hack critical data.

Before Rails 7.1

Before Rails 7.1, there were two ways to validate these issues.

  • The first way was to cover all the test scenarios for the controller. This would ensure that all the possible paths through the controller were tested, including those that involved the callbacks.

  • The second way was to write a script that checks the missing actions added in the only/except option. This script would scan the controller code for any callbacks that referenced actions that did not exist.

In Rails 7.1

Rails 7.1 allows configuring the controllers to raise an error when a callback's only/except options have missing actions.

When before_action :callback, only: :action_name is declared in a controller that doesn't respond to action_name, it raises an exception at request time. This is a safety measure to ensure that typos or forgetfulness don't prevent a crucial callback from being run.

This feature is turned on for new applications with Rails 7.1 by default. The config is true in the Rails config/environments/development.rb and config/environments/test.rb files. To turn off this behaviour, you must set the config to false in your application configuration.

config.action_controller.raise_on_missing_callback_actions = false

For the below UsersController, there is a typo in the after_action :notify_admins only option. When the application requests any action in the controller, it raises the below error.

class UsersController < ApplicationController
  before_action :set_user, except: [:index, :new, :create]
  after_action :notify_admins, only: [:crete] # typo crete => create
end

# Request for UsersController#index action will raise the error

The crete action could not be found for the :notify_admins
callback on UsersController,
but it is listed in the controller's
:only option.

Raising for missing callback actions is a new default in Rails 7.1,
if you'd
like to turn this off you can delete the option from the environment configurations
or set `config.action_controller.raise_on_missing_callback_actions` to `false`.

Adding undefined action in the callback options is a bug that should be caught during development. The feature is helpful while refactoring the code or there are typos when coding.

When to avoid this feature?

If your application has shared controller concerns, you might have to turn off this feature.

module AdminNotification
  extend ActiveSupport::Concern

  included do
    before_action :notify_admins, only: [:create, :destroy]
  end
end

class UsersController < ApplicationController
  include AdminNotification

  def create
  end
end

class Admins::UsersController < ApplicationController
  include AdminNotification

  def destroy
  end
end

The AdminNotification concern has a before_action that is included in the UsersController and Admins::UsersController. Requesting any action in the UsersController will raise an error for the missing destroy action. Similarly, a missing create action error gets raised when the request is made for any action in the Admins::UsersController. Turning this feature off makes more sense in these kind of scenarios.

To know more about this feature, please refer to this PR.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right