ruby 猴子修补模块的类方法

ruby monkey patching a module's classmethod

A gem 我正在使用 (gem "A") 依赖于另一个 gem (gem "B") 我的方法正在尝试打补丁。当我修补 gem "B" 并从 gem "A" 调用方法时,猴子修补程序将被忽略。我的补丁如下所示:

module B
  class<< self
    def patched_method()
      raise
    end
  end
end

我在整个过程中都插入了语句来打印方法的位置,使用的形式是

puts B.method(:patched_method).source_location

补丁前调用时,指向gem"B"的位置。补丁后指向我定义补丁的位置,但它仍然没有调用我的补丁!

现在我的补丁只调用 raise 所以我可以验证它是否被调用,但我也尝试打印文本并调用我的实际补丁代码,none 有效。我做错了什么?

Gem 和代码细节

我正在尝试修补 Numerizer gem's numerize method which is used by the Chronic gem

我尝试了多种方式来修补,但最近的是:

module NumerizerExpand
  module ClassMethods
    def self.numerize(value)
      raise
    end
  end

  def self.included(receiver)
    receiver.extend ClassMethods
  end
end
Numerizer.send(:include, NumerizerExpand)

Numerizer.instance_eval do
  class<< self
    def my_numerize(value)
      puts "here"
      raise
    end
    alias_method :numerize, :my_numerize
  end
end

对于第二种方法,我尝试同时使用 instance_evalmodule_eval,但似乎都不起作用。

I've created a gist on github 包含我正在 运行 测试的脚本。

Numerizer 是一个 class 而 numerize 是一个方法。 您不能使用模块覆盖 class 方法,因为 ruby 按以下顺序查找方法:

  • class
  • 包含/扩展模块
  • parent class
  • parent 的 class 个模块
  • ...
  • Object
  • Object 包含的模块
  • 基本Object

这个做起来应该很简单,不知道你有没有试过?

class Numerizer
  def self.numerize(string)
    # override it here
  end
end

我希望这能奏效,除非你先这样做然后 require 'numerizer' 在这种情况下,您将覆盖您的更改。

PS。 抱歉,我还没有测试过,但我懒得安装 gem,这需要我 运行 sudo gem sources -a http://gemcutter.org 我不知道如何撤消该操作,也不会 google。

PPS。 如果这行不通,我会感到非常惊讶。

你打错补丁了class :)

require 'numerizer'
require 'chronic'

puts Chronic::Numerizer.method(:numerize).source_location


class Chronic::Numerizer
    def self.numerize(value)
      puts "here"
      raise
    end
end

puts Chronic::Numerizer.method(:numerize).source_location

#p Chronic::Numerizer.numerize(3)
p Chronic.parse('January 2nd')

输出

bbozo@eva:~/dev/SO_question_1$ ruby wii.rb
/home/bbozo/.rvm/gems/ruby-2.2.3/gems/chronic-0.10.2/lib/chronic/numerizer.rb
72
wii.rb
8
here
wii.rb:10:in `numerize': unhandled exception
        from /home/bbozo/.rvm/gems/ruby-2.2.3/gems/chronic-0.10.2/lib/chronic/parser.rb:100:in `pre_normalize'
        from /home/bbozo/.rvm/gems/ruby-2.2.3/gems/chronic-0.10.2/lib/chronic/parser.rb:226:in `tokenize'
        from /home/bbozo/.rvm/gems/ruby-2.2.3/gems/chronic-0.10.2/lib/chronic/parser.rb:60:in `parse'
        from /home/bbozo/.rvm/gems/ruby-2.2.3/gems/chronic-0.10.2/lib/chronic.rb:90:in `parse'
        from wii.rb:17:in `<main>'

显然,chronic 团队决定他们不想将一个 class 作为单独的依赖项进行维护,他们只是 copy-pasted 并将其包含在命名空间中。

您正在修补 gem,实际上 chronic 并未使用它。猪的把戏 :) 但对 chronic 人来说是合理的。