I faced this issue while developing a feature.Lets say there is following code:
case 1:
module Person
module Employee
class Officer
def self.print_class(obj)
obj.is_a? Employee
end
end
end
end
case 2:
class Person::Employee::Officer
def self.print_class(obj)
puts obj.is_a? Employee
end
end
class Employee < ActiveRecord::Base
end
emp = Employee.last
and we have model Employee . Now
For case 1: Person::Employee::Officer.print_class(emp) gives "false"
For case 2: Person::Employee::Officer.print_class(emp) gives "true"
Why is this happening?
::
is the scope resolution operator. Unlike the class
and module
keywords it does not reopen the module and properly set the module nesting.
For example:
TEST = "I'm in the global scope"
module Foo
TEST = "I'm scoped to foo"
end
module Foo::Bar
def self.test
TEST
end
def self.nesting
Module.nesting
end
end
puts Foo::Bar.test # "I'm in the global scope"
puts Foo::Bar.nesting.inspect [Foo::Bar]
This is because the module nesting is resolved lexically at the point of definition. When you do module Foo::Bar
that module nesting is the global scope - when you reference TEST
it is not resolved to Foo::TEST
since Foo
is not in the module nesting.
In your case 2 Employee
is resolved to ::Employee
not Person::Employee
.
Therefore you should always explicitly nest classes and modules as it will set the correct module nesting and avoid these very unexpected module lookups.
TEST = "I'm in the global scope"
module Foo
TEST = "I'm scoped to foo"
module Bar
def self.test
TEST
end
def self.nesting
Module.nesting
end
end
end
puts Foo::Bar.test # "I'm scoped to foo"
puts Foo::Bar.nesting.inspect [Foo::Bar, Foo]