Typically, in Ruby, we see modules and classes nested by having one declaration inside of the the other.
1 2 3 4 |
module Foo class Bar end end |
When the outer class or module has previously been declared, however, we can also declare the nested one directly using its fully qualified name, and it is not uncommon to see that done.
1 2 3 4 |
module Foo class Foo::Bar end |
On the surface, these seem to be 2 ways to do exactly the same thing, and for the most part they are. They are not doing exactly the same thing, however, and that can result in some interesting side effects. We start to see those effects when methods methods defined within our declarations make reference to constants.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
X = 1 Y = 11 module Foo X = 2 Y = 22 class Bar def x ; X ; end end end class Foo::Bar def y ; Y ; end end bar = Foo::Bar.new puts bar.x, bar.y # Output: # 2 # 11 |
As you can see, each of the instance methods of Foo::Bar is accessing constants from a different level of nesting.
What’s going on is that the nesting with respect to an executing method belongs to the method itself and not the class or module on which it is defined. The source of that nesting is nesting of class and module code blocks at the point where the method was defined.
The side effects of this can get positively weird.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
module Foo ; end class Foo::Bar def self.hello puts 'hello' end def hello Bar.hello end end Foo::Bar.new.hello # Results in exception: # NameError: uninitialized constant Foo::Bar::Bar |
Say what? The hello instance method cannot even reference its own class using an unqualified name? That is because the Bar constant belongs to the Foo module, but since the method was not defined within a module Foo block, its constants (including Bar ) are not implicitly available.
Recent Comments