O que é mattr_accessor em um módulo Rails?

107

Eu realmente não consegui encontrar isso na documentação do Rails, mas parece que 'mattr_accessor' é o corolário do Módulo para 'attr_accessor' (getter & setter) em uma classe Ruby normal .

Por exemplo. em uma aula

class User
  attr_accessor :name

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

Por exemplo. em um módulo

module Authentication
  mattr_accessor :current_user

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

Este método auxiliar é fornecido pelo ActiveSupport .

JasonOng
fonte

Respostas:

181

Rails estende Ruby com ambos mattr_accessor(acessador de módulo) e cattr_accessor(bem como _ reader/ _writerversões). Como Ruby attr_accessorgera métodos getter / setter para instâncias , cattr/mattr_accessorforneça métodos getter / setter no nível de classe ou módulo . Portanto:

module Config
  mattr_accessor :hostname
  mattr_accessor :admin_email
end

é a abreviação 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 as versões permitem que você acesse as variáveis ​​de nível de módulo da seguinte forma:

>> Config.hostname = "example.com"
>> Config.admin_email = "[email protected]"
>> Config.hostname # => "example.com"
>> Config.admin_email # => "[email protected]"
Avdi
fonte
1
Em seus exemplos, você explica que mattr_accessorseria uma abreviação de variáveis ​​de instância de classe @variable, mas o código-fonte parece revelar que eles estão, na verdade, configurando / lendo variáveis ​​de classe. Você poderia explicar essa diferença?
sandre89
38

Aqui está a fonte para cattr_accessor

E

Aqui está a fonte para mattr_accessor

Como você pode ver, eles são praticamente idênticos.

Por que existem duas versões diferentes? Às vezes você deseja escrever cattr_accessorem um módulo, então você pode usá-lo para informações de configuração, como o Avdi menciona .
No entanto, cattr_accessornão funciona em um módulo, então eles mais ou menos copiaram o código para funcionar também em módulos.

Além disso, às vezes você pode querer escrever um método de classe em um módulo, de forma que sempre que qualquer classe incluir o módulo, ela obtenha esse método de classe, bem como todos os métodos de instância. mattr_accessortambém permite que você faça isso.

Porém, no segundo cenário, seu comportamento é bastante estranho. Observe o código a seguir, especialmente os @@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
fonte
Este foi um problema que me incomodou bastante ao definir default_url_options diretamente (um mattr_accessor). Uma vez que a classe os configuraria de uma maneira e outra os configuraria de uma maneira diferente, criando assim links inválidos.
Eric Davis
Na última versão do Rails cattr_*agora são apelidos para mattr_*. Veja a cattr_accessorfonte
ouranos