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

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

В приведенном ниже примере, почему "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

Ответы

Ответ 1

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, потому что в классе User не существует метода экземпляра с именем hash_password. Однако вы можете заменить его на self.class.hash_password.

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

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

Ответ 2

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

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

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

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

def Klass.method_name
  ..
end

или:

class << Klass
  def method_name
    ..
  end
end

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

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

Похоже, вы добавили метод singleton к объекту. На самом деле методы класса - это одноэлементные методы, которые работают на уровне класса.

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

Person.find(1) 

и методы экземпляра, такие как "save", который работает с отдельным объектом

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

Ответ 3

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