[英]How to get a detailed list of methods defined in a Ruby class via the command line?
我正在尋找一種方法來生成 Ruby 類中所有方法的列表,類似於 C 頭文件。 我想要這個,這樣我就可以看到類的概述,而無需使用 IDE 代碼折疊來折疊所有內容。
最好這將是一個 *nix 命令行函數,以便我可以使用其他命令行工具進一步處理它。
輸出將類似於
def foo( a, b, c )
def bar
def baz( d )
類似於這個問題,除了我正在尋找一個包含參數的列表,我想輸出到命令行以便進一步處理。 目標是讓我在工作時可以查看此列表,因此我希望它一目了然。
對該類使用instance_methods
然后查詢參數
例如
class A
def foo
end
def bar(a,b,c)
end
end
A.instance_methods(false).each do |s|
print "def #{s}(#{A.instance_method(s).parameters})\n"
end
輸出:
def foo([])
def bar([[:req, :a], [:req, :b], [:req, :c]])
您可能需要獲取子進程參數數組才能僅獲取名稱。
至於命令行,只需將其保存到 ruby 腳本中即可。
如果你想獲得一個定義的所有方法Module
,你可以使用一個Module#instance_methods
家庭的方法,這取決於,究竟是什么,你正在尋找:
Module#public_instance_methods
用於所有public
方法,Module#protected_instance_methods
用於所有protected
方法,Module#instance_methods
用於所有public
和protected
方法,或Module#private_instance_methods
用於所有private
方法。 每一個都有一個可選的布爾參數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
但是,請注意,列出某個模塊的方法並不會為您提供該模塊的協議(即可以理解的消息集)!
以下是兩個簡單示例,其中模塊中定義的方法集與該模塊的實例理解的消息集不對應:
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。
對於 Ruby >= 2.7.1,正確的答案通常是使用RBS或TypeProf創建(非常粗略的)頭文件的等效項。 由於您發布的代碼此時甚至不是類,並且不包含任何可推斷的類型信息,因此您的大多數類型都可能是“無類型的”,並且由您來填寫類型。
Ruby 本身不處理類型檢查。 為此,您需要使用 Steep、Sorbet 或類似的東西。 也就是說,出於文檔目的,如果您還沒有良好的 YARD 文檔,TypeProf 可能是您最好的選擇,而 RBS 原型設計是合理的后備方案。 例如,給定以下 Ruby 源文件:
class Example
def foo(a, b, c); end
def bar; end
def baz(d); end
end
運行typeprof example.rb
會產生:
# TypeProf 0.20.2
# Classes
class Example
def foo: (untyped a, untyped b, untyped c) -> nil
def bar: -> nil
def baz: (untyped d) -> nil
end
在可以通過 TypeProf 構建、解析 AST 和運行代碼路徑的實際代碼庫中,它在推斷常見類型方面做得相當合理,盡管有一些例外,並且它在某些元編程結構中表現不佳。 盡管如此,它在大多數情況下都能滿足您的要求。
不過老實說,除非您打算進行類型檢查,否則從文檔的角度來看,對@param和@return使用 YARD 標簽通常會產生更有用的結果。 基於文檔的類型的問題在於必須積極維護文檔; 否則,文檔可能基於程序員的錯誤或疏忽而撒謊。 這就是 RBS 和 TypeProf 的優勢所在:它們基於實際代碼,而不是程序員編輯到文件中的注釋。 因此,您的里程將根據您的用例而有所不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.