在Ruby中以编程方式访问属性/方法注释

有没有办法以编程方式访问方法注释? 或属性评论?

我想用它作为文档中方法的描述,我不希望它是静态的或用rdoc或等效的方法生成。

以下是Ruby类的示例:

Class MyClass ## # This method tries over and over until it is tired def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try puts thing_to_try go_go_go thing_to_try, tries - 1 end end 

基本上,我希望能够做到以下几点:

 get_comment MyClass.gogogo # => This method tries over and over until it is tired 

不,你不能这样做。

评论的全部意义在于它们不是该计划的一部分! 如果你想要一个属于你的程序的字符串,只需使用一个字符串。

在大多数Ruby实现中,注释已经在词法分析器中被抛弃,这意味着它们甚至不会到达解析器 ,更不用说解释器或编译器了。 在代码运行时,注释很快就消失了……事实上,在使用编译器的Rubinius或YARV这样的实现中,根本没有办法将注释存储在已编译的可执行文件中,所以即使它们没有被抛出在词法分析器或解析器之外, 仍然没有办法将它们传达给运行时。

所以,你唯一的机会就是解析Ruby源文件来提取注释。 但是,就像我上面提到的那样,你不能只使用任何解析器,因为大多数现有的解析器会抛出注释。 (这也是评论的重点,因此解析器抛弃它们没有任何问题。)然而,Ruby解析器保留了注释,最明显的是RDoc或YARD等工具中使用的注释。

YARD特别有趣,因为它还包含一个查询引擎,它允许您根据类名称,方法名称,YARD标记,API版本,类型签名等强大的谓词搜索和过滤文档。

但是,如果您最终使用RDoc或YARD进行解析,那么为什么不首先使用它们呢?

或者,就像我上面建议的那样,如果你想要字符串,只需使用字符串:

 module MethodAddedHook private def method_added(meth) (@__doc__ ||= {})[meth] = @__last_doc__ if @__last_doc__ @__last_doc__ = nil super end end class Module private prepend MethodAddedHook def doc(meth=nil, str) return @__doc__[meth] = str if meth @__last_doc__ = str end def defdoc(meth, doc, &block) @__doc__[meth] = doc define_method(meth, &block) end end 

这给了我们一个方法Module#doc ,我们可以用它来记录已经存在的方法,方法是用方法的名称和docstring来调用它,或者你可以用它来记录你定义的下一个方法。 它通过将docstring存储在临时实例变量中,然后定义一个method_added钩子来查看该实例变量并将其内容存储在文档哈希中。

还有Module#defdoc方法,可以一次定义和记录方法。

 module Kernel private def get_doc(klass, meth) klass.instance_variable_get(:@__doc__)[meth] end end 

这是你的Kernel#get_doc方法,它可以取出文档(如果方法没有记录则为nil )。

 class MyClass doc 'This method tries over and over until it is tired' def go_go_go(thing_to_try, tries = 10) puts thing_to_try go_go_go thing_to_try, tries - 1 end def some_other_meth; end # Oops, I forgot to document it! # No problem: doc :some_other_meth, 'Does some other things' defdoc(:yet_another_method, 'This method also does something') do |a, b, c| pa, b, c end end 

在这里,您可以看到记录方法的三种不同方法。

哦,它的工作原理:

 require 'test/unit' class TestDocstrings < Test::Unit::TestCase def test_that_myclass_gogogo_has_a_docstring doc = 'This method tries over and over until it is tired' assert_equal doc, get_doc(MyClass, :go_go_go) end def test_that_myclass_some_other_meth_has_a_docstring doc = 'Does some other things' assert_equal doc, get_doc(MyClass, :some_other_meth) end def test_that_myclass_yet_another_method_has_a_docstring doc = 'This method also does something' assert_equal doc, get_doc(MyClass, :yet_another_method) end def test_that_undocumented_methods_return_nil assert_nil get_doc(MyClass, :does_not_exist) end end 

注意:这非常hacky。 例如,没有锁定,因此如果两个线程同时为同一个类定义方法,则文档可能会被搞砸。 (即:docstring可能归因于错误的方法或迷路。)

我相信rake与它的desc方法基本上是一样的,并且代码库比这更好地测试,所以如果你打算在生产中使用它,我会窃取Jim的代码而不是我的代码。

词法分析器(通常)抛弃注释,并且在执行时不能在Ruby的符号表中使用

我认为你能做的最接近的是

(a)以这样的方式实现get_comment,即动态创建正则表达式并在源文件中搜索匹配项。 您需要像这样更改语法…

  get_comment :MyClass, :go_go_go 

您可以将符号转换为字符串,假设源文件是myclass.rb并在其中搜索comment-def-method_name模式的匹配项。

(b)从每个源文件调用一个方法来构建一个全局注释表。

无论如何,它比它的价值更麻烦,更麻烦。

同时,有一个“标准”gem method_source可以解决其中的一些问题:

https://github.com/banister/method_source

 Set.instance_method(:merge).comment Set.instance_method(:merge).source 

它还附带最近的Rails( railties = = 5.0 )版本,并由Pry引用。