Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 code snippet that I dreamed up:

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"] 

The book states that the class method constants returns the list of constants for the class (as you can see in the output for A.constants). I was trying to get the list of constants defined for the Module class when I came across the above strange behavior.

A's constants show up in Module.constants. How do I get the list of constants defined by the Module class?

The docs state

Module.constants returns all constants defined in the system. including names of all classes and methods

Since A inherits its implementation from Module.constants, how does it behave differently in the base and derived types?

p A.class               # => Class p A.class.ancestors       # => [Class, Module, Object, Kernel] 

Note: If you're using Ruby 1.9, constants would return an array of symbols instead of strings.

like image 622
Gishu Avatar asked Feb 22 '10 06:02

Gishu


People also ask

How do you find the constants in Ruby?

Ruby Constants Constants begin with an uppercase letter. Constants defined within a class or module can be accessed from within that class or module, and those defined outside a class or module can be accessed globally. Constants may not be defined within methods.

How do you define a module in Ruby?

To define a module, use the module keyword, give it a name and then finish with an end . The module name follows the same rules as class names. The name is a constant and should start with a capital letter. If the module is two words it should be camel case (e.g MyModule).

How do I use a class module in Ruby?

A user cannot access instance method directly with the use of the dot operator as he cannot make the instance of the module. To access the instance method defined inside the module, the user has to include the module inside a class and then use the class instance to access that method.

How to list any constant in Ruby into categories?

We can list any constant in Ruby into categories on is constant defined inside the class which can be accessed outside of class with the :: operator and another is the constant which we have defined at the top of the program which can be accessed by any class and method.

What is a constant in Ruby?

What is a constant in Ruby? A constant is a type of variable which always starts with a capital letter. They can only be defined outside of methods, unless you use metaprogramming.

Why does Ruby start with the object class?

If no class is open in the current scope, Ruby starts with the Object class. The context defined by where you are in the code allows local variables to be defined within a block without affecting variables outside of the block, for example.

Why is my ruby class uppercase?

One important point to understand this error is that Ruby classes are constants. Examples: They are constants because the first letter is uppercase. When you define a class, what you’re really doing is creating a Class object, which is assigned to a constant.


2 Answers

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

like image 117
Marc-André Lafortune Avatar answered Oct 01 '22 17:10

Marc-André Lafortune


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.

like image 35
Gishu Avatar answered Oct 01 '22 17:10

Gishu