Как я определял константы классом Модуля Ruby через отражение?

Я пытался получить "главу метапрограммирования" Языка программирования 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 возвратил бы массив символов вместо строк.

42
задан the Tin Man 2 August 2015 в 16:19
поделиться

2 ответа

Хороший вопрос!

Ваше замешательство связано с тем, что метод класса 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.

Правка : просто изменил официальную документацию, чтобы правильно отразить это ...

55
ответ дан 26 November 2019 в 23:54
поделиться

После ответа Марка мне пришлось ненадолго вернуться в пещеру своих мыслей. Поработал с другими фрагментами кода, а затем и с другими. Наконец, когда решение метода Ruby, казалось, имело смысл, записал его в блоге, чтобы я не забыл.

Обозначение: Если A " является собственным классом A

Когда вызывается A.constants , разрешение метода (см. Изображение в my сообщение в блоге для наглядного пособия) выполняет поиск следующих местоположений в порядке

  • MyClass ", Object" , BasicObject " (одноэлементные методы)
  • Класс (методы экземпляра)
  • Модуль (методы экземпляра)
  • Объект (методы экземпляра) и ядро ​​
  • BasicObject (методы экземпляра)

Ruby находит метод экземпляра Module # constants

Когда Module.константы , Ruby смотрит на

  • Module ", Object" , BasicObject " (одноэлементные методы)
  • Class (методы экземпляра )
  • Модуль (методы экземпляра)
  • Объект (методы экземпляра) и ядро ​​
  • BasicObject (методы экземпляра)

на этот раз Ruby находит метод одиночного элемента / класса в Модуль ".constants , как сказал Марк.

Модуль определяет одноэлементный метод, который скрывает метод экземпляра. Одноэлементный метод возвращает все известные константы, тогда как метод экземпляра возвращает константы, определенные в текущем классе и его предках.

3
ответ дан 26 November 2019 в 23:54
поделиться