我正在尝试使用基于谓词方法定义爆炸方法的元编程方法。现在我有我想要使用method_missing
的行为:
class PredicateBang
def true?
true
end
def false?
false
end
def method_missing(method, *args, &block)
if bang_match = /\A([^?]+)!\z/.match(method.to_s)
predicate_method = :"#{bang_match[1]}?"
if respond_to?(predicate_method)
unless send(predicate_method, *args, &block)
raise "#{predicate_method} is false"
end
return
end
end
super.method_missing(method, *args, &block)
end
end
PredicateBang.new.true!
PredicateBang.new.false! # false? is false (RuntimeError)
不是覆盖method_missing
,而是通过迭代instance_methods(false)
并使用define_method
为任何结束的方法创建一个bang方法来动态定义方法。带有匹配参数的问号,但我不确定如何反映方法的所有细节。
Method#parameters
似乎是一个不错的第一步,但我不确定如何将其转换为阻止参数或处理默认值。
答案 0 :(得分:2)
无需使用Method#parameters
,您可以使用*args
捕获传递的参数(包括默认值)并使用&block
样式参数传递任何块,就像您一样与method_missing
合作。所以一个基本的例子是
define_method(bang_method) do |*args, &block|
unless send(predicate_method, *args, &block)
raise "#{predicate_method} is false"
end
end
现在要添加一个方法add_bang_methods(clazz)
来为每个谓词添加一个bang方法,你可以使用instance_methods
来提取方法列表和class_eval
在类的上下文中定义这些方法。所以粗略的轮廓将是:
def add_bang_methods(clazz)
clazz.instance_methods.each do |method_name|
if predicate_match = /\A([^?]+)\?\z/.match(method_name.to_s)
bang_method = :"#{predicate_match[1]}!"
clazz.class_eval do
define_method(bang_method) do |*args, &block|
unless send(method_name, *args, &block)
raise "#{method_name} is false"
end
end
end
end
end
end