在Ruby/Rails中是否有任何方法可以在方法中的所有方法调用上添加前置/后置执行钩子?



例如,我有以下代码

def my_method
do_work_1
5.times do
puts "This is a test"
end
do_work_2 "Hello"
do_work_3 do 
puts "Inside block"
do_something_else
end
end

我想要的是以下日志:

do_work_1 called with no params
do_work_1 finished
do_work_2 called with param "Hello"
do_work_2 finished
do_work_3 called with a block param
do_work_3 finished

我可以创建一个助手方法并做如下操作

def my_method
log_call method(:do_work_1)
5.times do
puts "This is a test"
end
log_call method(:do_work_2) "Hello"
log_call method(:do_work_3) do 
puts "Inside block"
do_something_else
end
end
def log_call(m, params)
puts("#{m.name} called with params #{params}")
m.call(params)
puts("#{m.name} finished")
end

但是这不是很漂亮,并且需要我们在每个需要日志记录的方法上使用log_call。有没有更好的方法来实现这个目标?

在生产代码中,我会使用一些显式的东西,类似于您已经拥有的log_call解决方案。我可能会用更基本的东西。是的,这是大量的输入,但你的团队中的每个人都会理解它。

在非生产代码中,或者作为临时措施,我们可以考虑Module.prepend

require 'minitest/autorun'
class Worker
def do_work_1(*); end
def do_work_3(*); end
def my_method
do_work_1
2.times { puts "This is a test" }
do_work_3 { puts "Inside block" }
end
end
module WorkLogging
%i[do_work_1 do_work_3].each do |m|
define_method(m) do |*args, **kwargs, &block|
puts format('%s called with: %s', m, [args, kwargs, block&.source_location].compact)
super(*args, **kwargs, &block)
puts format('%s finished', m)
end
end
end
Worker.prepend(WorkLogging)
class MyTest < Minitest::Test
def test_1
expected = <<~EOS
do_work_1 called with: [[], {}]
do_work_1 finished
This is a test
This is a test
do_work_3 called with: [[], {}, ["derp.rb", 10]]
do_work_3 finished
EOS
assert_output(expected) { Worker.new.my_method }
end
end
为了简洁起见,这个问题已经简化了。确切的输出格式留给读者作为练习。

最新更新