ruby - 如何将 "magically"代码添加到ruby中的所有公共(public)类方法?

标签 ruby metaprogramming

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

我找到了 this answer有帮助,但对重复没有帮助。

class MyClass
  def initialize
    [:a, :b].each{ |method| add_code(method) }
  end

  def a
    sleep 1
    "returning from a"
  end

  def b
    sleep 1
    "returning from b"
  end

  private

  def elapsed
    start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    block_value = yield
    finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    puts "elapsed: #{finish - start} seconds, block_value: #{block_value}."
    block_value
  end

  def add_code(meth)
    meth = meth.to_sym
    self.singleton_class.send(:alias_method, "old_#{meth}".to_sym, meth)
    self.singleton_class.send(:define_method, meth) do
      elapsed do
        send("old_#{meth}".to_sym)
      end
    end
  end
end

以上确实有效,但更优雅的解决方案是什么?例如,我希望能够将 attr_add_code 放在类定义的开头,并列出我希望将代码添加到的方法,或者甚至可以指定我希望将它添加到所有 public方法。

注意:self.singleton_class 只是一种解决方法,因为我在初始化期间添加了代码。

最佳答案

如果重复是指要检测的方法列表,那么您可以执行以下操作:

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 并且在prepended 回调中你可以找到在类上定义的方法列表instance_methods(false)false参数表示不应考虑祖先。

关于ruby - 如何将 "magically"代码添加到ruby中的所有公共(public)类方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56508425/

相关文章:

ruby - 为什么 Heroku worker 有 100% dyno 负载?

知道其参数位置的 C++ 模板

c++ - 不能使用 if constexpr

c++ - 为什么允许嵌套类模板的部分特化,而不允许完全特化?

ruby-on-rails - 由于 Selenium 参数错误,Watir 浏览器初始化失败

ruby - bundle exec pod install -> 无法找到 URL 为 "trunk"的已知源

ruby-on-rails - 如何使用 rspec 和 capybara 测试索引/确认/完成流页面?

python - 如何覆盖类的 __dir__ 方法?

java - 有没有办法在 Java 中使用注解来代替访问器?

ruby-on-rails - 创建不带参数 params 的新 Ruby 字符串助手