包装Ruby函数

我希望能够完全透明地包装任何Ruby proc(包括我自己没有编写源代码的那些),并记录它的执行时间。

my_proc 

也就是说,我想创建一个调用my_proc保留的proc

  1. 上下文/接收者
  2. 争论
  3. 块。

并在打电话时打印出执行时间。


例如:

 my_proc = proc { |*args, &block| p self: self, args: args, block: block } Object.new.instance_eval &my_proc #=> { # :self=>#, # :args=>[#], # :block=>nil # } Object.instance_exec '5', &my_proc #=> { # :self=>Object, # :args=>["5"], # :block=>nil # } my_proc.call(1, 2) { } #=> { # :self=>main, # :args=>[1, 2], # :block=># # } 

然后我想包装它,它应该表现完全相同:

 def wrap(prc) # what does this look like? end wrapped_proc = wrap(my_proc) Object.new.instance_eval(&wrapped_proc) # took 1s #=> { # :self=>#, # :args=>[#], # :block=>nil # } Object.instance_exec '5', &wrapped_proc # took 2s #=> { # :self=>Object, # :args=>["5"], # :block=>nil # } wrapped_proc.call(1, 2) { } # took 3s #=> { # :self=>main, # :args=>[1, 2], # :block=># # } 

它似乎不是一个透明的函数包装器应该很难,但我无法弄清楚这一点。

这里唯一的技巧是处理λ.call传递块的情况,因为在后一种情况下不调用Proc#call调用 。 骰。

我的第一个[错误]意图是简单地:

 def wrap λ λ.singleton_class.prepend(Module.new do def call(*args, &cb) puts "⇓⇓⇓" super puts "⇑⇑⇑" end end) end 

但正如我已经说过的那样, specific_eval不会调用Proc#call也不Proc#call Proc#to_proc ,我放弃了。


另一方面,我们可能只是在接收器的上下文中将instance_exec包装为λ,但是没有办法将块作为参数传递给instance_exec ,因为它本身已经接收到λ。

情节变浓。 由于我们无法将块作为参数传递给instance_exec ,因此我们的包装器的使用者也不能。 是的,这解决了任务:

 def wrap λ -> (*args, &cb) do puts "⇓⇓⇓" (cb ? λ.call(*args, &cb) : instance_exec(*args, &λ)).tap do |result| puts result.inspect puts "⇑⇑⇑" end end end 

干得好。