所以我有一堆看起来像这样的代码:
module Foo
def detect(this_message)
#check for timeout
if Time.now > instance_variable_get("@#{this_message}_timeout".to_sym)
@state_machine.method("#{this_message}_timed_out".to_sym).call
return
end
yield
record
rescue StandardError => e
# retry on exception
@state_machine.method("#{this_message}_retry".to_sym).call(exception: e)
end
# a bunch of these
def detect_blah
detect(:blah) do
# detection code
@state_machine.method("#{this_message}_detected".to_sym).call
# or failed, you get the idea
end
end
end
...
class Bar
include Foo
# more stuff
end
我想取消def detect_blah
声明。我想简单地说detect(:blah)
并让它动态添加detect_blah
方法,其中包括与上述相同的处理,包括已产生的块。
我尝试了一些define_method
的排列。
如果我只是从define_method
调用detect
我得到NoMethodError,这是有道理的,因为我们在模块构建时调用detect并且模块(类?)无法调用它没有构建时自己的方法(对吧?)。
如果我将它添加到另一个模块并将该模块包含在此模块中,我会收到相同的错误。
我见过self.class.send(:define_method, method_name, method_definition)
的代码,但我认为我没有达到足够的效果。
也许有一种方法可以通过元类...来实现这一点。没看到如何为模块做这件事。 HM。
有没有合理的方法来做我想做的事情?
答案 0 :(得分:1)
试试这个:
module Foo
def detect(this_message, &block)
# boilerplate stuff using instance_variable_get(blah) and
# calling methods on instance variables...
yield block
# more boilerplate stuff
self.class.send(:define_method, "detect_#{this_message.to_s}") do
puts "This is templated detection code for #{this_message.to_s}"
# blah
end
end
end
答案 1 :(得分:0)
在对该问题的评论中,OP清楚地表明所创建的所有方法都将执行相同的操作并返回相同的值。这意味着在第一个之后创建的所有实例方法都可以简单地成为原始实例方法的 aliases 。 (目前尚不清楚为什么这样做会有所帮助,但这不是重点。)在模块Foo
中,我调用了原始的即时方法boilerplate
。我们可以写下面的内容:
module Foo
def detect(method_name)
self.class.send(:alias_method, method_name, :boilerplate)
end
def boilerplate
yield "Spud"
end
end
class Bar
include Foo
end
见Module#alias_method。我们需要使用send
,因为alias_method
是私有方法。
我们现在可以写下以下内容。
Bar.instance_methods && [:detect, :boilerplate]
#=> [:detect, :boilerplate]
b = Bar.new
b.detect(:say)
b.detect("hey")
Bar.instance_methods(false)
#=> [:say, :hey]
b.say { |name| "My name is #{name}" }
#=> "My name is Spud"
b.hey { |name| "My name is #{name}" }
#=> "My name is Spud"