![](/img/trans.png)
[英]How/When/Where to Extend Gem Classes (via class_eval and Modules) in Rails 3?
[英]Ruby: defining constants using class_eval can be found only via const_get but not via directly :: lookup
給定用戶 class:
class User
end
我想使用.class_eval
定義一個新常量。 所以:
User.class_eval { AVOCADO = 'fruit' }
如果我嘗試通過User::AVOCADO
訪問它,我會得到uninitialized constant User::AVOCADO
,但User.const_get(:AVOCADO)
有效。 為什么?
如果我在included
的方法中的 Rails Concern 中定義了一個常量,並將關注點包含在User
class 中,我可以通過常規::
查找來訪問它。 例如:
module FruitConcern
extend ActiveSupport::Concern
included do
AVOCADO = 'fruit'
end
end
class User
include FruitConcern
end
User::AVOCADO
=> 'fruit'
但是,查找 ActiveSupport::Concern 的源代碼, 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
者都更改為接收者。 這里發生了什么? 這個常量是如何在頂層和用戶 scope 中定義的?
self
,“當前對象”:隱式接收器和 scope 實例變量。def bar
定義的方法沒有明確的目標(即不是def foo.bar
)。前兩個在鏈接的文章中得到了很好的解釋。 鏈接的文章中承諾了第三篇的一篇文章,但它從未出現過。 很多人寫過很多關於常量的文字,但不幸的是,沒有人寫過權威的規范,類似於 yugui 為默認的 definee 所做的。
所以,不幸的是,我也只能推測,因此無益地增加了關於這個話題的大量非權威性文字。
塊在 Ruby 中的詞法范圍,它們捕獲它們的詞法環境。 一般來說,這意味着塊內部的引用與塊外部的含義完全相同,就好像塊不存在一樣。 (當然,塊局部變量除外。)
所以,
foo { AVOCADO = 'fruit' }
意思是一樣的
AVOCADO = 'fruit'
當然,除非foo
以某種方式改變了評估塊的上下文。 我們知道instance_eval
改變了self
。 我們知道class_eval
改變了self
和默認的 definee 。 然而,重要的是: class_eval
不會改變隱式常量 scope。
因此,分配AVOCADO = 'fruit'
inside
User.class_eval { AVOCADO = 'fruit' }
如果它在塊之外,則具有完全相同的含義:
AVOCADO = 'fruit'
換句話說,它與頂層的常量賦值具有相同的含義,正如我們所知,在頂層:
self
是未命名的 singleton object,俗稱main ,Object
,增加了方法默認為private
的扭曲,並且Object
。 因此, AVOCADO
在Object
中定義。
這意味着以下工作:
class User
AVOCADO
end
#=> 'fruit'
因為常量查找首先在詞匯上“向外”(在這種情況下失敗),然后在 inheritance 層次結構中“向上”,它成功,因為User
隱式是Object
的子類。
User.const_get(:AVOCADO)
#=> 'fruit'
也有效,因為這實際上與之前的相同,只是通過反射動態完成:它在 class User
中啟動常量查找算法,就像你寫的一樣
class User
AVOCADO
end
但是,這不起作用:
User::AVOCADO
老實說,這令人困惑,因為AVOCADO
應該繼承自Object
。
當您定義常量時,您不會將常量分配給self 。 您正在當前模塊嵌套中定義一個常量。
當您顯式打開 class 或模塊時,您還設置了模塊嵌套:
module Foo
BAR = 1
puts Module.nesting.inspect # [Foo]
end
當您執行User.class_eval { AVOCADO = 'fruit' }
時,模塊嵌套是“Main”,也就是全局 object:
User.class_eval do
ADVOCADO = 'Fruit'
puts Module.nesting.inspect # []
end
塊實際上並不改變模塊嵌套。 另一方面, const_set
在另一個模塊嵌套中定義了一個常量。
Ruby 實際上也沒有兩次定義常量。 相反,當您使用 const_get 或引用常量而不明確使用 scope 分辨率運算符 Ruby 將查看模塊嵌套並將樹向上遍歷到全局 scope:
class A
end
B = 'eureka'
A.const_get(:B) # 'eureka'
這就是您可以引用頂級常量而不用::
開頭的方式。 但是,當您使用User::ADVOCADO
時,您明確地引用了User
內部的常量。
當談到這個例子時,你誤解了正在發生的事情。 根本不是關於class_eval
。 您正在定義常量FruitConcern::AVOCADO
。
然后,當您將FruitConcern
包含到User
中時,您將FruitConcern
添加到包含在常量查找中的祖先鏈中:
module FruitConcern
ADVOCADO = 'fruit'
end
class User
include FruitConcern
end
User::ADVOCADO # fruit
看:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.