【问题标题】:Override module_function inside ruby class with access to original在 ruby​​ 类中覆盖 module_function 并访问原始
【发布时间】:2019-04-13 14:17:15
【问题描述】:

我正在尝试覆盖这样定义的 Rails 辅助方法:

class Foo
  module Bar

    def orig
      # orig code
    end

    alias o orig

    module_function :o
    module_function :orig

  end
end

这样我就可以覆盖origo 并为其添加功能,如下所示:

def orig
  # new code
  # super (run orig code)
end
alias o orig

我查看了several different monkey patching methods,但它们似乎不起作用。我相信module_function 是它的原因。

有谁知道我是怎么做到的?

【问题讨论】:

  • 我认为 Rails 标记没有任何理由,并且还会删除“覆盖”和“猴子补丁”,只留下“红宝石”。

标签: ruby-on-rails ruby ruby-on-rails-5 overriding monkeypatching


【解决方案1】:

几乎所有过去使用猴子补丁的情况现在都可以通过继承和Module#prepend:

解决
module Foo
  def bar(arg)
    "self=#{self}, arg=#{arg}"
  end
  module_function :bar
end

module FooExtension
  def bar(arg)
    super(arg + 1)
  end
end

[Foo, Foo.singleton_class].each do |mod|
  mod.prepend FooExtension
end

Foo.bar(1) #=> "self=Foo, arg=2"

class MyClass
  include Foo
end

MyClass.new.bar(1) #=> "self=#<MyClass:0x00007fb66a86cbf8>, arg=2"

【讨论】:

  • 不错,Jörg,与其修改Foo(可能在其他地方使用),不如在类定义中包含和前置所有内容:[self, singleton_class].each { |k| k.include Foo; k.prepend FooExtension }。 Thomas:请注意,这不依赖于声明 module_function :bar。如果您不需要 bar 作为 Foo 中的模块方法,则可以删除该语句。我同意 Jörg 的观点,这是您应该采取的方法。
【解决方案2】:

这里有一个解决方法。您可以重新打开模块,对原始实例方法进行未绑定引用,然后重新定义它以调用原始方法(具有一些更改的行为)。

一、原定义:

module Foo
  def bar(arg)
    "self=#{self}, arg=#{arg}"
  end
  module_function :bar
end

接下来,重新打开并重新定义方法:

module Foo
  OrigBarMethod = instance_method(:bar)
  def bar(arg)
    Foo::OrigBarMethod.bind(self).call(arg + 1)
  end
  module_function :bar
end

puts Foo.bar(1) # => "self=Foo, arg=2"

我使用bind(self),这样原来的方法仍然可以使用self,例如:

class MyClass
  include Foo
end

MyClass.new.send(:bar, 1) # => "self=#<MyClass:0x00007fb66a86cbf8>, arg=2"

【讨论】:

  • 我曾经是这个技巧的大力支持者,因为与alias_method 不同,它不会污染命名空间,但不再需要了。可以使用alias_method 完成的所有事情,现在也可以通过简单的继承 (prepend) 完成。
  • @JörgWMittag 好吧,我必须承认我还没有养成使用 prepend 的习惯。但是,我看到它在这里更简单。谢谢
  • prepend 是一种强大的继承机制。它可以包含猴子补丁、面向方面的编程(Before-、After- 和 Around-Advice)、CLOS 风格的方法组合器(同样,Before-、After- 和 Around-Combinators)。如果您向下滚动到 OP 链接到的 my answer 的末尾,我会举几个例子来说明在引入 prepend 之前浮动的 Ruby 中增强继承的想法,以及如何将所有这些想法替换为 @ 987654332@,通常甚至具有附加功能和灵活性。
猜你喜欢
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 2011-07-06
  • 2012-03-07
  • 1970-01-01
相关资源
最近更新 更多