当以不同的方式编写时,范围解析在 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,但持续查找将到达顶级上下文。

:: 是范围解析运算符。与 classmodule 关键字不同,它不会重新打开模块并正确设置模块嵌套。

例如:

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]