Co to jest mattr_accessor w module Rails?

107

Nie mogłem znaleźć tego w dokumentacji Railsów, ale wygląda na to, że 'mattr_accessor' jest następstwem modułu 'attr_accessor' (getter & setter) w normalnej klasie Ruby .

Na przykład. w klasie

class User
  attr_accessor :name

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

Na przykład. w module

module Authentication
  mattr_accessor :current_user

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

Ta metoda pomocnicza jest udostępniana przez ActiveSupport .

JasonOng
źródło

Odpowiedzi:

181

Railsy rozszerzają Rubiego zarówno o mattr_accessor(Accessor modułu), jak i cattr_accessor(jak również _ reader/ _writerwersje). Ponieważ Ruby attr_accessorgeneruje metody pobierające / ustawiające dla instancji , należy cattr/mattr_accessorzapewnić metody pobierające / ustawiające na poziomie klasy lub modułu . A zatem:

module Config
  mattr_accessor :hostname
  mattr_accessor :admin_email
end

jest skrótem od:

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

Obie wersje umożliwiają dostęp do zmiennych na poziomie modułu, takich jak:

>> Config.hostname = "example.com"
>> Config.admin_email = "[email protected]"
>> Config.hostname # => "example.com"
>> Config.admin_email # => "[email protected]"
Avdi
źródło
1
W swoich przykładach wyjaśniasz, że mattr_accessorbyłoby to skrótem od zmiennych instancji klasy @variable, ale kod źródłowy wydaje się ujawniać, że w rzeczywistości ustawiają / odczytują zmienne klas. Czy mógłbyś wyjaśnić tę różnicę?
sandre89
38

Oto źródło cattr_accessor

I

Oto źródło mattr_accessor

Jak widać, są prawie identyczne.

Dlaczego istnieją dwie różne wersje? Czasami chcesz pisać cattr_accessorw module, więc możesz go użyć do informacji konfiguracyjnych, takich jak wspomina Avdi .
Jednak cattr_accessornie działa w module, więc mniej więcej skopiowali kod, aby również pracować dla modułów.

Ponadto czasami możesz chcieć napisać metodę klasy w module, tak że za każdym razem, gdy jakakolwiek klasa zawiera moduł, pobiera tę metodę klasy, jak również wszystkie metody instancji. mattr_accessorrównież na to pozwala.

Jednak w drugim scenariuszu jego zachowanie jest dość dziwne. Przestrzegaj poniższego kodu, szczególnie zwracaj uwagę na @@mattr_in_modulebity

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
źródło
To była pułapka, która dość mocno mnie ugryzła podczas bezpośredniego ustawiania default_url_options (mattr_accessor). Kiedyś klasa ustawiłaby je w jeden sposób, a inna w inny, tworząc w ten sposób nieprawidłowe linki.
Eric Davis,
W najnowszej wersji Railsów cattr_*są teraz aliasy dla mattr_*. Zobacz cattr_accessorźródło
ouranos