Custom model callbacks in RubyOnRails

RubyOnRails provides us with many model callbacks around the object’s lifecycle when object is being created, updated or destroyed. For example: before_create, after_create, before_update, after_destroy etc. We use them to write & run our code around the object’s lifecycle by defining a method and associating them as one of the callbacks.

But then how can we make a piece of code execute as a callback for any another defined method except create, update, save and destroy? For example, let’s say we have a model Article and we want to execute something just before and after an article is being published without hooking into model’s before_save and after_save callbacks?

RubyOnRails or more precisely ActiveRecord provides us with a module ActiveModel::Callbacks, which allows us to define and register custom callbacks using define_model_callbacks method. Lets have a look at the snippet below for the above scenario:


class Article 
  extend ActiveModel::Callbacks
  
  define_model_callbacks :publish, :only => [:before, :after]

  before_publish :check_publishability
  after_publish  :notify_subscribers

  def publish
    run_callbacks :publish do
      puts "Publishing article..."
    end
  end

  private
  
  def check_publishability
    puts "Checking publishability rules..."
  end

  def notify_subscribers
    puts "Notifying subscribers..."
  end

end

By default define_model_callbacks provides with all three callbacks i.e. before, after and around, but here in our example we have chosen to have just before and after callbacks by specifying a hash :only => [:after, :before] since only those two were needed. By using define_model_callbacks :publish, :only => [:after, :before] we got two new callback before_publish and after_publish, which we then used to register our callback methods.

So now when we will call publish method on an article object, check_publishability will be executed as a before callback and notify_subscribers will be executed as an after callback for the publish method.

One point to note here is that the code in the publish method should be wrapped in run_callback :publish block, so that when it is called on an object, its callbacks are executed too.

For the snippet above run_callback triggers before callbacks, yield the block given and then trigger after callbacks.

More information on define_model_callbacks can be found here.


>> article = Article.last
>> article.publish
Checking publishability rules...
Publishing article...
Notifying subscribers...

Leave a Reply

Your email address will not be published. Required fields are marked *