假设我有以下内容:
class MyWrapper
def self.wraps_methods(mtd)
mtd = Array.wrap(mtd)
mtd.each do |m|
new_name = m.to_sym
old_name = "old_#{m}".to_sym
alias_method old_name, new_name
class_eval do
define_method(new_name) do
begin
send(old_name)
rescue StandardError => e
# do something with error
end
end
end
end
end
end
和一个继承自:
的类 class MyChildClass < MyWrapper
wraps_methods :first, :second
def first
# does something
end
def second
# does something else
end
end
问题是,如果我在声明first
和second
之后放了wraps_methods,就像这样:
class MyChildClass < MyWrapper
def first
# does something
end
def second
# does something else
end
wraps_methods :first, :second
end
然后,它有效。如果我在开头保留wraps_methods,我会得到未定义的方法。我知道这是因为解释器还没有评估第一个和第二个的代码,但是有没有办法进行某种延迟/延迟评估,这样我可以在声明第一个和第二个方法之前保留wraps_methods?< / p>
答案 0 :(得分:2)
从2.0开始,你可以预装一个模块,有效地使它成为前置类的代理。
在下面的示例中,调用扩展模块模块的方法,传递要包装的方法的名称。对于每个方法名称,都会创建并预先添加一个新模块。这是为了简化代码。您还可以将多个方法附加到单个代理。
当然,您可以使用父类的方法来执行相同的操作。
使用alias_method和instance_method的解决方案的一个重要区别是,您可以在定义方法之前定义要包装的方法。
module Prepender
def wrap_me(*method_names)
method_names.each do |m|
proxy = Module.new do
define_method(m) do |*args|
puts "the method '#{m}' is about to be called"
super *args
end
end
self.prepend proxy
end
end
end
使用:
class Dogbert
extend Prepender
wrap_me :bark, :deny
def bark
puts 'Bah!'
end
def deny
puts 'You have no proof!'
end
end
Dogbert.new.deny
# => the method 'deny' is about to be called
# => You have no proof!
作为一种懒惰的评估,您可以利用Module#method_added。
的强大功能答案 1 :(得分:1)
我得到了它:
class MyWrapper
def self.wraps_methods(*mtd)
@wrapped_methods = mtd
mtd.each do |m|
wrap_method(m) if instance_methods.include?(m)
end
end
def self.wrap_method(m)
new_name = m.to_sym
old_name = "old_#{m}".to_sym
alias_method old_name, new_name
class_eval do
Thread.exclusive do
@skip_method_added = true
define_method(new_name) do
begin
send(old_name)
rescue StandardError => e
puts e
end
end
@skip_method_added = false
end
end
end
def self.method_added(m)
return if @skip_method_added
wrap_method(m) if @wrapped_methods.include?(m)
end
def self.inherited(klass)
klass.instance_variable_set('@wrapped_methods', [])
end
end
class A < MyWrapper
wraps_methods :foo
def foo
raise 'hello'
end
end
A.new.foo #=> hello