Ruby on rails 模块范围不正确
我试图通过将通用方法移动到一个模块或类中,并将其包含/继承到不同模块下的新类中,来保持枯燥。如果我在同一个模块下有两个类名称空间,那么只要我在同一个名称空间下,我就可以调用它们而不包括模块名。但是,如果我有一个包含在不同模块中的方法,那么我的名称空间范围就会改变,我不知道为什么或者如何避免它 比如说。此代码工作并返回“bar”:Ruby on rails 模块范围不正确,ruby-on-rails,ruby,Ruby On Rails,Ruby,我试图通过将通用方法移动到一个模块或类中,并将其包含/继承到不同模块下的新类中,来保持枯燥。如果我在同一个模块下有两个类名称空间,那么只要我在同一个名称空间下,我就可以调用它们而不包括模块名。但是,如果我有一个包含在不同模块中的方法,那么我的名称空间范围就会改变,我不知道为什么或者如何避免它 比如说。此代码工作并返回“bar”: module Foo class Bar def test_it Helper.new.foo end end end modul
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
但是,如果我将方法测试转移到一个模块中,那么它就不再工作了:NameError:UninitializedConstantMixins::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
此外,如果类_eval是求值字符串而不是块,则作用域将变成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助手constantize):
编辑: 在获得了一次使用代码的机会之后,我看到了更多的问题。我不认为你能完全做到你所尝试的,但这很接近:
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
注意,由于在Ruby中解析常量的方式不同,助手类需要在Foo::Bar下命名。要理解这个问题,您需要了解常量查找在Ruby中是如何工作的。它与方法查找不同。在此代码中:
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Helper.new.foo
end
end
end
end
Helper
指的是一个名为“Helper”的常量,该常量位于a
、Mixins
,或在顶层(即对象
)中定义,而不是在栏中定义的名为“Helper”的常量。仅仅因为你用classBar
来评估这个代码,就不会改变它。如果您知道“词法绑定”和“动态绑定”之间的区别,那么您可以说Ruby中的常量解析使用词法绑定。您希望它使用动态绑定
请记住,传递给base.class_eval
的块被编译成字节码一次,随后,每次调用included
钩子时,同一个预编译块(包括对Helper
的引用)都会像self
一样使用不同的类(base
)执行。解释器不会在每次执行base.class\u eval
时重新解析和编译块
另一方面,如果将字符串传递给class\u eval
,则每次运行included
钩子时都会重新解析和编译该字符串。重要提示:从字符串中提取的eval
ed代码在空词汇环境中求值。这意味着来自周围方法的局部变量对于从字符串中被eval
ed的代码不可用。更重要的是,它还意味着周围的范围不会影响eval
ed代码中的常量查找
如果您确实希望动态解析常量引用,那么显式常量引用将永远无法工作。这根本不是语言的工作方式(这是有充分理由的)。想想看:如果常量引用是动态解析的,取决于self
的类,那么您永远无法预测对Array
或Hash
之类的引用在运行时是如何解析的。如果模块中有这样的代码
hash = Hash[array.map { |x| ... }]
…模块被混合到一个带有嵌套的哈希
类的类中,哈希。[]
将引用标准库中的嵌套类,而不是哈希
!显然,动态解析常量引用有太多发生名称冲突和相关错误的可能性
现在使用方法查找,这是另一回事。OOP的整个概念(至少是OOP的Ruby风格)是方法调用(即消息)的作用取决于接收方的类
如果您确实希望根据接收器的类别动态查找常量,则可以使用self.class.const\u get
进行查找。可以说,这比使用字符串来实现相同的效果要干净得多。在过去的几个主要版本中,Ruby中的常量查找与#class_eval相比发生了变化。有关更多信息,请参阅本文:代码并没有过于复杂,我只是简化了我的案例,使问题看起来过于复杂。你的回答不能解决我的问题。如果我太想在模块“Foo2”中包含你的mixin,它会调用Foo::Helper而不是Foo2::Helper,这就是我不想显式指定顶级名称空间并动态解析它的原因。我明白了。在包含Mixens::A
之前,请尝试在模块中定义Helper
。最后一次尝试解决这个有趣的问题。请参阅上面的编辑。感谢您的回复。不幸的是,我不能在Bar下命名helper(我正在使用rails引擎,我的模型需要在顶层模块下,该模块是引擎的名称)。我做了和您建议的类似的事情——使用self.class来获得正确的名称空间。它看起来很粗糙,但现在就可以了。谢谢,谢谢。这解释了很多。如果我用字符串而不是块来做class_eval,我仍然不明白为什么嵌套会改变。我用字符串类_eval得到[Mixins::A,Foo::Bar],用块类_eval得到[Mixins::A]。范围界定是有道理的,只是需要考虑一下,寻找一个解决办法。@luriG,我花了很长时间才回到这个问题上来。希望添加的信息能帮助您理解为什么eval
ing块和字符串会产生不同的结果。很高兴您找到了解决方案!
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Helper.new.foo
end
end
end
end
hash = Hash[array.map { |x| ... }]