¿Qué es mattr_accessor en un módulo Rails?

107

Realmente no pude encontrar esto en la documentación de Rails, pero parece que 'mattr_accessor' es el corolario del módulo para 'attr_accessor' (getter & setter) en una clase Ruby normal .

P.ej. en una clase

class User
  attr_accessor :name

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

P.ej. en un módulo

module Authentication
  mattr_accessor :current_user

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

Este método auxiliar lo proporciona ActiveSupport .

JasonOng
fuente

Respuestas:

181

Rails extiende Ruby tanto con mattr_accessor(Módulo de acceso) como cattr_accessor(así como con _ reader/ _writerversiones). Como Ruby attr_accessorgenera métodos getter / setter para instancias , cattr/mattr_accessorproporcione métodos getter / setter a nivel de clase o módulo . Así:

module Config
  mattr_accessor :hostname
  mattr_accessor :admin_email
end

es la abreviatura de:

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

Ambas versiones le permiten acceder a las variables de nivel de módulo de esta manera:

>> Config.hostname = "example.com"
>> Config.admin_email = "[email protected]"
>> Config.hostname # => "example.com"
>> Config.admin_email # => "[email protected]"
Avdi
fuente
1
En sus ejemplos, explica que mattr_accessorsería una abreviatura de las variables de instancia de clase @variable, pero el código fuente parece revelar que en realidad están configurando / leyendo variables de clase. ¿Podría explicar esta diferencia?
sandre89
38

Aquí está la fuente de cattr_accessor

Y

Aquí está la fuente de mattr_accessor

Como puede ver, son prácticamente idénticos.

¿Por qué hay dos versiones diferentes? A veces, desea escribir cattr_accessoren un módulo, por lo que puede usarlo para información de configuración como menciona Avdi .
Sin embargo, cattr_accessorno funciona en un módulo, por lo que más o menos copiaron el código para que funcione también con los módulos.

Además, a veces es posible que desee escribir un método de clase en un módulo, de modo que siempre que una clase incluya el módulo, obtenga ese método de clase, así como todos los métodos de instancia. mattr_accessortambién te permite hacer esto.

Sin embargo, en el segundo escenario, su comportamiento es bastante extraño. Observe el siguiente código, particularmente tenga en cuenta los @@mattr_in_modulebits

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"
Orion Edwards
fuente
Este fue un problema que me mordió bastante al configurar default_url_options directamente (un mattr_accessor). Una vez que la clase los configuraría de una manera y otra los configuraría de una manera diferente, creando así enlaces inválidos.
Eric Davis
En la última versión de Rails cattr_*ahora son alias para mattr_*. Ver la cattr_accessorfuente
ouranos