Ruby метапрограммирование для бенчмаркинга - PullRequest
0 голосов
/ 09 марта 2012

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

benchmark_data = Benchmark.realtime do
  begin
    do_something()
   rescue StandardError => e
     log_errors("error occurred")
     raise e
   end
end

write_benchmark_data_to_db(benchmark_data)

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

Я думал

class Foo
  def a()
    puts "A"
  end
  ... #repeat for b,c,d,e...
end

f = Foo.new()

add_benchmark(f, [:a,:b,:d]) #records a benchmark event each time f.a,f.b, and f.d are called

У кого-нибудь есть идеи?Если возможно, я бы хотел избежать использования подкласса Foo, потому что тогда подкласс будет необходим для каждого объекта, для которого требуется сравнительный анализ.

Ответы [ 2 ]

0 голосов
/ 13 мая 2012

почему бы тебе не сделать что-то вроде

class Object
  alias :old_method_missing :method_missing
  def method_missing( meth, *args, &block )
    if benchmark_method? meth
      begin
        old_method_missing meth, *args, &block
      rescue Exception => e
        log_errors( "..." )
        raise e
      end
    else old_method_missing meth, *args, &block
    end
  end
end

Я думаю, это говорит само за себя. Может быть, я не поняла, что вы на самом деле имеете в виду ...

0 голосов
/ 09 марта 2012

Вот одно идиоматическое решение Ruby:

def with_stored_benchmark(&block)
  benchmark_data = Benchmark.realtime do
    begin
      block.call
    rescue StandardError => e
      log_errors("error occurred")
      raise e
    end
  end
  write_benchmark_data_to_db(benchmark_data)
end

Вот еще одно решение (вроде):

class Module
  def method_bmark_database(*syms)
    syms.each do |sym|
      old=instance_method(sym)
      define_method(sym) do |*args|
        benchmark_data = Benchmark.realtime do
          begin
            old.call(*args)
          rescue StandardError => e
            log_errors("error occurred")
            raise e
          end
        end
        write_benchmark_data_to_db(benchmark_data)
      end
    end
  end
end

Это обезьяна-патчи Module, ноВы всегда можете поместить его в модуль и включить его.Я не уверен, что он работает, так как сейчас я не за компьютером с Ruby, но он мне подходит.


Кроме того, если вы хотите выбрать, какие объекты сравниваются:

def log_performance_to_DB(obj, *methods)
  methods.each do |m|
    old=obj.method(m)
    obj.define_singleton_method(m) do |*args|
      benchmark_data = Benchmark.realtime do
        begin
          old.call(*args)
        rescue StandardError => e
          log_errors("error occurred")
          raise e
        end
      end
      write_benchmark_data_to_db(benchmark_data)
    end
  end
end
...