Search code examples
ruby-on-railsrubycallbackbefore-save

How are callbacks called?


What is the environment of a callback?

For example, in listing 6.20 of the standard Ruby on Rails Tutorial" (http://rails-4-0.railstutorial.org/book/modeling_users) it has:

class User < ActiveRecord::Base
  before_save { self.email = email.downcase }

Where does "email" come from? are we in a function where email is defined? or is actually @email?

Also, is there an important reason why we use self.email there rather than @email (or just email if it's the same as @email)? or is that just stylistic, or pedagogical?


Solution

  • There are several things going on at once here:

    1. The block is called in the context of the model instance being saved so self in the block is the User being saved. The block is also passed an argument but you don't have to use it.
    2. Local variables in Ruby aren't explicitly declared, they are implicitly declared when you first try to give them a value.
    3. The implicit receiver for method calls is self.
    4. But local variables take precedence over method calls so a mutator method (such as self.email=) needs an explicit receiver to distinguish it from a local variable.
    5. Instance variables (@email) don't have anything to do with attributes in ActiveRecord models.
    6. The accessor (self.email or just email if there is no email local variable) and mutator methods (self.email=) for ActiveRecord objects work with attributes, not instance variables.

    So this:

    before_save { self.email = email.downcase } # (1)
    

    could also be written as:

    before_save { |user| user.email = user.email.downcase } # (2)
    

    or even:

    before_save { |user| user.send(:email=, user.send(:email).downcase) } # (3)
    

    The main functional difference is that (2) requires the email and email= methods to be public.