Как получить константы, определенные классом Ruby Module через отражение?
Я пытался получить главу метапрограммирования Matz и Flanagan "Язык программирования Ruby" в моей голове. Однако я не мог понять результат из следующего фрагмента кода, о котором я мечтал:
p Module.constants.length # => 88
$snapshot1 = Module.constants
class A
NAME=:abc
$snapshot2 = Module.constants
p $snapshot2.length # => 90
p $snapshot2 - $snapshot1 # => ["A", "NAME"]
end
p Module.constants.length # => 89
p Module.constants - $snapshot1 # => ["A"]
p A.constants # => ["NAME"]
В книге указано, что метод класса constants
возвращает список констант для класса (как вы можете видеть на выходе для A.constants
).
Я пытался получить список констант, определенных для класса модуля, когда я столкнулся с этим странным поведением.
A
константы отображаются в Module.constants. Как получить список констант, определенных классом модуля?
docs state
Module.constants
возвращает все константы, определенные в системе. включая имена всех классов и методов
Так как A
наследует свою реализацию от Module.constants
, как он ведет себя по-разному в базовом и производном типах?
p A.class # => Class
p A.class.ancestors # => [Class, Module, Object, Kernel]
Примечание. Если вы используете Ruby 1.9, constants
вернет массив символов вместо строк.
Ответы
Ответ 1
Хороший вопрос!
Ваше замешательство связано с тем, что метод класса Module.constants
скрывает метод экземпляра Module#constants
для Module
.
В Ruby 1.9 это было устранено добавлением необязательного параметра:
# No argument: same class method as in 1.8:
Module.constants # ==> All constants
# One argument: uses the instance method:
Module.constants(true) # ==> Constants of Module (and included modules)
Module.constants(false) # ==> Constants of Module (only).
В приведенном выше примере A.constants
вызывает Module#constants
(метод экземпляра), а Module.constants
вызывает, ну, Module.constants
.
В Ruby 1.9 вам нужно позвонить Module.constants(true)
.
В Ruby 1.8 можно вызвать метод экземпляра #constants
на Module
. Вам нужно получить метод экземпляра и связать его как метод класса (используя другое имя):
class << Module
define_method :constants_of_module, Module.instance_method(:constants)
end
# Now use this new class method:
class Module
COOL = 42
end
Module.constants.include?("COOL") # ==> false, as you mention
Module.constants_of_module # ==> ["COOL"], the result you want
Мне жаль, что я не смог полностью сохранить функциональность 1.9 до 1.8 для моего драгоценного камня backports
, но я не могу придумать способ получить только константы модуля, исключая унаследованные, в Ruby 1.8.
Изменить: просто изменил официальную документацию, чтобы правильно отразить это...
Ответ 2
Мне пришлось вернуться в мою мыслящую пещеру некоторое время после ответа Марка. Переработаны дополнительные фрагменты кода, а затем еще несколько. Наконец, когда решение Ruby-метода показало смысл, оно записано в виде сообщения в блоге, чтобы я не забыл.
Обозначение: если A " является eigenclass A
Когда вызывается A.constants
, разрешение метода (см. изображение в мой пост в блоге, чтобы иметь наглядную помощь), смотрит вверх в следующих местах:
-
MyClass"
, Object"
, BasicObject"
(методы однопользовательского режима)
-
Class
(методы экземпляра)
-
Module
(методы экземпляра)
-
Object
(методы экземпляра) и Kernel
-
BasicObject
(методы экземпляра)
Ruby находит метод экземпляра Module#constants
Когда вызывается Module.constants
, Ruby смотрит на
-
Module"
, Object"
, BasicObject"
(методы Singleton)
-
Class
(методы экземпляра)
-
Module
(методы экземпляра)
-
Object
(методы экземпляра) и Kernel
-
BasicObject
(методы экземпляра)
на этот раз Ruby находит метод singleton/class в Module".constants
, как сказал Марк.
Модуль определяет одноэлементный метод, который затеняет метод экземпляра. Метод singleton возвращает все известные константы, тогда как метод экземпляра возвращает константы, определенные в текущем классе и его предках.