4.17. Miscellaneous

In this section, I am going to show you some examples of topics and questions that are important for your everyday work, but as a whole go beyond the scope of this book aimed at beginners. They provide recipes for solving specific ActiveRecord problems.

Callbacks

Callbacks are defined programming hooks in the life of an ActiveRecord object. You can find a list of all callbacks at http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html. Here are the most frequently used callbacks:
  • before_validation
    Executed before the validation.
  • after_validation
    Executed after the validation.
  • before_save
    Executed before each save.
  • before_create
    Executed before the first save.
  • after_save
    Executed after every save.
  • after_create
    Executed after the first save.
A callback is always executed in the model. Let's assume you always want to save an e-mail address in a User model in lower case, but also give the user of the web interface the option to enter upper case letters. You could use a before_save callback to convert the attribute email to lower case via the method downcase.
The Rails application:
$ rails new shop
  [...]
$ cd shop
$ rails generate model user email login
  [...]
$ rake db:migrate
  [...]
$
Here is what the model app/models/user.rb would look like. The interesting stuff is the before_save part:
class User < ActiveRecord::Base
  validates :login,
            presence: true

  validates :email,
            presence: true,
            format: { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }

  before_save :downcase_email

  private

  def downcase_email
    self.email = self.email.downcase
  end

end
Let's see in the console if it really works as we want it to:
$ rails console
Loading development environment (Rails 4.0.0)
>> User.create(login: 'smith', email: 'SMITH@example.com')
   (0.1ms)  begin transaction
  SQL (29.9ms)  INSERT INTO "users" ("created_at", "email", "login", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Wed, 21 Nov 2012 09:14:47 UTC +00:00], ["email", "smith@example.com"], ["login", "smith"], ["updated_at", Wed, 21 Nov 2012 09:14:47 UTC +00:00]]
   (0.7ms)  commit transaction
=> #<User id: 1, email: "smith@example.com", login: "smith", created_at: "2012-11-21 09:14:47", updated_at: "2012-11-21 09:14:47">
>> exit
$ 
Even though the e-mail address was entered partly with a capital letters, ActiveRecord has indeed converted all letters automatically to lower case via the before_save callback.
In Chapter 9, Action Mailer you will find an example for the same model where we use an after_create callback to automatically send an e-mail to a newly created user. In the section called “Default Values” you will find an example for defining a default value for a new object via an after_initialize callback.

Default Values

If you need specific default values for an ActiveRecord object, you can easily implement this with the after_initialize callback. This method is called by ActiveRecord when a new object is created. Let's assume we have a model Order and the minimum order quantity is always 1, so we can enter 1 directly as default value when creating a new record.
Let's set up a quick example:
$ rails new shop
  [...]
$ cd shop
$ rails generate model order product_id:integer quantity:integer
  [...]
$ rake db:migrate
  [...]
$
We write an after_initialize callback into the file app/models/order.rb:
class Order < ActiveRecord::Base
  after_initialize :set_defaults

  private
  def set_defaults 
    self.quantity ||= 1
  end
end
And now we check in the console if a new order object automatically contains the quantity 1:
$ rails console
Loading development environment (Rails 4.0.0)
>> order = Order.new
=> #<Order id: nil, product_id: nil, quantity: 1, created_at: nil, updated_at: nil>
>> order.quantity
=> 1
>> exit
$
That's working fine.