根据 Ruby 中的现有方法定义方法
Define Method Based on Existing Method in Ruby
我正在尝试对一种基于谓词方法定义 bang 方法的方法进行元编程。现在我有了想要使用的行为 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
似乎是不错的第一步,但我不确定如何将其转换为块参数或处理默认值。
无需使用 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)
,它会为 class 上的每个谓词添加一个 bang 方法,您可以像您提到的那样使用 instance_methods
来获取方法列表和 class_eval
在 class 的上下文中定义这些方法。所以粗略的大纲是:
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
我正在尝试对一种基于谓词方法定义 bang 方法的方法进行元编程。现在我有了想要使用的行为 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
似乎是不错的第一步,但我不确定如何将其转换为块参数或处理默认值。
无需使用 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)
,它会为 class 上的每个谓词添加一个 bang 方法,您可以像您提到的那样使用 instance_methods
来获取方法列表和 class_eval
在 class 的上下文中定义这些方法。所以粗略的大纲是:
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