给定一个 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' }
直接地?User.class_eval { AVOCADO = 'teste' }
时,看来 Ruby 也将常量泄漏到顶层。所以:User.const_get(:AVOCADO)
=> "fruit"
AVOCADO
=> 'fruit'
我知道块有平坦的作用域,但常量没有事先定义,我预计 class_eval
更改两者 self
和 default 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
. 所以,
AVOCADO
在 Object
中定义.这意味着以下工作:
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/