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

Arn*_*met 4 ruby ruby-on-rails code-comments

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

我想用它作为文档中方法的描述,我不希望它是静态的或用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
Run Code Online (Sandbox Code Playgroud)

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

get_comment MyClass.gogogo # => This method tries over and over until it is tired
Run Code Online (Sandbox Code Playgroud)

Jör*_*tag 13

不,你不能这样做.

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

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

所以,你唯一的机会就是解析Ruby源文件来提取注释.但是,就像我上面提到的那样,你不能只使用任何解析器,因为大多数现有的解析器会抛出注释.(这又是整点的意见,所以没有什么不对的解析器扔掉.)有,但是,红宝石解析器其保留意见,最显着的工具,如RDoc的或者YARD所使用的那些.

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

但是,如果你做的最终使用的RDoc或院子里解析,那么为什么不首先使用它们?

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

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
Run Code Online (Sandbox Code Playgroud)

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

还有Module#defdoc一种方法可以一次定义和记录该方法.

module Kernel
  private

  def get_doc(klass, meth)
    klass.instance_variable_get(:@__doc__)[meth]
  end
end
Run Code Online (Sandbox Code Playgroud)

这是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|
    p a, b, c
  end
end
Run Code Online (Sandbox Code Playgroud)

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

哦,它有效:

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
Run Code Online (Sandbox Code Playgroud)

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

我认为,rake做实质上是一样的,其desc方法和代码库很多比这更好的测试,所以如果你打算在生产中使用它,我会偷吉姆的代码,而不是我的.