Ruby动态重新定义了include中不存在的类方法

时间:2015-12-21 15:31:08

标签: ruby

我打开了HTTParty模块,并使用以下命令在运行时重新定义了get和post类方法:

define_singleton_method(method_name) do |*args, &block|
  method = method(method_name)
  method(hook).call(*args)
  method.call(*args, &block)
end

此处,hook是HTTParty模块中另一个方法的名称。 method_name可以是获取或发布。

之后我尝试在TestClient类中包含HTTParty,如下所示:

class TestClient
  include HTTParty
  ...
end

但是包含的get和post方法的版本只是原始版本。它不应该使用重新定义的方法吗?

1 个答案:

答案 0 :(得分:1)

我认为这是因为当你define_singleton_method时,接收者是HTTParty模块,而不是它所包含的类(它还不存在)调用define_singleton_method时。因此,当您include HTTParty TestClient时,您重新定义的get已经绑定到HTTParty,并且在TestClient上调用它将无法到达它。但是,如果您执行HTTParty.get("http://google.com"),则会获得重新定义的方法:

  module HTTParty
    %i{ get post }.each do |method_name|
      define_singleton_method(method_name) do |*args, &block|
        puts "redefined!"
      end
    end
  end

  class TestClient
    include HTTParty
  end
  TestClient.get("http://google.com")
  # real GET
  TestClient.method(:get).source_location 
  # ["/Users/kitkat/.rvm/gems/ruby-2.0.0-p576@kitkat/gems/httparty-0.13.7/lib/httparty.rb", 475]
  HTTParty.get("http://google.com")
  # => redefined!
  HTTParty.method(:get).source_location
   => ["(irb)", 30]

以下是您如何真正重新定义您的方法:

  module HTTParty
    def self.included(klass)          
      %i{ get post }.each do |method_name|
        klass.define_singleton_method(method_name) do |*args, &block|
          puts "redefined!"
        end
      end
    end
  end

  class TestClient
    include HTTParty
  end

2.0.0-p576 :058 >       TestClient.get("http://google.com")
redefined!
 => nil
2.0.0-p576 :060 >         TestClient.method(:get).source_location
 => ["(irb)", 48]