当以不同的方式编写时,范围解析在 ruby 中的工作方式不同
Scope resolution works differently in ruby when written in different ways
我在开发 feature.Lets 时遇到了这个问题,说有以下代码:
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
我们有模型 Employee 。
现在
案例一:
Person::Employee::Officer.print_class(emp) 给出“假”
对于案例 2:
Person::Employee::Officer.print_class(emp) 给出“真”
为什么会这样?
首先我会试着弄清楚什么是上下文
class 方法 Emplyee::print_class
已定义:
# 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
现在我们可以考虑方法Emplyee::print_class
定义和执行了。
当您定义方法 Emplyee::print_class
时,您使用了常量 Employee
:
何时以及在何种上下文中将此常量计算为 class 或模块或字符串?
def self.print_class(obj)
obj.is_a? Employee
end
when的答案是:方法执行的时候。
在哪个上下文中的答案是:你定义方法的上下文,而不是你执行它的上下文。因为 Constant 是方法定义的外部范围(如果你试图在方法内部创建一个常量,你会得到一个错误'dynamic constant assignment')。
在您的示例案例 1 中,上下文将为 context_a。
在您的示例案例 2 中,上下文将是 context_b,但持续查找将到达顶级上下文。
::
是范围解析运算符。与 class
和 module
关键字不同,它不会重新打开模块并正确设置模块嵌套。
例如:
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]
这是因为模块嵌套是在定义时按词法解析的。当您执行 module Foo::Bar
时,模块嵌套是全局范围 - 当您引用 TEST
时,它不会解析为 Foo::TEST
,因为 Foo
不在模块嵌套中。
在你的情况下,2 Employee
被解析为 ::Employee
而不是 Person::Employee
。
因此,您应该始终显式嵌套 类 和模块,因为它将设置正确的模块嵌套并避免这些非常意外的模块查找。
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]
我在开发 feature.Lets 时遇到了这个问题,说有以下代码:
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
我们有模型 Employee 。 现在
案例一: Person::Employee::Officer.print_class(emp) 给出“假”
对于案例 2: Person::Employee::Officer.print_class(emp) 给出“真”
为什么会这样?
首先我会试着弄清楚什么是上下文
class 方法 Emplyee::print_class
已定义:
# 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
现在我们可以考虑方法Emplyee::print_class
定义和执行了。
当您定义方法 Emplyee::print_class
时,您使用了常量 Employee
:
何时以及在何种上下文中将此常量计算为 class 或模块或字符串?
def self.print_class(obj)
obj.is_a? Employee
end
when的答案是:方法执行的时候。
在哪个上下文中的答案是:你定义方法的上下文,而不是你执行它的上下文。因为 Constant 是方法定义的外部范围(如果你试图在方法内部创建一个常量,你会得到一个错误'dynamic constant assignment')。
在您的示例案例 1 中,上下文将为 context_a。
在您的示例案例 2 中,上下文将是 context_b,但持续查找将到达顶级上下文。
::
是范围解析运算符。与 class
和 module
关键字不同,它不会重新打开模块并正确设置模块嵌套。
例如:
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]
这是因为模块嵌套是在定义时按词法解析的。当您执行 module Foo::Bar
时,模块嵌套是全局范围 - 当您引用 TEST
时,它不会解析为 Foo::TEST
,因为 Foo
不在模块嵌套中。
在你的情况下,2 Employee
被解析为 ::Employee
而不是 Person::Employee
。
因此,您应该始终显式嵌套 类 和模块,因为它将设置正确的模块嵌套并避免这些非常意外的模块查找。
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]