【问题标题】:Access attributes/methods comments programmatically in Ruby在 Ruby 中以编程方式访问属性/方法注释
【发布时间】:2011-02-26 06:14:41
【问题描述】:

有没有办法以编程方式访问方法 cmets?还是一个属性 cmets?

我想用它作为文档中方法的描述,我不希望它是静态的或使用 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-on-rails ruby comments


    【解决方案1】:

    不,你不能这样做。

    cmets 的全部意义在于它们不是程序的一部分!如果您想要一个字符串作为程序的一部分,只需使用字符串即可。

    在大多数 Ruby 实现中,cmets 已经在词法分析器中被丢弃,这意味着它们甚至无法到达 解析器,更不用说 解释器 或编译器了。在代码运行时,cmets 早已不复存在……事实上,在 Rubinius 或 YARV 等使用编译器的实现中,根本没有办法将 cmets 存储在编译后的可执行文件中,所以即使它们没有被抛出远离词法分析器或解析器,仍然无法将它们传达给运行时。

    因此,您几乎唯一的机会就是解析 Ruby 源文件以提取 cmets。但是,就像我上面提到的那样,您不能只使用 any 解析器,因为大多数现有的解析器都会丢弃 cmets。 (这又是 cmets 的全部意义,因此解析器将它们丢弃并没有错。)然而,有保留 cmets 的 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,我们可以用它来记录一个已经存在的方法,通过方法名和一个文档字符串来调用它,或者你可以用它来记录你定义的下一个方法。它通过将文档字符串存储在一个临时实例变量中,然后定义一个 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|
        p a, 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。例如,没有锁定,所以如果两个线程同时为同一个类定义方法,文档可能会搞砸。 (即:文档字符串可能归因于错误的方法或丢失。)

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

    【讨论】:

    • 我必须承认,这是一个写得很好的答案。感谢您对此的见解。你让我下定了决心:我永远不会为我的应用做这样的事情。
    【解决方案2】:

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

    https://github.com/banister/method_source

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

    它还附带最新的 Rails (railties >= 5.0) 版本,并被 Pry 使用。

    【讨论】:

      【解决方案3】:

      注释(通常)被词法分析器丢弃,并且在执行时在符号表中对 Ruby 不可用

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

      (a) 以这样一种方式实现 get_comment,即它会动态创建一个正则表达式并在源文件中搜索匹配项。你需要像这样改变你的语法......

       get_comment :MyClass, :go_go_go
      

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

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

      无论如何,这很混乱,而且比它的价值更麻烦。

      【讨论】:

      • 有道理。我想我会改变主意并重新考虑我的需求。谢谢
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      • 2023-01-10
      相关资源
      最近更新 更多