【问题标题】:Overwrite class method from another module覆盖另一个模块的类方法
【发布时间】:2017-04-07 19:22:08
【问题描述】:

我想从库中修改现有模块A

module A
  class << self
    def foo
      bar('Baz')
    end

    private

    def bar(val)
      val.upcase
    end
  end
end

A.foo
=> "BAZ"

module B
  extend A

  def self.bar(val)
    val.downcase
  end
end

B.foo            # hoping for 'baz', but instead:
NoMethodError: undefined method `foo' for B:Module

有没有办法从A重用方法.foo并且只修改方法.bar

【问题讨论】:

标签: ruby module


【解决方案1】:

extend A 不起作用,因为foobar 不是A 的实例方法,而是A 的单例类。要重用这些方法,您必须按照 mudasobwa 的描述创建 A 的副本,或者您可以使用 A 的单例类的改进,如下所示:

module B
  extend(refine(A.singleton_class) do
    def bar(val)
      val.downcase
    end
  end)
end

B.foo # => "baz"

您不能使用extend A.singleton_class,因为extend 不接受类作为参数。 refine 返回的模块正是我们所需要的。

【讨论】:

  • 我从来没有想过 Module#refine 被用于此目的。很好的答案!
  • 这太棒了,点赞。第一次申请refine 看到真的很有意义。
  • 请注意,虽然 Refinements 是 Ruby 的一部分,但并非在所有 Ruby 实现中都实现了它们。最值得注意的是,Rubinius 开发人员在哲学上反对 Refinements 的想法,因此永远不会实现它们。 (有人可能会争论一个故意忽略 Ruby 特性的实现是否可以称为“Ruby 实现”。)
  • 不幸的是,Ruby 代码中的这种更改使此解决方案无效:mla.n-z.jp/~w3ml/w3ml.cgi/ruby-changes/msg/46511。这适用于 Ruby 版本 >= 2.3.5 和 >= 2.4.2。然后错误消息是ArgumentError: refinement module is not allowed
【解决方案2】:

假设你已经像上面那样声明了A,下面的代码就可以了:

▶ B = A.clone
#⇒ B
▶ (class << B; self; end).send :define_method, :bar do |val|
  val.downcase
end
▶ A.foo
#⇒ "BAZ"
▶ B.foo
#⇒ "baz"

可能应该有不那么棘手的方法,但我还想不通。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    相关资源
    最近更新 更多