Ruby on rails Ruby:使用class_eval定义常量只能通过const_get找到,而不能直接通过::lookup找到

Ruby on rails Ruby:使用class_eval定义常量只能通过const_get找到,而不能直接通过::lookup找到,ruby-on-rails,ruby,Ruby On Rails,Ruby,给定一个用户类: 类用户 结束 我想使用.class\u eval定义一个新常量。因此: User.class_eval{AVOCADO='fruit'} 如果我试图通过User::AVOCADO访问它,我会得到未初始化的常量User::AVOCADO,但是User.const\u get(:AVOCADO)有效。为什么? 如果我在included方法中在Rails关注点中定义了一个常量,并将该关注点包含在User类中,我可以通过常规的:查找来访问它。例如: module关注点 扩展Ac

给定一个用户类:

类用户
结束
我想使用
.class\u eval
定义一个新常量。因此:

User.class_eval{AVOCADO='fruit'}
  • 如果我试图通过
    User::AVOCADO
    访问它,我会得到
    未初始化的常量User::AVOCADO
    ,但是
    User.const\u get(:AVOCADO)
    有效。为什么?

  • 如果我在
    included
    方法中在Rails关注点中定义了一个常量,并将该关注点包含在
    User
    类中,我可以通过常规的
    查找来访问它。例如:

  • module关注点
    扩展ActiveSupport::关注点
    包括做
    鳄梨=‘水果’
    结束
    结束
    类用户
    包括水果关切
    结束
    用户:鳄梨
    =>“水果”
    
    但是,在查找ActiveSupport::Concern时,
    included
    只将该块存储在一个实例变量(
    @\u included\u block
    )中,然后它对
    append\u功能的覆盖将调用
    base.class\u eval(&@\u included\u block)

    那么,如果它只是用同一个块调用
    User.class_eval
    ,为什么
    User::AVOCADO
    included
    块中定义常量时有效,而在我直接调用
    User.class_eval{AVOCADO='fruit'}
    时无效

  • 奇怪的是(请参阅),在执行
    User.class_eval{AVOCADO='teste'}
    时,Ruby似乎也将常量泄漏到了顶层。因此:
  • User.const\u get(:鳄梨)
    =>“水果”
    鳄梨
    =>“水果”
    

    我知道块的作用域是平坦的,但常数事先没有定义,我希望
    class_eval
    self
    默认定义对象更改为接收方。这是怎么回事?如何在顶层和用户范围内两次定义此常量?

    定义常量时,您没有将常量分配给self。您正在当前模块嵌套中定义一个常量

    当您显式打开一个类或模块时,您也在设置模块嵌套:

    module Foo
      BAR = 1
      puts Module.nesting.inspect # [Foo]
    end
    
    执行
    User.class_eval{AVOCADO='fruit'}
    时,模块嵌套为“Main”,即全局对象:

    User.class_eval do
      ADVOCADO = 'Fruit'
      puts Module.nesting.inspect # []
    end
    
    块实际上不会更改模块嵌套<另一方面,code>const_set
    在另一个模块嵌套中定义了一个常量

    Ruby实际上也没有两次定义常量。相反,当您使用const_get或引用一个常量而不显式使用scope resolution操作符时,Ruby将查看模块嵌套并将树遍历到全局作用域:

    class A
    end
    B = 'eureka'
    A.const_get(:B) # 'eureka'
    
    这就是如何引用顶级常量,而不必在它们前面加上
    。但是当您使用
    User::ADVOCADO
    时,您显式地引用了
    User
    中的常量

    当涉及到这个例子时,你已经误解了正在发生的事情。完全不是关于
    类的评估
    。您正在定义常量
    水果关注::鳄梨

    然后,当您将
    水果关注点
    包含到
    用户
    中时,您正在将
    水果关注点
    添加到包含在持续查找中的祖先链中:

    module FruitConcern
      ADVOCADO = 'fruit'
    end
    
    class User
      include FruitConcern
    end
    
    User::ADVOCADO  # fruit
    
    见:

      • 有:

        • self
          ,“当前对象”:隐式接收器和实例变量的作用域
        • 默认定义对象:其中使用
          def bar
          定义的方法最终没有明确的目标(即非
          def foo.bar
        • 恒定范围
        前两个在链接的文章中解释得很好。链接文章中承诺为第三篇撰写一篇文章,但从未出现过。许多人已经写了很多关于常量的文字,但不幸的是,没有人编写过权威的规范,类似于yugui为默认定义者所做的

        因此,不幸的是,我也只能猜测,因此毫无帮助地增加了关于这个话题的大量非权威性文字

        块在Ruby中是词汇范围的,它们捕获它们的词汇环境。通常,这意味着块内的引用与块外的引用的含义完全相同,就好像块不在那里一样。(当然,块局部变量除外。)

        所以

        foo{AVOCADO='fruit'}
        
        意思和

        鳄梨=‘水果’
    当然,除非,
    foo
    以某种方式更改了计算块的上下文。我们知道,
    instance\u eval
    改变了
    self
    。我们知道
    class\u eval
    更改了
    self
    和默认的定义者。但是,重要的是:
    class_eval
    不会更改隐式常量范围

    因此,分配
    AVOCADO='fruit'
    inside

    User.class_eval{AVOCADO='fruit'}
    
    与位于区块外时的含义完全相同:

    鳄梨=‘水果’
    换言之,它的含义与顶层的常量赋值相同,正如我们所知,在顶层:

    • self
      是未命名的单例对象,通常称为main
    • 默认的定义对象是
      对象
      ,增加了方法在默认情况下变为
      私有
      ,以及
    • 隐式常量范围也是
      对象
    因此,
    AVOCADO
    Object
    中定义

    这意味着以下工作:

    类用户
    鳄梨
    结束
    #=>“水果”
    
    因为常量查找首先在词汇上“向外”(在本例中它失败),然后在继承层次结构中“向上”,在继承层次结构中它成功,因为
    User
    隐式地是
    Object
    的子类

    User.const\u get(:鳄梨)
    #=>“水果”
    
    肌萎缩侧索硬化