围绕现有的指定方法运行 ruby 方法
Posted
技术标签:
【中文标题】围绕现有的指定方法运行 ruby 方法【英文标题】:Running ruby method around existing specified method 【发布时间】:2020-02-05 23:13:56 【问题描述】:我有一个由许多服务组成的现有库,它们都响应方法execute
每个方法都符合逻辑
class BaseService
def execute
raise NotImplementedError
end
end
class CreateUserService < BaseService
def execute
# Some code that does Other stuff
end
end
class NotifyService < BaseService
def execute
# Some code that does other other stuff
end
end
我想做一些事情来完成类似的事情:
class BaseService
around :execute do |&block|
puts 'Before'
block.call
puts 'After'
end
end
然后包装每个执行方法,包括子类,以便可以在之前和之后执行逻辑。
我做过这样的事情:
module Hooks
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def around(*symbols, &block)
to_prepend = build_module(symbols) do |*args, &mblock|
result = nil
block.call do
result = super(*args, &mblock)
end
result
end
prepend to_prepend
end
private
def build_module(symbols, &block)
Module.new do
symbols.each do |symbol|
define_method(symbol, &block)
end
end
end
end
end
class BaseService
include Hooks
around :execute do |&block|
puts 'before'
block.call
puts 'after'
end
# ..
end
但是,当around
方法仅在基类上执行时。我猜这是由于prepend
的性质。祖序如下:
[<Module>, BaseService, Hooks, ...]
[NotifyService, <Module>, BaseService, Hooks, ...]
etc
有没有办法可以做到这一点? 谢谢!
【问题讨论】:
别猜祖序是什么,问!MyClass.ancestors
.
谢谢@tadman,我已经更新以传达确切的顺序
【参考方案1】:
您不修改子类,也不从它们的 execute
方法调用 super
。
据我所知,CreateUserService#execute
没有理由调用已包装的 BaseService#execute
。
实现您想要的一种方法是refinements:
class BaseService
def execute
p "BaseService#execute"
end
end
class CreateUserService < BaseService
def execute
p "CreateUserService#execute"
end
end
class NotifyService < BaseService
def execute
p "NotifyService#execute"
end
end
module WrappedExecute
[NotifyService, CreateUserService, BaseService].each do |service_class|
refine service_class do
def execute
puts "Before"
super
puts "After"
end
end
end
end
CreateUserService.new.execute
#=> "CreateUserService#execute"
using WrappedExecute
CreateUserService.new.execute
# Before
# "CreateUserService#execute"
# After
注意:
to_prepend = build_module(symbols) do |*args, &mblock|
result = nil
block.call do
result = super(*args, &mblock)
end
result
end
可以替换为
to_prepend = build_module(symbols) do |*args, &mblock|
block.call do
super(*args, &mblock)
end
end
不过,您仍然需要在每个 Service
类中使用 include Hooks
。
【讨论】:
谢谢,我以前从未听说过refine
方法,所以我一定会读一读【参考方案2】:
我最终做的是我不确定自己是否可以接受的事情。
class BaseService
include Hooks
def self.inherited(subclass)
subclass.around(:execute) do |&block|
Rails.logger.tagged(tags) do
block.call
end
end
end
end
这使我能够将周围的功能应用到所有其他类,以避免大规模重构。不确定这是否是最好的解决方案,但想发布给其他人看。
【讨论】:
好主意。inherited
是缺少的部分,以便您的子类也可以由 Hooks
更新。以上是关于围绕现有的指定方法运行 ruby 方法的主要内容,如果未能解决你的问题,请参考以下文章