2016-11-18 4 views
3

Я хочу иметь возможность полностью прозрачно обернуть любой Ruby proc (включая те, которые я сам не написал для себя), и запишите его время выполнения.Оберните функцию Ruby

my_proc 

То есть, я хочу создать процедурный, который вызывает my_proc сохранение

  1. контексте/приемник
  2. аргументы
  3. блок.

и распечатать время выполнения, когда оно было вызвано.


Например:

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> 
# } 

И тогда я хочу, чтобы обернуть его, и он должен вести себя точно так же:

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> 
# } 

Это не похоже, как у прозрачная функциональная оболочка должна быть сложной, но я не могу понять это.

+1

Этот код для меня не работает. Вы имеете в виду 'p self, args, block' и' instance_eval (& wrapped_proc) '? И вы, вероятно, хотите использовать 'my_proc' вместо' wrapped_proc' в первом примере. – Stefan

+0

Нет, я сдаюсь. это либо приемник, либо блок. Я не мог пройти все три. –

+0

@ Серхио Туленцев, ты, должно быть, шутишь. Конечно, этот вопрос (все еще абсурдный) имеет ответ: 'def wrap (λ); λ; end'. – mudasobwa

ответ

3

Единственный трюк здесь обрабатывать как λ.call и случаев сквозного-блок, так как в последнем случае Proc#call не вызываются. Sic.

Моя первая [неправильно] намерение состояло в том, чтобы просто:

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, и я сдался.


С другой стороны, мы могли бы просто 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 

Здесь вы идете.

+0

«Не может быть и потребителем нашей обертки» - Ах! Как я об этом не думал! –

+0

@SergioTulentsev откровенно, я все еще копаю в ruby-код (код самого ruby ​​:) :), пытаясь понять, в чем причина обхода 'ProC# call' внутри' & λ' вызовов. И я не могу найти. Первый отрывок, который я опубликовал, имеет для меня больше смысла. – mudasobwa

+0

Хорошее решение, и первая попытка имела бы смысл. Используется ли код UTF-8 в крупных официальных проектах Ruby? –

Смежные вопросы