Kvass Kvass - 6 months ago 15
Ruby Question

Rails -- self vs. @

I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:

class User < ActiveRecord::Base
attr_accessor :password

attr_accessible :name, :email,: password, :password_confirmation

email_regex = /^[A-Za-z0-9._+-]+@[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/
#tests for valid email addresses.

validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true

before_save :encrypt_password

private

def encrypt_password
self.encrypted_password = encrypt(password)
end

def encrypt(string)
string
end
end


I posted a previous question about
before_save
not working, and it turns out that what I had accidentally done is written my encrypt_password as:

def encrypt_password
@encrypted_password = encrypt(password)
end


I understand that if self.encrypted_password sets the encrypted_password attribute, but why does @encrypted_password not do that as well? In the response to the previous post about
before_save
not working someone said that the instance variable was "forgotten" after the method ended with the way I had originally coded it -- why was this the case? Can someone please explain how self and @ work differently in the context of the code above?

NOTE: I already took a look at the posts here and here, but they both say that "self" is calling the
attribute =
method, and I don't even understand how that method could exist here since I never created it or declared the encrypted_password w/
attr_accessor
. So I am still confused, and this is not a re-posting of those questions.

Answer

The accessors for encrypted_password have been automatically added by Rails for you because a field by that name exists in the users table.

Any field you add to a table will be automatically made available via self.field_name.

Here is where Michael Hartl's tutorial creates the encrypted_password field in the users table.

Also look at the user_spec.rb (Listing 7.3) in the linked page, where the author is testing for the presence of the encrypted_password field.

UPDATED:

As @mu points out, the @ is used for Ruby instance variables (aka "iv"). But encrypted_password is an "attribute" defined by Rails, and is not an instance variable.

If you run User.find(1).instance_variables, you will see that there is an iv called @attributes, which is of type Hash.

Inside that iv is where the encrypted_password is stored. Rails has defined accessor methods for encrypted_password, which gets/sets the data for that attribute in the @attributes Hash.

Note that you could also get/set the data via @attributes["encrypted_password"] called from within the User class (but the accessor methods are convenient way to do just that).