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

标签 ruby-on-rails ruby

给定一个 User 类:

class User
end
我想使用 .class_eval 定义一个新常量.所以:
User.class_eval { AVOCADO = 'fruit' }
  • 如果我尝试通过 User::AVOCADO 访问它, 我得到 uninitialized constant User::AVOCADO ,但是 User.const_get(:AVOCADO)作品。为什么?
  • 如果我在 included 中的 Rails 关注中定义一个常量方法并将关注点包含在 User 中类,我可以通过常规 :: 访问它抬头。例如:

  • module FruitConcern
      extend ActiveSupport::Concern
    
      included do
         AVOCADO = 'fruit'
      end
    
    end
    
    class User
      include FruitConcern
    end
    
    User::AVOCADO
    => 'fruit'
    
    但是,查找source code对于 ActiveSupport::关注,included只是将该块存储在一个实例变量( @_included_block )中,然后它会覆盖 append_features将调用 base.class_eval(&@_included_block) .
    所以,如果它只是调用 User.class_eval同一个块,为什么User::AVOCADO当常量定义在 included 中时有效阻止,但不是当我打电话时 User.class_eval { AVOCADO = 'fruit' }直接地?
  • 奇怪的是(见 this blog post),在做 User.class_eval { AVOCADO = 'teste' } 时,看来 Ruby 也将常量泄漏到顶层。所以:

  • User.const_get(:AVOCADO)
    => "fruit"
    AVOCADO
    => 'fruit'
    
    我知道块有平坦的作用域,但常量没有事先定义,我预计 class_eval更改两者 selfdefault definee到接收器。这里发生了什么?这个常量是如何在顶层和用户范围内被定义两次的?

    最佳答案

    three implicit contexts in Ruby :

  • self ,“当前对象”:隐式接收器和实例变量的作用域。
  • 默认定义:方法最终定义为 def bar没有明确的目标(即不是 def foo.bar )。
  • 恒定范围。

  • 前两个在链接的文章中得到了很好的解释。在链接的文章中 promise 了第三篇文章,但它从未出现过。很多人写过很多关于常量的文字,可惜没有人写出权威的规范,类似于yugui对默认definee所做的。
    所以,不幸的是,我也只能推测,从而无益地增加了关于这个话题的大量非权威性词语。
    块在 Ruby 中是词法范围的,它们捕获它们的词法环境。通常,这意味着块内的引用与块外的引用完全相同,就好像块不存在一样。 (当然块局部变量除外。)
    所以,
    foo { AVOCADO = 'fruit' }
    
    意思是一样的
    AVOCADO = 'fruit'
    
    当然,除非 foo以某种方式改变了评估块的上下文。我们知道instance_eval变化 self .我们知道class_eval变化 self和默认定义。然而,重要的是:class_eval不会更改隐式常量范围。
    因此,分配 AVOCADO = 'fruit'里面
    User.class_eval { AVOCADO = 'fruit' }
    
    如果它在块之外,则具有完全相同的含义:
    AVOCADO = 'fruit'
    
    换句话说,它与顶层的常量赋值具有相同的含义,正如我们所知,在顶层:
  • self是未命名的单例对象,通常称为 main,
  • 默认定义为 Object ,加上方法变成 private默认情况下,和
  • 隐式常量范围也是 Object .

  • 所以,AVOCADOObject 中定义.
    这意味着以下工作:
    class User
      AVOCADO
    end
    #=> 'fruit'
    
    因为常量查找首先在词法上“向外”(在这种情况下它失败),然后在继承层次结构中“向上”,它成功了,因为 User隐式是 Object 的子类.
    User.const_get(:AVOCADO)
    #=> 'fruit'
    
    也有效,因为这实际上与之前的相同,只是通过反射动态完成:它在类 User 中启动常量查找算法, 就像你写的一样
    class User
      AVOCADO
    end
    
    但是,这不起作用:
    User::AVOCADO
    
    老实说,这令人困惑,因为 AVOCADO应该继承自 Object .

    关于ruby-on-rails - Ruby:使用 class_eval 定义常量只能通过 const_get 找到,但不能直接通过::lookup 找到,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66751918/

    相关文章:

    javascript - Javascript 中的组合比较/"Spaceship"运算符 (<=>)?

    mysql - ActiveRecord 格式连接成数组

    ruby - 什么时候在 Ruby 中使用 Struct 而不是 Hash 更好?

    ruby-on-rails - strip_tags(unsafe_string).html_safe 安全吗?

    ruby-on-rails - 有办法处理 `after_save` 和 `after_destroy` "equally"吗?

    ruby-on-rails - Rails rel nofollow链接

    ruby-on-rails - 安装后无法立即找到 bcrypt

    ruby-on-rails - Rails 性能测试中 ruby​​-prof 的错误

    javascript - before_update 无法与 Ajax、Rails 4 一起正常工作

    ruby - 在 Ruby 中,是否有与 Smalltalk 中等效的 printNl 方法?