在 class 的每个方法的开头插入代码

Insert code into the beginning of each method of a class

如何在不实际手动插入代码的情况下,动态轻松地将代码插入 class 和 subclass 的每个方法的开头?我想要宏之类的东西。

class C1
   def m1
     @i_am = __method__
   end

   def m2
      @i_am = __method__
   end
 end

这是我想避免重复的示例之一。

假设功能相同,您可以创建一个模块并将其包含在您的 类 中。

示例:

module MyModule
  def test_method
    puts "abc"
  end
end

class MyClass
  include MyModule
  def my_method
    puts "my method"
  end
end

inst = MyClass.new
inst.test_method # => should print "abc"
inst.my_method   # => should print "my method"

您可以使用 rails 之类的 class 装饰器。下面的代码片段正在呈现一个名为 before_action 的方法,该方法定义在 ActiveRecord 模块的 Base class 中。 Test class 继承自ActiveRecord。如果我们想从 Base class 中显式调用某些内容,则使用 define_method

module ActiveRecord
    class Base

        def self.before_action(name)
            puts "#{name}"
            puts "inside before_action of class Base"

            define_method(name) do
                puts "Base: rendering code from Base class"
            end
        end
    end
end

class Test < ActiveRecord::Base

    before_action :hola

    def render()
        puts "inside render of class Test"
    end

end

test = Test.new
test.render
test.hola

它有输出

hola
inside before_action of class Base
inside render of class Test
Base: rendering code from Base class

因此,在 运行 render 方法之前,它运行 Base class 中的 before_action 方法。它可以应用于 Test class 中的所有其他方法。这是 ruby 中表示宏的一种方式。

我最初误解了这个问题(但在下面的水平线之后留下了我原来的答案)。相信以下可能是您要找的。

class C1
  [:m1, :m2].each do |m|
    define_method(m) do |name|
      @i_am = __method__
      puts "I'm #{name} from method #{@i_am}"
    end
  end
end

C1.instance_methods(false)
  #=> [:m1, :m2] 
c1 = C1.new
  #=> #<C1:0x007f94a10c0b60> 
c1.m1 "Bob"
  # I'm Bob from method m1
c1.m2 "Lucy"
  # I'm Lucy from method m2

我原来的解决方案如下。

class C1
  def add_code_to_beginning(meth)
    meth = meth.to_sym
    self.class.send(:alias_method, "old_#{meth}".to_sym, meth)
    self.class.send(:define_method, meth) do
      yield
      send("old_#{meth}".to_sym)
    end
  end
end

Module#alias_method Module#define_method 是私有的;因此需要使用 send.

c = C1.new
  #=> #<C1:0x007ff5e3023650> 
C1.instance_methods(false)
  #=> [:m1, :m2, :add_code_to_beginning] 

c.add_code_to_beginning(:m1) do
  puts "hiya"
end

C1.instance_methods(false)
  #=> [:m1, :m2, :add_code_to_beginning, :old_m1] 
c.m1
  # hiya
  #=> :m1