Я пытался получить "главу метапрограммирования" Языка программирования Ruby Matz и Flanagan в голову, Однако я не мог понять вывод от следующего фрагмента кода, который я выдумал:
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. Как я получаю список констант, определенных классом Модуля?
Состояние документов
Module.constants
возвраты все константы определяются в системе. включая названия всех классов и методов
С тех пор A
наследовал его реализацию от Module.constants
, как это ведет себя по-другому в базовых и производных типах?
p A.class # => Class
p A.class.ancestors # => [Class, Module, Object, Kernel]
Примечание: Если Вы используете Ruby 1.9, constants
возвратил бы массив символов вместо строк.
Хороший вопрос!
Ваше замешательство связано с тем, что метод класса Module.constants
скрывает метод экземпляра Константы Module #
для 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 #
(метод экземпляра), в то время как 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.
Правка : просто изменил официальную документацию, чтобы правильно отразить это ...
После ответа Марка мне пришлось ненадолго вернуться в пещеру своих мыслей. Поработал с другими фрагментами кода, а затем и с другими. Наконец, когда решение метода Ruby, казалось, имело смысл, записал его в блоге, чтобы я не забыл.
Обозначение: Если A " является собственным классом A
Когда вызывается A.constants
, разрешение метода (см. Изображение в my сообщение в блоге для наглядного пособия) выполняет поиск следующих местоположений в порядке
MyClass "
, Object"
, BasicObject "
(одноэлементные методы) Класс
(методы экземпляра) Модуль
(методы экземпляра) Объект
(методы экземпляра) и ядро BasicObject
(методы экземпляра) Ruby находит метод экземпляра Module # constants
Когда Module.константы
, Ruby смотрит на
Module "
, Object"
, BasicObject "
(одноэлементные методы) Class
(методы экземпляра ) Модуль
(методы экземпляра) Объект
(методы экземпляра) и ядро BasicObject
(методы экземпляра) на этот раз Ruby находит метод одиночного элемента / класса в Модуль ".constants
, как сказал Марк.
Модуль определяет одноэлементный метод, который скрывает метод экземпляра. Одноэлементный метод возвращает все известные константы, тогда как метод экземпляра возвращает константы, определенные в текущем классе и его предках.