I am working with a lot of pre-existing files, classes, and modules and trying to come up with better namespacing for the different components of the framework. I've been using modules as a way to namespace mainly because this seems like the standard convention (and being able to 'include' different parts of the framework could be useful).
The problem is that there was a ton of classes underneath the global namespace that should exist underneath a module. For example, let's say there is a class that was simply defined as:
class FirstClass
def meth
puts "HELLO"
end
end
But now I want to have this class within a module:
Using Double Colons:
module Foo; end
class Foo::FirstClass
def meth
puts 'HELLO'
end
end
Using Module Blocks:
module Foo
class FirstClass
def meth
puts 'HELLO'
end
end
Using double colons is a lot cleaner and also a lot easier to implement since I am changing many class definitions. Both of these ways work and I thought that they are both effectively the same thing, but evidently they are not. The double colon method seems to result in a different namespace within each class compared to the module block. For instance, with two classes underneath "Foo":
Using Module Blocks:
module Foo
class FirstClass
def meth
puts 'HELLO'
end
end
class SecondClass
def meth
FirstClass.new.meth
end
end
end
Foo::SecondClass.new.meth
Using Double Colons:
module Foo; end
class Foo::FirstClass
def meth
puts 'HELLO'
end
end
class Foo::SecondClass
def meth
FirstClass.new.meth
end
end
Foo::SecondClass.new.meth
The code works when using module blocks, but doesn't work with double colons. With the double colons, NameError is raised because it resolves FirstClass
as Foo::SecondClass::FirstClass
(instead of Foo::FirstClass
), which doesn't exist.
This can easily be solved by including Foo
in SecondClass
, but how come this isn't done by default?
Note: I'm using Ruby 2.1.5, which I know is outdated, but I get the same results on repl.it with ruby 2.5.5p157: https://repl.it/@joep2/Colon-vs-Block-Namespacing
It may seem counter-intuitive, but constant lookup in Ruby is done using current lexical scope , ie the current lexical nesting level (location in the source code), not the semantic nesting level.
This can be tested by inspecting Module.nesting
, which prints the current lexical scope:
class Foo::SecondClass
pp Module.nesting # -> [Foo::SecondClass]
end
module Foo
class SecondClass
pp Module.nesting # -> [Foo::SecondClass, Foo]
end
end
Since Ruby uses this nesting level for symbol lookup, it means in the situation where you try to look up FirstClass
within nesting [Foo::SecondClass]
, Ruby will not find it.
However when you try to look it up within nesting [Foo::SecondClass, Foo]
, it will find FirstClass
under Foo
, just like you expect.
To get around this, you could do:
class Foo::SecondClass
def meth
Foo::FirstClass.new.meth
end
end
Which will now work as you expect, since you provided the necessary lookup hint for FirstClass
, and told Ruby it is inside Foo
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.