在 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
如何在不实际手动插入代码的情况下,动态轻松地将代码插入 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