Ruby:使用 class_eval 定义常量只能通过 const_get 找到,但不能直接通过 :: Lookup 找到

2024-04-25

给定一个 User 类:

class User
end

我想使用定义一个新常量.class_eval. So:

User.class_eval { AVOCADO = 'fruit' }
  1. 如果我尝试通过访问它User::AVOCADO, I get uninitialized constant User::AVOCADO, but User.const_get(:AVOCADO)作品。为什么?

  2. 如果我在 Rails Concern 中定义一个常量included方法并将关注点包含在User类,我可以通过常规访问它::抬头。例如:

module FruitConcern
  extend ActiveSupport::Concern

  included do
     AVOCADO = 'fruit'
  end

end

class User
  include FruitConcern
end

User::AVOCADO
=> 'fruit'

然而,查找源代码 https://github.com/rails/rails/blob/5aaaa1630ae9a71b3c3ecc4dc46074d678c08d67/activesupport/lib/active_support/concern.rb对于 ActiveSupport:: 关注,included只是将该块存储在实例变量中(@_included_block),然后它被覆盖append_features将会通知base.class_eval(&@_included_block).

所以,如果只是打电话User.class_eval同一个块,为什么User::AVOCADO当常量在其中定义时起作用included阻止,但当我打电话时不阻止User.class_eval { AVOCADO = 'fruit' }直接地?

  1. 奇怪的是(见这篇博文 https://thoughtbot.com/blog/constant-annoyance),当做User.class_eval { AVOCADO = 'teste' },看来 Ruby 也将常量泄漏到了顶层。所以:
User.const_get(:AVOCADO)
=> "fruit"
AVOCADO
=> 'fruit'

我知道块具有平面范围,但该常量未事先定义,并且我期望class_eval改变两者selfdefault definee到接收器。这里发生了什么?这个常量是如何在顶层和用户范围内定义两次的?


当您定义常量时,您并不是将常量分配给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

块实际上并不改变模块嵌套。const_set另一方面,在另一个模块嵌套中定义一个常量。

Ruby 实际上并没有两次定义该常量。相反,当您使用 const_get 或引用常量而不显式使用范围解析运算符时,Ruby 将查看模块嵌套并将树向上遍历到全局范围:

class A
end
B = 'eureka'
A.const_get(:B) # 'eureka'

这就是您如何引用顶级常量而无需在它们前面加上::。但是当你使用User::ADVOCADO你明确引用了里面的常量User.

当谈到这个令人担忧的例子时,你误解了正在发生的事情。根本不是关于class_eval。您正在定义常数FruitConcern::AVOCADO.

当你然后包括FruitConcern into User你正在添加FruitConcern到包含在常量查找中的祖先链上:

module FruitConcern
  ADVOCADO = 'fruit'
end

class User
  include FruitConcern
end

User::ADVOCADO  # fruit

See:

  • Ruby 模块:包含、前置、扩展 https://medium.com/@leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073
  • 关于 Ruby 中的常量查找,您想了解的所有信息 https://cirw.in/blog/constant-lookup.html
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Ruby:使用 class_eval 定义常量只能通过 const_get 找到,但不能直接通过 :: Lookup 找到 的相关文章

随机推荐