DEV Community

Cover image for Callbacks are calling...
rockshellS
rockshellS

Posted on

Callbacks are calling...

Writing my second blog for Flatiron has taken me down some worm holes. Which hasn't been a bad thing! While learning Ruby on Rails we came across Validations, and how/when to use them. Validations are used to ensure that only valid data is saved into your database. Seems like a secure way to handle information. A Callback is called before or after the Validation is called. These actions happen fast and are considered magic.

-Callbacks are methods that get called at certain moments of an object’s life cycle. With them is possible to write code that will run whenever an ActiveRecord object is created, saved, updated, deleted, validated or loaded from the database - Ruby on Rails Guide

Alt Text

In rails c,

run    ActiveRecord::Callbacks::CALLBACKS
Enter fullscreen mode Exit fullscreen mode
=> [:after_initialize, :after_find, :after_touch,
 :before_validation, :after_validation, :before_save,
 :around_save, :after_save, :before_create, :around_create,
 :after_create, :before_update, :around_update, :after_update,
 :before_destroy, :around_destroy, :after_destroy, 
:after_commit, :after_rollback]
Enter fullscreen mode Exit fullscreen mode

this is a list of available callbacks.

In order to use callbacks, you need to register them. Which means you need to add them to your Model using the correct method. You can do this four different ways:

  • Method references (symbol)- recommended
  • Callback objects -recommended
  • Inline methods (using a proc)-when appropriate
  • Inline evals methods (using a string)-deprecated

Here are some examples on how to use them. These code examples are from the ActiveRecords guide page
http://guides.rubyonrails.org/active_record_callbacks.html

class User < ApplicationRecord
  validates :login, :email, presence: true

  before_validation :ensure_login_has_a_value

  private
    def ensure_login_has_a_value
      if login.nil?
        self.login = email unless email.blank?
      end
    end
end
Enter fullscreen mode Exit fullscreen mode

This callback before_validation is making sure that the user has a valid login before it will run the validates method. By calling the ensure_login_has_a_value method wrote in the private section in the User class. Everything happens here in the model.

You can pass blocks ( useful for short one-liner logic )

class User < ApplicationRecord
  validates :login, :email, presence: true

  before_create do
    self.name = login.capitalize if name.blank?
  end
end
Enter fullscreen mode Exit fullscreen mode

Callbacks can also be registered to only fire on certain life cycle events:

       class User < ApplicationRecord
          before_validation :normalize_name, on: :create

        # :on takes an array as well
          after_validation :set_location, on: [ :create, 
          :update ]

     private
         def normalize_name
           self.name = name.downcase.titleize
          end
        def set_location
           self.location = LocationService.query(self)
          end
       end 
Enter fullscreen mode Exit fullscreen mode

Here before_validation is calling the normalize_name method on: :create. Which will make sure the User name is in downcase and titlized. The after_validation is calling the set_location method on: [ :create, :update ]

Relational Callbacks:

      class User < ApplicationRecord
        has_many :articles, dependent: :destroy
      end

      class Article < ApplicationRecord
        after_destroy :log_destroy_action

      def log_destroy_action
        puts 'Article destroyed'
       end
     end
irb>
user = User.first
=> #<User id: 1>
irb>
user.articles.create!
=> #<Article id: 1, user_id: 1>
irb>
user.destroy
Article destroyed
=> #<User id: 1>
Enter fullscreen mode Exit fullscreen mode

Here the user has many accounts and when they delete their account all their articles should be deleted as well by using dependent: :destroy.

Another example of registering callbacks with callback macros is that they are inheritable

     class Sweet < ApplicationRecord
       before_save :do_something_with_sweet
     end
Enter fullscreen mode Exit fullscreen mode
     class Vendor < ApplicationRecord
       before_save :do_something_with_vendor
     end
Enter fullscreen mode Exit fullscreen mode

Here when the save method is call on Vendor instance, both :do_something_with_sweet and :do_something_with_vendor are triggered.

    class Comment < ApplicationRecord
     after_create :send_email_to_author, if: 
      :author_wants_emails?,
     unless: Proc.new { |comment| 
       comment.article.ignore_comments? }
    end
Enter fullscreen mode Exit fullscreen mode

Here the after_create will send_email_to_author if author_wants_emails.
unless (Proc is looping thorough all the comments) and the people who had comments wouldn't get an email.

It’s possible to skip callbacks with these methods: http://guides.rubyonrails.org/active_record_callbacks.html

  • decrement!
  • decrement_counter
  • delete
  • delete_all
  • delete_by
  • increment!
  • increment_counter
  • insert
  • insert!
  • insert_all
  • insert_all!
  • touch_all
  • update_column
  • update_columns
  • update_all
  • update_counters
  • upsert
  • upsert_all

Alt Text

Conclusion, There are so many different fun ways for Callbacks to be used. They are magical and should be used sparingly. Using too many could become a problem and hard to navigate. I hope this was helpful.

Latest comments (0)