如何";“神奇地”;向ruby中的所有公共类方法添加代码?

如何";“神奇地”;向ruby中的所有公共类方法添加代码?,ruby,metaprogramming,Ruby,Metaprogramming,我希望能够在类中方法的开头和结尾插入一些代码。我也希望避免重复 我发现这很有帮助,但对重复并没有帮助 class-MyClass def初始化 [:a,:b]。每个{方法{添加{u代码(方法)} 结束 定义a 睡眠1 “从一家公司回来” 结束 def b 睡眠1 “从b回来” 结束 私有的 def消耗 start=Process.clock\u gettime(Process::clock\u单调) 区块价值=收益率 finish=Process.clock\u gettime(Process:

我希望能够在类中方法的开头和结尾插入一些代码。我也希望避免重复

我发现这很有帮助,但对重复并没有帮助

class-MyClass
def初始化
[:a,:b]。每个{方法{添加{u代码(方法)}
结束
定义a
睡眠1
“从一家公司回来”
结束
def b
睡眠1
“从b回来”
结束
私有的
def消耗
start=Process.clock\u gettime(Process::clock\u单调)
区块价值=收益率
finish=Process.clock\u gettime(Process::clock\u单调)
放入“已用:{finish-start}秒,块值:{block值}”
块值
结束
def添加_代码(甲基)
meth=meth.to_sym
self.singleton_class.send(:别名_方法,“old#{meth}”。to_sym,meth)
self.singleton\u class.send(:define\u method,meth)do
过去的事
发送(“old#{meth}”。发送至_sym)
结束
结束
结束
结束
上述方法确实有效,但有什么更优雅的解决方案呢?例如,我希望能够将
attr\u add\u code
放在类定义的开头,列出我希望将代码添加到的方法,或者甚至指定我希望将其添加到所有公共方法中


注意:
self.singleton\u类
只是一种变通方法,因为我在初始化过程中添加了代码。

看起来您正在尝试进行一些基准测试。你查过那张支票了吗?它在标准图书馆里

require 'benchmark'
puts Benchmark.measure { MyClass.new.a }
puts Benchmark.measure { MyClass.new.b }

如果重复是指要插入的方法列表,则可以执行以下操作:

module Measure
  def self.prepended(base)
    method_names = base.instance_methods(false)

    base.instance_eval do
      method_names.each do |method_name|
        alias_method "__#{method_name}_without_timing", method_name
        define_method(method_name) do
          t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
          public_send("__#{method_name}_without_timing")
          t2 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
          puts "Method #{method_name} took #{t2 - t1}"
        end
      end
    end
  end
end

class Foo
  def a
    puts "a"
    sleep(1)
  end
  def b
    puts "b"
    sleep(2)
  end
end

Foo.prepend(Measure)

foo = Foo.new
foo.a
foo.b

# => a
# => Method a took 1.0052679998334497
# => b
# => Method b took 2.0026899999938905

主要的变化是我使用了
prepend
,在
prepend
回调中,您可以找到使用
instance\u methods(false)
在类上定义的方法列表,该
false
参数指示不应考虑祖先。

而不是使用方法别名,在我看来,自从引入以来,这已经是过去的事情了,我们可以预先准备一个匿名模块,该模块对于要度量的类的每个实例方法都有一个方法。这将导致调用
MyClass#a
调用此匿名模块中的方法,该模块测量时间并简单地诉诸
super
调用实际的
MyClass#a
实现

def测量(klass) mod=模块。新do klass.instance_方法(false)。每个do|方法| 定义_方法(方法)do |*args和blk| start=Process.clock\u gettime(Process::clock\u单调) 值=超级(*参数和黑色) finish=Process.clock\u gettime(Process::clock\u单调) 放入“已用:{finish-start}秒,值:{value}” 价值 结束 结束 结束 klass.prepend(国防部) 结束 或者,您可以使用
class\u eval
,这也更快,并且允许您只调用
super
,而不指定任何参数来转发方法调用中的所有参数,这在
define\u method
中是不可能的

def测量(klass) mod=模块。新do klass.instance_方法(false)。每个do|方法|
class_eval另一种可能是创建如下包装器类:

class Measure < BasicObject
  def initialize(target)
    @target = target
  end

  def method_missing(name, *args)
    t1 = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
    target.public_send(name, *args)
    t2 = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
    ::Kernel.puts "Method #{name} took #{t2 - t1}"
  end

  def respond_to_missing?(*args)
    target.respond_to?(*args)
  end

  private

  attr_reader :target
end

foo = Measure.new(Foo.new)

foo.a
foo.b
类度量
你说的“重复”是什么意思?标题引用类方法,但您只有公共实例方法。如果您给出一个示例,说明“添加”代码前后的方法,则If将非常有用。我认为一种方法就足够了。也许我们可以通过研究您的代码来了解您想要做什么,但这不是必需的。谢谢,但我正在做一些比基准测试更复杂的事情。我没有把它包括在我的示例代码中,因为它是不相关的。如果您能提供更多关于更广泛的上下文的详细信息,以及您试图对计时信息做什么,这将是很有帮助的。登录Rails并只关心端到端定时?考虑一个机架中间件。正在查找完整的堆栈配置文件?考虑一个貌似宝石,比如我喜欢“Pascal的<代码>准备/ <代码>回答你的问题的直接答案,但是我想你会发现如果你分享你想要完成的更大的画面,你会得到更好的帮助。它可以带来完全不同的方法来解决这个问题沿着
alias_method“{method_name}的思路,方法_name
可能更惯用一些,表明不应该调用原始方法,但这是一个好的解决方案。您也可以对
方法\u missing
执行类似的操作,但这可能会在一天结束时在堆栈中更快更清晰。@DavidBodow添加了
\u
前缀。不确定如何解决方法缺失的问题,因为方法
a
并不是真正缺失的(需要重命名该方法,这似乎有点过分了)。哈!这正是我自己写了一半作为答案的东西。投票表决。