我试图将常用方法移动到模块或类中,并将其包含/继承在不同模块下命名空间的新类中。如果我在同一模块下有两个类名称空间,那么只要我在同一个名称空间下,我就可以在不包含模块名称的情况下调用它们。但是,如果我有一个方法包含在不同的模块而不是我的命名空间范围更改,我不知道为什么或如何避免它。
例如。此代码有效并返回'bar':
module Foo
class Bar
def test_it
Helper.new.foo
end
end
end
module Foo
class Helper
def foo
'bar'
end
end
end
Foo::Bar.new.test_it
但如果我将方法test_it移出到模块中,那么它就不再起作用了:NameError:未初始化的常量Mixins :: A :: Helper。
module Mixins; end
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Helper.new.foo
end
end
end
end
module Foo
class Bar
include Mixins::A
end
end
module Foo
class Helper
def foo
'bar'
end
end
end
Foo::Bar.new.test_it
此外,如果class_eval是evaling string而不是block,则scope变为Foo :: Bar而不是Foo。
module Mixins; end
module Mixins::A
def self.included(base)
base.class_eval %q{
def test_it
Helper.new.foo
end
}
end
end
module Foo
class Bar
include Mixins::A
end
end
module Foo
class Helper
def foo
'bar'
end
end
end
Foo::Bar.new.test_it
谁有想法?
编辑:
感谢Wizard和Alex,我最终得到的代码不是很漂亮但完成了工作(注意它使用的是Rails helper constantize ):
module Mixins; end
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
_nesting::Helper
end
def _nesting
@_nesting ||= self.class.name.split('::')[0..-2].join('::').constantize
end
end
end
end
module Foo
class Helper
end
class Bar
include Mixins::A
end
end
module Foo2
class Helper
end
class Bar
include Mixins::A
end
end
Foo::Bar.new.test_it #=> returns Foo::Helper
Foo2::Bar.new.test_it #=> returns Foo2::Helper
答案 0 :(得分:1)
要理解这个问题,您需要了解Ruby中常量查找的工作原理。它与方法查找不同。在这段代码中:
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Helper.new.foo
end
end
end
end
Helper
指的是一个名为“助手”的常量,它位于A
,Mixins
或在顶级定义(即在Object
中),< em> not 一个名为“Helper”的常量,它在Bar
中定义。仅仅因为class_eval
此类代码与Bar
类相同,就不会改变它。如果您知道“词法绑定”和“动态绑定”之间的区别,那么您可以说Ruby中的常量分辨率使用 lexical 绑定。您期望它使用动态绑定。
请记住,传递给base.class_eval
的块被编译为字节码一次,然后,每次调用included
挂钩时,都会编译同一个预编译块(包括对Helper
的引用)使用不同的类(base
)作为self
执行。每次执行base.class_eval
时,解释器都不会重新解析和编译块。
另一方面,如果将 String 传递给class_eval
,则每次 included
挂钩都会重新解析并编译该字符串运行。重要信息:在 null词法环境中评估来自字符串eval
的代码。这意味着来自周围方法的局部变量不可用于来自字符串eval
的代码。更重要的是,它还意味着周围的范围不会影响eval
ed代码中的常量查找。
如果确实需要动态解析常量引用,则显式常量引用将永远不会起作用。这根本不是语言的工作方式(并且有充分的理由)。考虑一下:如果动态解析常量引用,取决于self
的类,您永远无法预测在运行时如何解析对Array
或Hash
之类的引用的引用。如果你在模块中有这样的代码......
hash = Hash[array.map { |x| ... }]
...模块被混合到一个带有嵌套Hash
类的类中,Hash.[]
将引用嵌套类而不是Hash
来自标准库!显然,动态解析常量引用对于名称冲突和相关错误的可能性太大了。
现在通过方法查找,这是另一回事。 OOP的整个概念(至少是OOP的Ruby风格)是方法调用(即消息)的作用取决于接收者的类。
如果你想要动态地找到一个常量,取决于接收器的类,你可以使用self.class.const_get
来完成。这可以说比eval
一个字符串更清晰,以达到同样的效果。
答案 1 :(得分:0)
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Foo::Helper.new.foo
编辑:
在有机会玩了一下代码后,我发现了更多问题。我认为你不能完全按照自己的意思行事,但这很接近:
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
self.class.const_get(:Helper).new.foo
end
end
end
end
module Foo
class Bar
include Mixins::A
end
end
module Foo
class Bar::Helper
def foo
'bar'
end
end
end
请注意,Helper类需要在Foo :: Bar下进行命名空间,因为方式常量在Ruby中得到解析。
答案 2 :(得分:0)
在过去的几个主要版本中,Ruby中的常量查找已经改变了#class_eval。有关详情,请参阅此帖子:http://jfire.posterous.com/constant-lookup-in-ruby