如何在包含include的类中包含重新定义的方法

时间:2015-12-21 17:01:47

标签: ruby

我写了这个模块:

module Hooks
  module ExecutionHooks
    def before_action(hook, *method_names)
      method_names.each do |method_name|
        method = method(method_name)
        define_singleton_method(method_name) do |*args, &block|
          method(hook).call(*args)
          method.call(*args, &block)
        end
      end
    end
  end

  def self.included(base)
    base.send(:extend, Hooks::ExecutionHooks)
  end
end

此模块允许其他模块或类定义一个钩子,该钩子应该在类似于Rails中的before_action的特定动作之前调用。 然后我将这个模块包含在我的HTTParty模块中:

module HTTParty
  include Hooks
  before_action :perform_action, :get

  def self.perform_action
    puts "performed"
  end
end

有一个包含HTTParty模块的类:

class TestClient 
  include HTTParty
  ...
end

当我尝试访问TestClient中的get方法时,它不会调用perform_action。此处包含的get方法是原始方法,而不是重新定义的方法。

有没有办法在TestClient类中包含重新定义的get方法?

1 个答案:

答案 0 :(得分:1)

您的代码几乎可以使用,但get实际上并未直接在HTTParty上定义,您没有预料到,HTTParty' s { {1}}类方法通过另一条路径向您的班级添加included

get有一个名为HTTParty的模块,其中包含HTTParty::ClassMethods等。它将它们放在两个位置:get本身,因此您可以调用HTTParty ,以及HTTParty.get通过include HTTParty挂钩的任何课程。当您打开includedmodule HTTParty时,您在include Hooks上插入了一个挂钩,这是一个与您呼叫HTTParty.get时不同的查找链。仅保留TestClient.get模块,建议您创建一个Hooks::ExecutionHooks模块而不是monkeypatching HookedHTTParty。这样可以更清楚地了解发生了什么,并避免了HTTParty内部的复杂性,我们不应该这样做。

HTTParty

这样可以确保# hooked_httparty.rb require 'httparty' require 'hooks' module HookedHTTParty module ClassMethods def global_perform(*args) puts "Running global perform with args #{args.inspect}" end end def self.included(base) base.include(HTTParty) base.include(Hooks) base.extend(HookedHTTParty::ClassMethods) base.before_action :global_perform, :get end end 上可以使用HTTPartyHooks,然后在每个base上使用global_perform挂钩扩展它。与初始代码的主要区别在于getbefore_action)而不是base上调用TestClient,因此我们会使用正确的HTTParty方法。您还会注意到get接受global_perform,因为您在生成挂钩时以这种方式调用它。

由于我们包含*args,您现在还可以访问Hooks中的before_action,因此您还可以定义更具体的TestClient

before_action

正在运行class TestClient include HookedHTTParty before_action :local_perform, :get def self.local_perform(*args) puts "Running local perform with args #{args.inspect}" end end

get

如果你确实需要包括> TestClient.get 'https://www.stackoverflow.com' Running local perform with args ["https://www.stackoverflow.com"] Running global perform with args ["https://www.stackoverflow.com"] => #<HTTParty::Response:0x7fa523a009e0 ... > 在内的任何内容(也许是因为你无法控制包括它在内的东西),你可能需要直接monkeypatch HTTParty,因为那& #39;是定义HTTParty::ClassMethods的瓶颈,但它进入了更黑暗的领域。只要您重新注入代码,您也可以get使其更明确,并使其更加封装。