Ruby 如何在包含的类中包含重新定义的方法
我写了这个模块:Ruby 如何在包含的类中包含重新定义的方法,ruby,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| metho
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
class TestClient
include HTTParty
...
end
有一个类包括HTTParty模块:
module HTTParty
include Hooks
before_action :perform_action, :get
def self.perform_action
puts "performed"
end
end
class TestClient
include HTTParty
...
end
当我尝试访问TestClient中的get
方法时,它不会调用perform\u action
。此处包含的get
方法是原始方法,而不是重新定义的方法
有没有办法在TestClient类中包含重新定义的
get
方法?您的代码几乎可以正常工作,但是get
实际上并没有直接在HTTParty
上定义,这是您没有想到的,而HTTParty
的included
类方法通过另一个路径将get
添加到类中
HTTParty
有一个名为HTTParty::ClassMethods
的模块,该模块包含get
等。它将它们放在两个位置:在HTTParty
本身上,您可以通过include
钩子调用HTTParty.get
,在任何带有include HTTParty
的类上。当您打开模块HTTParty
和包含钩子
时,您正在HTTParty.get
上插入钩子,这是调用TestClient.get
时使用的另一个查找链。让您的Hooks::ExecutionHooks
模块单独使用,我建议您制作一个hookedhtparty
模块,而不是monkeypatchingHTTParty
。这将使事情变得更加清楚,并避免了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
这确保HTTParty
和Hooks
在base
上可用,然后在每个get
上使用global\u-perform
钩子对其进行扩展。与初始代码的主要区别在于在base
(TestClient
)而不是HTTParty
)上调用动作之前调用,因此我们采用了正确的get
方法。您还将注意到global\u perform
接受*args
,因为您在生成钩子时是这样调用它的
由于我们包含了钩子
,您现在还可以在TestClient
本身中访问操作前
,因此您还可以定义更具体的操作前
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'
使用args运行本地执行[”https://www.stackoverflow.com"]
使用args运行全局执行[”https://www.stackoverflow.com"]
=> #
如果您真的需要任何东西,包括HTTParty
来获取钩子(可能是因为您无法控制包含它的东西),那么您可能需要直接使用monkeypatchHTTParty::ClassMethods
,因为这是定义get
的瓶颈,但这正进入更黑暗的领域。只要你在周围注入代码,你也可以其他东西。包括(hookedhtparty)
,使它更明确,并使它更封装。我认为你的代码看起来非常接近目标,但更现代的方法是将super
与模块#prepend
一起使用。我对Ruby很陌生。请解释一下,我应该在什么时候使用模块#prepend?请阅读prepend
文档,必要时进一步研究和实验,然后再要求我们编写更多文档。对不起,我忘了提到我正在使用的ruby版本。它是2.0.0p247。我对模块#prepend
做了一些研究。我还不完全清楚,但看起来这正是我需要的。问题是,prepend
在这个版本的ruby中是私有的。谢谢你澄清了所有的疑问。我正在从Java迁移到Ruby,因此需要一些时间来理解这两种语言在行为上的差异。