如果您想获取Module 中定义的所有方法,您可以使用Module#instance_methods 系列方法之一,具体取决于确切您要查找的内容: p>
每一个都有一个可选的布尔参数include_super=true,它可以让你决定是包括继承的方法(默认)还是只从你将消息发送到的确切模块中返回方法(当传递false时)。
如果你想获取这些方法的参数,你首先需要获取一个UnboundMethod反射代理对象,它代表你感兴趣的方法。你可以使用Module#instance_method来做到这一点。
一旦有了UnboundMethod,就可以使用UnboundMethod#parameters 来获取方法参数的描述。但是请注意,您确实不会获得可选参数的默认参数。这实际上是不可能的。
使用这些构建块,您可以构建如下内容:
class MethodHeaderFormatter
private
attr_accessor :name, :parameter_list
def initialize(name, parameter_list)
self.name = name
self.parameter_list = MethodParameterListFormatter.new(parameter_list)
end
public
def to_s = "def #{name}" + if parameter_list.empty? then '' else "(#{parameter_list})" end
class MethodParameterListFormatter
private
attr_accessor :parameter_list
def initialize(parameter_list)
self.parameter_list = parameter_list.map(&MethodParameterFormatter.method(:[]))
end
public
def empty? = parameter_list.empty?
def to_s = parameter_list.join(', ')
module MethodParameterFormatter
private
attr_accessor :name, :prefix, :suffix
def initialize(name) = self.name = name
public
def self.[]((type, name)) = const_get(:"#{type.capitalize}MethodParameterFormatter").new(name)
def to_s = "#{prefix}#{name}#{suffix}"
class ReqMethodParameterFormatter; include MethodParameterFormatter end
class OptMethodParameterFormatter
include MethodParameterFormatter
def initialize(name)
super
self.suffix = '=unknown'
end
end
class RestMethodParameterFormatter
include MethodParameterFormatter
def initialize(name)
super
self.prefix = '*'
end
end
class KeyreqMethodParameterFormatter
include MethodParameterFormatter
def initialize(name)
super
self.suffix = ':'
end
end
class KeyMethodParameterFormatter
include MethodParameterFormatter
def initialize(name)
super
self.suffix = ': unknown'
end
end
class KeyrestMethodParameterFormatter
include MethodParameterFormatter
def initialize(name)
super
self.prefix = '**'
end
end
class BlockMethodParameterFormatter
include MethodParameterFormatter
def initialize(name)
super
self.prefix = '&'
end
end
private_constant *constants
end
private_constant *constants
end
private_constant *constants
end
你可以这样使用它:
module Test
def foo(a, b, c) end
def bar; end
def baz(d) end
def quux(m, o = 23, *r, k:, ok: 42, **kr, &b) end
alias_method :blarf, :quux
attr_accessor :frotz
end
puts Test.public_instance_methods(false).map { |meth| MethodHeaderFormatter.new(meth, Test.instance_method(meth).parameters) }
# def baz(d)
# def quux(m, o=unknown, *r, k:, ok: unknown, **kr, &b)
# def frotz=()
# def blarf(m, o=unknown, *r, k:, ok: unknown, **kr, &b)
# def frotz
# def foo(a, b, c)
# def bar
但是,请注意,列出某些模块的方法并不为您提供该模块的协议(即可以理解的消息集)! p>
这里有两个简单的示例,其中模块中定义的方法集与该模块实例所理解的消息集不对应:
class Foo
def bar = raise(NoMethodError)
def respond_to?(meth) = meth != :bar && super
end
foo = Foo.new
foo.respond_to?(:bar) #=> false
foo.bar # NoMethodError
虽然这是一个愚蠢的例子,而且代码希望没人会真正编写,但它清楚地表明虽然 Foo 有一个名为 bar 的方法,但它的实例不会像您那样响应 bar 消息会期待的。
这是一个更现实的例子:
class Bar
def method_missing(meth, *) = if meth == :foo then 'Fooooo!' else super end
def respond_to_missing?(meth, *) = meth == :foo || super
end
bar = Bar.new
bar.respond_to?(:foo) #=> true
bar.foo #=> 'Fooooo!'
最后,如果你希望你能找到一些疯狂的元编程抽象解释技巧,实际上可以让你列出模块的整个协议,让我打消你的想法:
class Quux
def method_missing(*); end
def respond_to_missing?(*) = true
end
Voilà:一个类,其实例响应无限数量的消息,实际上,它们响应每条可能的消息。如果您认为这不切实际,那么实际上,Ruby 世界中使用最广泛的库之一就是这样:ActiveRecord。