How do I get constants defined by Ruby's Module class via reflection?

前端 未结 2 837
时光说笑
时光说笑 2020-12-24 01:31

I was trying to get Matz and Flanagan\'s \"Ruby Programming Language\" metaprogramming chapter into my head, However I couldn\'t understand the output from the following cod

相关标签:
2条回答
  • 2020-12-24 01:54

    I had to go back into my thinking cave for a while after Marc's response. Tinkered with more code snippets and then some more. Finally when Ruby's method resolution seemed to make sense wrote it down as a blog post so that I don't forget.

    Notation: If A" is the eigenclass of A

    When A.constants is called, method resolution (refer to the image in my blog post to have a visual aid) looks up the following locations in order

    • MyClass", Object", BasicObject" (singleton methods)
    • Class (instance methods)
    • Module (instance methods)
    • Object (instance methods) and Kernel
    • BasicObject (instance methods)

    Ruby finds the instance method Module#constants

    When Module.constants is called, Ruby looks at

    • Module", Object", BasicObject" (singleton methods)
    • Class (instance methods)
    • Module (instance methods)
    • Object (instance methods) and Kernel
    • BasicObject (instance methods)

    this time, Ruby finds the singleton/class method at Module".constants as Marc said.

    Module defines a singleton method which shadows the instance method. The singleton method returns all known constants whereas the instance method returns the constants defined in current class and its ancestors.

    0 讨论(0)
  • 2020-12-24 02:06

    Good question!

    Your confusion is due to the fact that the class method Module.constants hides the instance method Module#constants for Module.

    In Ruby 1.9, this has been addressed by adding an optional parameter:

    # 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).
    

    In your example above, A.constants calls Module#constants (the instance method), while Module.constants calls, well, Module.constants.

    In Ruby 1.9, you thus want to call Module.constants(true).

    In Ruby 1.8, it is possible to call the instance method #constants on Module. You need to get the instance method and bind it as a class method (using a different name):

    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
    

    I wish I was able to backport the 1.9 functionality completely to 1.8 for my backports gem, but I can't think of a way to get only the constants of a Module, excluding the inherited ones, in Ruby 1.8.

    Edit: Just changed the official documentation to correctly reflect this...

    0 讨论(0)
提交回复
热议问题