Если у вас есть set_my_name_var
метод в модуле A
, выполняющий @@my_name = 'A'
, это устанавливает переменную модуля в A
. Это поведение не меняется, когда метод вызывается через включающий класс. Это также приводит к другому факту, который иногда привлекает внимание людей - если вы включаете A
в несколько классов, то существует только один экземпляр @@my_name
, , а не один экземпляр на каждый включающий класс. Следующий пример иллюстрирует это:
module Example
def name=(name)
@@name = name
end
def name
@@name
end
end
class First
include Example
end
class Second
include Example
end
irb(main):066:0> f = First.new
=> #<First:0x2d4b80c>
irb(main):067:0> s = Second.new
=> #<Second:0x2d491d8>
irb(main):068:0> f.name = 'Set via f'
=> "Set via f"
irb(main):069:0> s.name
=> "Set via f"
Обновление
Думаю, я понял, что происходит, и это объяснит, почему это не работает так, как вы ожидаете. cattr_reader
(и, соответственно, cattr_accessor
) содержит следующее:
class_eval(<<-EOS, __FILE__, __LINE__)
unless defined? @@#{sym} # unless defined? @@hair_colors
@@#{sym} = nil # @@hair_colors = nil
end
# code to define reader method follows...
Происходит следующая последовательность:
B
определяется
- модуль
A
включен
- обратный вызов
included
делает klass.send(:cattr_accessor, :my_name)
.
- и
@@my_name
создается в классе B
, для которого установлено nil
.
Без cattr_accessor
, тогда после вызова set_my_name_var
, когда вы говорите @@my_name
в B
, это будет означать переменную модуля. Но с помощью cattr_accessor
в классе теперь существует переменная с тем же именем, поэтому, если мы скажем @@my_name
в B
, мы получим значение переменной B
вместо A
. Это то, что я имел в виду под маскировкой. (Переменная B
мешает нам видеть A
)
Может быть, следующее проиллюстрирует. Представьте, что мы дошли до вашего b = B.new
, и мы делаем следующее:
>> A.class_variables
=> [] # No methods called on A yet so no module variables initialised
>> B.class_variables
=> ["@@my_other_name", "@@my_name"] # these exist and both set to nil by cattr_accessor
>> B.send(:class_variable_get, '@@my_name')
=> nil # B's @@my_name is set to nil
>> b.set_my_name_var # we call set_my_name_var as you did in the question
=> "A"
>> A.send(:class_variable_get, '@@my_name')
=> "A" # the variable in the module is to to 'A' as you expect
>> B.send(:class_variable_get, '@@my_name')
=> nil # but the variable in the class is set to nil
>> B.my_name
=> nil # B.my_name accessor has returned the variable from the class i.e. nil
Я думаю, cattr_reader
делает это, чтобы избежать ошибок uninitialized class variable
, если вы пытаетесь использовать метод получения перед установщиком. (переменные класса по умолчанию не равны nil
так же, как переменные экземпляра.)