Возьмите следующий код в Ruby 3.3.0:
module A
def print
puts "A"
end
end
module B
def print
puts "B"
end
end
class C
end
c_instance = C.new
C.send(:include, A)
c_instance.print
C.send(:include, B)
c_instance.print
C.send(:include, A)
c_instance.print
Я ожидал, что он напечатает:
A
B
A
Но он распечатывает
A
B
B
Почему это?
Руби игнорирует второй звонок. Из документации Ruby :
Реализация Ruby по умолчанию заключается в добавлении констант, методов и переменных модуля этого модуля в mod, если этот модуль еще не был добавлен в mod или в один из его предков. См. также Модуль#include.
Это цитата из описания метода append_features
, который используется в методе include
.
Да, вполне логично, модуль уже находится внутри цепочки предков. Большое спасибо!
Возможно, это не ответит на ваш вопрос напрямую, но вы можете использовать ancestors
, чтобы проверить окончательную цепочку наследования.
C.ancestors
# => [C, B, A, Object, Kernel, BasicObject]
Существует множество способов реализации динамического метапрограммирования в Ruby. На этот раз вас смутил :include
, в следующий раз вас может смутить :prepend
или даже другие виды загадочного наследования. Вы даже можете использовать
c_instance.method(:print).source_location
что, черт возьми, внутри этого метода
Обратите внимание, что
C.send(:include, A)
можно упростить доC.include A
.