包装一个 Ruby 函数 [英] Wrap a Ruby function

查看:46
本文介绍了包装一个 Ruby 函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够完全透明地包装任何 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

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

并打印出调用时的执行时间.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆