2009-05-14 10 views
13

Próbuję zrozumieć, kiedy użyć self.method_name vs. kiedy używać Classname.method_name.Próba zrozumienia użycia self.method_name vs. Classname.method_name w Ruby

W poniższym przykładzie, dlaczego "before_create" musi odnosić się do "User.hash_password" zamiast "self.hash_password" lub po prostu "hash_password"?

Ponieważ jesteśmy już w klasie User, myślałem, że metoda before_create "wie", że "hash_password" jest członkiem własnej klasy i nie potrzebuje specjalnej składni, aby się do niej odwoływać.

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 

Odpowiedz

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

W tym przykładzie User.hash_password wywołuje metodę hash_password NA klasyUser, natomiast self.hashed_password= wywołuje metodę hashed_password= na tym konkretnym przypadku z User.

Jeśli zastąpić User.hash_password z self.hash_password, Ruby będzie narzekać z NoMethodError, ponieważ żadna metoda instancji o nazwie hash_password istnieje w klasie User. Można go jednak zamienić na self.class.hash_password.

Jeśli zastąpić self.hashed_password= ze po prostu hashed_password=, Ruby byłoby utworzyć lokalną zmienną o nazwie hashed_password, zamiast wywołać metodę instancji hashed_password=. Musisz wyraźnie dodać self, jeśli chcesz wywoływać twórców atrybutów.

Parametr self w definicji metody (def self.hash_password) tworzy metodę klasy zamiast metody instancji. W tym kontekście self odnosi się do klasy . W kontekście metody instancji self odnosi się do instancji .

+0

Ponieważ w ostatnich kilku wierszach definiuje się def self.hash_hasword (hasło), pomyślałem, że self.hash_password odnosi się do tej metody. Ale mówisz, że self.hash_password dostanie NoMethodError - nawet jeśli definicja metody mówi "self". Nie mogę nazywać tego "własnym". Mylące! –

+0

self.hashed_password = wywołuje pisarz atrybutów? Ale nie zdefiniowałem pisarza atrybutów. Czy został utworzony automatycznie? –

+0

self.hashed_password = jest prawdopodobnie zdefiniowany przez ActiveRecord dla ciebie, jeśli masz kolumnę bazy danych o nazwie "hashed_password". – molf

1

Jeszcze muszę zagłębić się Ruby, ale z opisu Powiedziałbym, że hash_password jest metoda statyczna lub może być stosowany w sposób statyczny.

+3

To prawda. W ruby, metoda statyczna nazywa się metodami klasy –

12

Pytasz o różnicę między metodą klasy i metodą instancji.

Istnieje kilka sposobów definiowania metody klasy:

class Klass 
    def Klass.method_name 
    .. 
    end 
end 

która jest taka sama jak robi:

class Klass 
    def self.method_name 
    .. 
    end 
end 

lub preferowany rubinowy idiom:

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

Jeśli Klass jest już zadeklarowany, możesz też zrobić ...

def Klass.method_name 
    .. 
end 

czyli

class << Klass 
    def method_name 
    .. 
    end 
end 

lub można nawet użyć modułu # przedłużyć:

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

Podobnie jak należy dodać metodę singleton do obiektu. W rzeczywistości metody klasowe są metodami singleton, które działają na poziomie klasy.

w szynach ActiveRecord na przykład masz metody klasy „Znajdź”, które można wykorzystać na dowolny model:

Person.find(1) 

i instancji metody jak „zaoszczędzić”, która działa na pojedynczego obiektu

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

W bieżącym projekcie, nad którym pracuję, mam model kanału, który zawiera pliki danych. Okresowo muszę uruchomić metodę, która aktualizuje wszystkie kanały, więc mam metodę fetch_all, która to umożliwia.

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 
Powiązane problemy