2009-05-14 1 views
13

Я пытаюсь понять, когда использовать self.method_name и когда использовать Classname.method_name.Попытка понять использование self.method_name vs. Classname.method_name в Ruby

В приведенном ниже примере почему «before_create» необходимо ссылаться на «User.hash_password» вместо «self.hash_password» или просто «hash_password»?

Поскольку мы уже находимся в классе User, я думал, что метод before_create «знал бы», что «hash_password» является членом собственного класса и не нуждается в специальном синтаксисе для ссылки на него.

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 

ответ

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

В этом примере User.hash_password вызывает метод hash_password на классUser, тогда как self.hashed_password= вызывает метод hashed_password= на данном конкретном случае из User.

Если заменить User.hash_password с self.hash_password, Ruby будет жаловаться с NoMethodError, потому что ни один метод экземпляра по имени hash_password не существует в классе User. Однако вы можете заменить его на self.class.hash_password.

Если заменить self.hashed_password= с просто hashed_password=, Ruby бы создать локальную переменную с именем hashed_password, а не вызывать метод экземпляра hashed_password=. Вам нужно явно добавить self, если вы хотите вызвать авторов атрибутов.

В определении метода (def self.hash_password) hash_password метод класса вместо метода экземпляра. В этом контексте self относится к классу . В контексте метода экземпляра self ссылается на экземпляр .

+0

Поскольку последние несколько строк определяют def self.hash_password (пароль), я думал, что self.hash_password ссылается на этот метод. Но вы говорите, что self.hash_password получит NoMethodError - хотя определение метода говорит «я». Я не могу ссылаться на него с «я». Смешение! –

+0

self.hashed_password = вызывает автора атрибута? Но я не определил автора атрибута. Был ли он автоматически создан? –

+0

self.hashed_password =, вероятно, определен ActiveRecord для вас, если у вас есть столбец базы данных с именем hashed_password. – molf

1

Я еще вникать в Ruby, но из вашего описания я бы сказал, что hash_password статический метод или может быть использован статическим способом.

+3

это право. В ruby ​​статический метод называются методами класса –

12

Вы спрашиваете разницу между методом класса и методом экземпляра.

Есть несколько способов определения метода класса:

class Klass 
    def Klass.method_name 
    .. 
    end 
end 

, который так же, как делают:

class Klass 
    def self.method_name 
    .. 
    end 
end 

или предпочтительные рубиновые идиомы:

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

Если Класс уже объявлен, вы также можете сделать ..

def Klass.method_name 
    .. 
end 

или:

class << Klass 
    def method_name 
    .. 
    end 
end 

или вы можете даже использовать Модуль # удлиняет:

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

Так же, как вы бы добавить одноплодный метод к объекту. На самом деле методы класса - это одноэлементные методы, которые работают на уровне класса.

В Рельсы ActiveRecord, например, у вас есть метод класса «найти», которые вы можете использовать на любой модели:

Person.find(1) 

и экземпляра методы, такие как «сохранить», которая действует на отдельного объекта

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

В текущем проекте, над которым я работаю, у меня есть модель Feed, которая содержит фиды данных. Периодически мне нужно запустить метод, который обновляет все фиды, поэтому у меня есть метод fetch_all, который выполняет это.

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