包装一个 Ruby 函数 [英] Wrap a Ruby function
问题描述
我希望能够完全透明地包装任何 Ruby proc(包括我没有为自己编写源代码的那些),并记录其执行时间.
I want to be able to completely transparently wrap any Ruby proc (including ones that I have not written the source code for myself), and log its execution time.
my_proc
也就是说,我想创建一个调用 my_proc
保留
That is, I want to create a proc that calls my_proc
preserving
- 上下文/接收者
- 论据
- 块.
并打印出调用时的执行时间.
and print out the execution time when it was called.
例如:
my_proc = proc { |*args, &block| p self: self, args: args, block: block }
Object.new.instance_eval &my_proc
#=> {
# :self=>#<Object:0x007fd4c985f3e0>,
# :args=>[#<Object:0x007fd4c985f3e0>],
# :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=>#<Proc:0x007fd4c985e9b8>
# }
然后我想包装它,它的行为应该完全相同:
And then I want to wrap it, and it should behave exactly the same:
def wrap(prc)
# what does this look like?
end
wrapped_proc = wrap(my_proc)
Object.new.instance_eval(&wrapped_proc)
# took 1s
#=> {
# :self=>#<Object:0x007fd4c985f3e0>,
# :args=>[#<Object:0x007fd4c985f3e0>],
# :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=>#<Proc:0x007fd4c985e9b8>
# }
<小时>
透明函数包装器似乎并不难,但我想不通.
It doesn't seem like a transparent function wrapper should be hard, but I can't figure this out.
推荐答案
这里唯一的技巧是处理 λ.call
和 &λ
pass-through-阻止情况,因为在后一种情况下Proc#call
没有被调用.哎呀.
The only trick here is to handle both λ.call
and &λ
pass-through-block cases because in the latter scenario the Proc#call
is not invoked. Sic.
我的第一个 [错误] 意图是:
My first [wrong] intent was to simply:
def wrap λ
λ.singleton_class.prepend(Module.new do
def call(*args, &cb)
puts "⇓⇓⇓"
super
puts "⇑⇑⇑"
end
end)
end
但正如我已经说过的,specific_eval
不调用 Proc#call
也不调用 Proc#to_proc
,我放弃了.
but as I have already said, the specific_eval
does not call Proc#call
nor Proc#to_proc
and I gave up.
另一方面,我们可以简单地instance_exec
在接收者的上下文中包装λ,但是没有办法将一个块作为参数传递给instance_exec
,因为它已经收到了 λ 本身.
On the other hand, we might simply instance_exec
wrapped λ in the context of the receiver, but there is no way to pass a block as a parameter to instance_exec
, since it already receives λ itself.
情节变厚了.由于我们无法将块作为参数传递给 instance_exec
,因此包装器的使用者也不能.是的,这解决了任务:
The plot thickens. Since we can’t pass a block as a parameter to instance_exec
, neither can the consumer of our wrapper. Yes, that solves the task:
def wrap λ
-> (*args, &cb) do
puts "⇓⇓⇓"
(cb ? λ.call(*args, &cb) : instance_exec(*args, &λ)).tap do |result|
puts result.inspect
puts "⇑⇑⇑"
end
end
end
给你.
这篇关于包装一个 Ruby 函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!