tags:

views:

319

answers:

3

I'm trying to understand when to use self.method_name vs. when to use Classname.method_name.

In the example below, why does "before_create" need to reference "User.hash_password" instead of "self.hash_password" or just "hash_password"?

Since we are in the User class already, I thought the before_create method would "know" that "hash_password" is a member of its own class and would not need any special syntax to refer to it.

require 'digest/sha1'

class User < ActiveRecord::Base

  attr_accessor :password
  attr_accessible :name, :password

  validates_presence_of :name, :password
  validates_uniqueness_of :name

  def before_create
    self.hashed_password = User.hash_password(self.password)
  end

  def after_create
    @password = nil
  end

  def self.login(name, password)
    hashed_password = hash_password(password || "")
    self.find(:first, :conditions => ["name = ? and hashed_password = ?", name, hashed_password])
  end

  def try_to_login
    User.login(self.name, self.password)
  end

  private

  def self.hash_password(password)
    Digest::SHA1.hexdigest(password)
  end

end
+1  A: 

I have yet to delve into Ruby, but from your description I'd say that hash_password is a static method or is capable of being used in a static manner.

Randolpho
that's right. In ruby, static method are called class methods
Maximiliano Guzman
+8  A: 

def before_create self.hashed_password = User.hash_password(self.password) end

In this example, User.hash_password calls the hash_password method on the class User, whereas self.hashed_password= calls the hashed_password= method on this particular instance of User.

If you replace User.hash_password with self.hash_password, Ruby would complain with a NoMethodError, because no instance method by the name of hash_password exists in the class User. You could replace it with self.class.hash_password, though.

If you replace self.hashed_password= with simply hashed_password=, Ruby would create a local variable named hashed_password, rather than call the instance method hashed_password=. You need to explicitly add self if you want to call attribute writers.

The self in the method definition (def self.hash_password) makes hash_password a class method instead of an instance method. In this context, self refers to the class. In the context of an instance method, self refers to an instance.

molf
Because the last few lines defines def self.hash_password(password), I thought self.hash_password referred to this method. But you're saying self.hash_password would get a NoMethodError - even though the method definition says "self." I can't refer to it with "self." Confusing!
pez_dispenser
self.hashed_password= calls an attribute writer? But I didn't define an attribute writer. Was one automatically created?
pez_dispenser
self.hashed_password= is probably defined by ActiveRecord for you, if you have a database column named 'hashed_password'.
molf
"The self in the method definition (def self.hash_password) makes hash_password a class method instead of an instance method." So I was correct: it's a static method. And no rep love. :( :(
Randolpho
+4  A: 

You are asking the difference between a class method and an instance method.

There are a few ways of defining a class method:

class Klass
  def Klass.method_name
    ..
  end
end

which is the same as doing:

class Klass
  def self.method_name
    ..
  end
end

or the preferred ruby idiom:

class Klass
  class << self
    def method_name
      ..
    end
  end
end

If Klass is already declared you can also do..

def Klass.method_name
  ..
end

or:

class << Klass
  def method_name
    ..
  end
end

or you could even use Module#extend:

Klass.extend(Module.new { def method_name; puts 'ducky'; end })

Much like you would add a singleton method to an object. In fact class methods are singleton methods which operate on the class level.

In rails ActiveRecord for example you have a class method 'find' which you can use on any Model:

Person.find(1)

and instance methods like 'save' which operates on the individual object

person = Person.find(1)
...
person.save

In the current project I am working on I have a model Feed which contains data feeds. Periodically I need to run a method which updates all the feeds so i have an fetch_all method which accomplishes this.

class Feed < ActiveRecord::Base 

  // class method Feed.fetch_all
  def self.fetch_all
    Feed.all.each do |feed|
      feed.fetch_value
    end
  end

  // instance method
  def fetch_value
    // grabs updated value and saves 
  end
end
Corban Brook