Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the "self" keyword in Rails

Regarding the keyword self in Rails, I know that the keyword refers to an instance of the class itself. For example, in self.encrypted_password.

I have less of an idea why the attribute password, passed as an parameter on the right hand side, isn't prefixed with the self keyword too?

class User < ActiveRecord::Base

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

  validates :password, :presence     => true,
                      :confirmation => true,
                      :length       => { :within => 6..40 }

  before_save :encrypt_password

  private

  def encrypt_password
    self.encrypted_password = encrypt(password)
  end

  def encrypt(string)
    string # Only a temporary implementation!
  end
end

Can someone explain when to use, or not use, the self keyword?

like image 640
Sarun Sermsuwan Avatar asked Jan 13 '12 01:01

Sarun Sermsuwan


2 Answers

Answer

The answer is simple: scope visibility.

def encrypt_password
  self.encrypted_password = encrypt(password)
end

There is (or, rather, there should be at runtime) something called password. In your case, it's an attribute from the database. But it also can be a local variable. If such name isn't found, error will be raised.

But you have to prefix encrypted_password with self to explicitly state that you're going to update instance attribute. Otherwise, new local variable encrypted_password will be created. Obviously, not the effect you wanted.

More explanation

Here's a little snippet of code

class Foo
  attr_accessor :var

  def bar1
    var = 4
    puts var
    puts self.var
  end
end

f = Foo.new
f.var = 3
f.bar1

Output

4
3

So, as we can see, var is assigned without self keyword and, because of this, now there are two names var in the scope: local variable and instance attribute. Instance attribute is hidden by local variable, so if you really want to access it, use self.

like image 118
Sergio Tulentsev Avatar answered Nov 11 '22 07:11

Sergio Tulentsev


The question has nothing to do with Rails, but with Ruby. When you look at your code:

def encrypt_password
  self.encrypted_password = encrypt(password)
end

it will be translated by Ruby into:

  • self.encrypted_password = is a method call to encrypted_password= method.
  • This method is generated by Rails on the fly, because there exists a database column with the same name.
  • encrypt(password) contains 2 methods calls.

    1. password calls the method password which is a real attribute of the model by the declaration attr_accessor :password. This declaration creates two methods:
      • password: getter of the attribute
      • password=: setter of the attribute

    See the explanation of @Sergio_Tulentsev how the getter could be hidden by a local variable (which is not the case in your implementation, there is no local variable).

    1. Calls the method encrypt with the return value of method call password.

So the use of self. makes it explicit that you have (all the time) a method call, you don't access the attribute directly. If you want to do that, you have to use @password = <some value> inside an instance method, but I like the style with self.password = <some value> much more.

I hope it is clear now how your code is interpreted.

like image 22
mliebelt Avatar answered Nov 11 '22 06:11

mliebelt