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
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 KernelBasicObject
(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 KernelBasicObject
(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.
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...