2015-12-21 87 views
2

我寫了這個模塊:與類如何包括重新定義的方法包括

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

我認爲你的代碼看起來非常接近目標,但更現代的方法是在'Module#prepend'中使用'super'。 – sawa

+0

我對Ruby很新。請解釋一下,我應該在代碼的哪一點使用Module#prepend? – crashstorm

+0

請先閱讀'prepend'文檔,如果需要進行進一步研究並進行實驗,然後再要求我們編寫更多文檔。 –

回答

1

您的代碼幾乎工作,但get實際上並沒有直接定義在HTTParty,你沒想到的,和HTTPartyincluded類方法通過其他路徑添加到get類。

HTTParty有一個名爲HTTParty::ClassMethods模塊包含get等,這使他們在兩個地方:在HTTParty本身,所以你可以打電話HTTParty.get,並在任何類include HTTParty,通過included鉤。當您打開module HTTPartyinclude Hooks時,您將在HTTParty.get上插入掛鉤,這是與您撥打TestClient.get時不同的查找鏈。離開您的Hooks::ExecutionHooks模塊,我建議您製作一個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鉤上的每個get擴展它。與您的初始代碼主要不同的是before_actionbaseTestClient)上被調用而不是在HTTParty上,所以我們趕上了正確的get方法。您還會注意到global_perform接受*args,因爲您在生成掛鉤時就是這樣調用它的。

因爲我們包括Hooks,你現在也有TestClient本身獲得before_action,所以你也可以定義更加具體before_action S:

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 ... > 

如果你真的需要任何東西,包括HTTParty來獲得你的鉤子(可能是因爲你沒有包括它的東西的控制),你可能需要monkeypatch HTTParty::ClassMethods直接,因爲這是定義get的瓶頸,但這是進入更黑暗的領域。只要你注入代碼,你也可以使它更加明確,並保持它更多的封裝。

+0

感謝您澄清所有的疑惑。我正在從Java遷移到Ruby,因此需要一些時間來理解這兩種語言的行爲差異。 – crashstorm