Qu'est-ce que mattr_accessor dans un module Rails?

107

Je n'ai pas vraiment trouvé cela dans la documentation de Rails mais il semble que «mattr_accessor» soit le corollaire du module pour «attr_accessor» (getter & setter) dans une classe Ruby normale .

Par exemple. dans une classe

class User
  attr_accessor :name

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

Par exemple. dans un module

module Authentication
  mattr_accessor :current_user

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

Cette méthode d'assistance est fournie par ActiveSupport .

JasonOng
la source

Réponses:

181

Rails étend Ruby avec à la fois mattr_accessor(Accesseur de module) et cattr_accessor(ainsi que _ reader/ _writerversions). Lorsque Ruby attr_accessorgénère des méthodes getter / setter pour les instances , cattr/mattr_accessorfournissez des méthodes getter / setter au niveau de la classe ou du module . Donc:

module Config
  mattr_accessor :hostname
  mattr_accessor :admin_email
end

est l'abréviation 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

Les deux versions vous permettent d'accéder aux variables au niveau du module comme ceci:

>> Config.hostname = "example.com"
>> Config.admin_email = "[email protected]"
>> Config.hostname # => "example.com"
>> Config.admin_email # => "[email protected]"
Avdi
la source
1
Dans vos exemples, vous expliquez que ce mattr_accessorserait court pour les variables d'instance de classe @variable, mais le code source semble révéler qu'ils définissent / lisent en fait des variables de classe. Pouvez-vous expliquer cette différence?
sandre89
38

Voici la source de cattr_accessor

Et

Voici la source de mattr_accessor

Comme vous pouvez le voir, ils sont à peu près identiques.

Quant à savoir pourquoi il existe deux versions différentes? Parfois, vous voulez écrire cattr_accessordans un module, vous pouvez donc l'utiliser pour des informations de configuration comme les mentionne Avdi .
Cependant, cattr_accessorne fonctionne pas dans un module, ils ont donc plus ou moins copié le code pour travailler également pour les modules.

De plus, vous pouvez parfois vouloir écrire une méthode de classe dans un module, de sorte que chaque fois qu'une classe inclut le module, elle obtient cette méthode de classe ainsi que toutes les méthodes d'instance. mattr_accessorvous permet également de le faire.

Cependant, dans le deuxième scénario, son comportement est assez étrange. Observez le code suivant, notez en particulier les @@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
la source
C'était un piège qui m'a mordu assez dur lors de la définition directe de default_url_options (un mattr_accessor). Une fois, la classe les définirait d'une manière et une autre les définirait d'une manière différente, créant ainsi des liens invalides.
Eric Davis
Dans la dernière version de Rails cattr_*sont désormais des alias pour mattr_*. Voir la cattr_accessorsource
ouranos