Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 模块范围不正确_Ruby On Rails_Ruby - Fatal编程技术网

Ruby on rails 模块范围不正确

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

我试图通过将通用方法移动到一个模块或类中,并将其包含/继承到不同模块下的新类中,来保持枯燥。如果我在同一个模块下有两个类名称空间,那么只要我在同一个名称空间下,我就可以调用它们而不包括模块名。但是,如果我有一个包含在不同模块中的方法,那么我的名称空间范围就会改变,我不知道为什么或者如何避免它

比如说。此代码工作并返回“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
但是,如果我将方法测试转移到一个模块中,那么它就不再工作了: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”的常量。仅仅因为你用class
Bar
来评估这个代码,就不会改变它。如果您知道“词法绑定”和“动态绑定”之间的区别,那么您可以说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| ... }]