This blog is part of our Rails 5 series.

Before Rails 5, returning false from any before_ callback in ActiveModel or ActiveModel::Validations , ActiveRecord and ActiveSupport resulted in halting of callback chain.

class Order < ActiveRecord :: Base before_save :set_eligibility_for_rebate before_save :ensure_credit_card_is_on_file def set_eligibility_for_rebate self . eligibility_for_rebate ||= false end def ensure_credit_card_is_on_file puts "check if credit card is on file" end end Order . create! => ActiveRecord :: RecordNotSaved : ActiveRecord :: RecordNotSaved

In this case the code is attempting to set the value of eligibility_for_rebate to false. However the side effect of the way Rails callbacks work is that the callback chain will be halted simply because one of the callbacks returned false .

Right now, to fix this we need to return true from before_ callbacks, so that callbacks are not halted.

Improvements in Rails 5

Rails 5 fixed this issue by adding throw(:abort) to explicitly halt callbacks.

Now, if any before_ callback returns false then callback chain is not halted.

class Order < ActiveRecord :: Base before_save :set_eligibility_for_rebate before_save :ensure_credit_card_is_on_file def set_eligibility_for_rebate self . eligibility_for_rebate ||= false end def ensure_credit_card_is_on_file puts "check if credit card is on file" end end Order . create! => check if credit card is on file => < Order id: 4 , eligibility_for_rebate: false >

To explicitly halt the callback chain, we need to use throw(:abort) .

class Order < ActiveRecord :: Base before_save :set_eligibility_for_rebate before_save :ensure_credit_card_is_on_file def set_eligibility_for_rebate self . eligibility_for_rebate ||= false throw ( :abort ) end def ensure_credit_card_is_on_file puts "check if credit card is on file" end end Order . create! => ActiveRecord :: RecordNotSaved : Failed to save the record

Opting out of this behavior

The new Rails 5 application comes up with initializer named callback_terminator.rb .

ActiveSupport.halt_callback_chains_on_return_false = false

By default the value is to set to false .

We can turn off this default behavior by changing this configuration to true . However then Rails shows deprecation warning when false is returned from callback.

ActiveSupport . halt_callback_chains_on_return_false = true class Order < ApplicationRecord before_save :set_eligibility_for_rebate before_save :ensure_credit_card_is_on_file def set_eligibility_for_rebate self . eligibility_for_rebate ||= false end def ensure_credit_card_is_on_file puts "check if credit card is on file" end end => DEPRECATION WARNING : Returning `false` in Active Record and Active Model callbacks will not implicitly halt a callback chain in the next release of Rails . To explicitly halt the callback chain , please use `throw :abort` instead . ActiveRecord :: RecordNotSaved : Failed to save the record

How older applications will work with this change?

The initializer configuration will be present only in newly generated Rails 5 apps.

If you are upgrading from an older version of Rails, you can add this initializer yourself to enable this change for entire application.

This is a welcome change in Rails 5 which will help prevent accidental halting of the callbacks.