Что такое mattr_accessor в модуле Rails?

107

Я не смог найти это в документации Rails, но похоже mattr_accessor - это следствие модуля для attr_accessor (getter & setter) в обычном классе Ruby .

Например. в классе

class User
  attr_accessor :name

  def set_fullname
    @name = "#{self.first_name} #{self.last_name}"
  end
end

Например. в модуле

module Authentication
  mattr_accessor :current_user

  def login
    @current_user = session[:user_id] || nil
  end
end

Этот вспомогательный метод предоставляется ActiveSupport .

JasonOng
источник

Ответы:

181

Rails расширяет Ruby как mattr_accessor(модуль доступа), так и cattr_accessor(а также _ reader/ _writerверсии). Поскольку Ruby attr_accessorгенерирует методы получения / установки для экземпляров , cattr/mattr_accessorпредоставьте методы получения / установки на уровне класса или модуля . Таким образом:

module Config
  mattr_accessor :hostname
  mattr_accessor :admin_email
end

это сокращение от:

module Config
  def self.hostname
    @hostname
  end
  def self.hostname=(hostname)
    @hostname = hostname
  end
  def self.admin_email
    @admin_email
  end
  def self.admin_email=(admin_email)
    @admin_email = admin_email
  end
end

Обе версии позволяют получить доступ к переменным уровня модуля следующим образом:

>> Config.hostname = "example.com"
>> Config.admin_email = "admin@example.com"
>> Config.hostname # => "example.com"
>> Config.admin_email # => "admin@example.com"
Авди
источник
1
В ваших примерах вы объясняете, что это mattr_accessorбыло бы сокращением для переменных (переменных @variable) экземпляра класса , но исходный код, кажется, показывает, что они на самом деле устанавливают / читают переменные класса. Не могли бы вы объяснить эту разницу?
sandre89
38

Вот источник для cattr_accessor

И

Вот источник для mattr_accessor

Как видите, они практически идентичны.

А почему существуют две разные версии? Иногда вы хотите написать cattr_accessorв модуле, чтобы вы могли использовать его для информации о конфигурации, как упоминает Avdi .
Однако cattr_accessorэто не работает в модуле, поэтому они более или менее скопировали код, чтобы работать и с модулями.

Кроме того, иногда вам может потребоваться написать метод класса в модуле, чтобы всякий раз, когда какой-либо класс включает модуль, он получал этот метод класса, а также все методы экземпляра. mattr_accessorтакже позволяет вам это делать.

Однако во втором сценарии это поведение довольно странное. Обратите внимание на следующий код, особенно на @@mattr_in_moduleбиты

module MyModule
  mattr_accessor :mattr_in_module
end

class MyClass
  include MyModule
  def self.get_mattr; @@mattr_in_module; end # directly access the class variable
end

MyModule.mattr_in_module = 'foo' # set it on the module
=> "foo"

MyClass.get_mattr # get it out of the class
=> "foo"

class SecondClass
  include MyModule
  def self.get_mattr; @@mattr_in_module; end # again directly access the class variable in a different class
end

SecondClass.get_mattr # get it out of the OTHER class
=> "foo"
Орион Эдвардс
источник
Это была ошибка, которая меня очень сильно укусила при непосредственной установке default_url_options (mattr_accessor). Однажды класс установил бы их одним способом, а другой установил бы их другим, таким образом создавая недействительные ссылки.
Эрик Дэвис,
В последней версии Rails cattr_*теперь есть псевдонимы для mattr_*. См. cattr_accessorИсточник
ouranos