我正在尋找一種方法來生成 Ruby 類中所有方法的串列,類似于 C 頭檔案。我想要這個,這樣我就可以看到類的概述,而無需使用 IDE 代碼折疊來折疊所有內容。
最好這將是一個 *nix 命令列函式,以便我可以使用其他命令列工具進一步處理它。
輸出將類似于
def foo( a, b, c )
def bar
def baz( d )
類似于這個問題,除了我正在尋找一個包含引數的串列,我想輸出到命令列以便進一步處理。目標是讓我在作業時可以查看此串列,因此我希望它一目了然。
uj5u.com熱心網友回復:
使用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?? 腳本中即可。
uj5u.com熱心網友回復:
如果你想獲得一個定義的所有方法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
但是,請注意,列出某個模塊的方法并不會為您提供該模塊的協議(即可以理解的訊息集)!
Here are two simple examples where the set of methods defined in a module does not correspond to the set of messages understood by instances of that module:
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
While this is a stupid example, and code that hopefully nobody would write for real, it clearly shows that while Foo has a method named bar, its instances do not respond to a bar message the way you would expect.
Here is a more realistic example:
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!'
And finally, just in case you get your hopes up that you can find some insane meta-programming abstract interpretation trick that actually lets you list out the entire protocol of a module, let me disabuse you of that notion:
class Quux
def method_missing(*); end
def respond_to_missing?(*) = true
end
Voilà:一個類,它的實體回應無限數量的訊息,事實上,它們回應每一個可能的訊息。如果您認為這不切實際,那么實際上,Ruby 世界中使用最廣泛的庫之一就是這樣做的:ActiveRecord。
uj5u.com熱心網友回復:
使用 TypeProf 創建 RBS 檔案
對于 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 的優勢所在:它們基于實際代碼,而不是程式員編輯到檔案中的注釋。因此,您的里程將根據您的用例而有所不同。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/359084.html
