Reputation: 73
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?
Upvotes: 7
Views: 476
Reputation: 857
At first I will try to make clearer what is the context in which
the class method Emplyee::print_class
is defined:
# top-level context
class Employee
end
module Person
module Employee
class Officer
# context_a the context of case 1
## here the constant Employee is a Module
p (Employee.class) # -> Module
# ## If you want a reference to a constant defined at the top-level(the Employee class)
# # you may preceded it with ::
p ::Employee.class # -> class
p ::Employee == Employee # -> false
end
end
end
class Person::Employee::Officer
#context_b the context of case 2
# here The constant Employee is the class Employee
# There are not Employee Constant defined in this context
# the constant look up will reach the top-level context
# and Employee will reference to ::Employee
p (Employee.class) # -> Class
p ::Employee == Employee # -> true
end
Now we can take into consideration the method Emplyee::print_class
definition and execution.
When you defined the method Emplyee::print_class
you used the constant Employee
:
When and in which context this constant is evaluated to be a class or a module or a string?
def self.print_class(obj)
obj.is_a? Employee
end
The answer to when is: when the method is executed.
The answer to in which context is: the context where you defined the method, not the one you execute it. For the Constant is the outer scope of the method definition (If you try to create a constant inside the method you will get an error 'dynamic constant assignment').
In your example case 1 the context will be context_a.
In your example case 2 the context will be context_b, but the constant look-up will reach the top-level context.
Upvotes: 0
Reputation: 101811
::
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]
Upvotes: 1