在 Ruby 中以编程方式访问属性/方法注释
Posted
技术标签:
【中文标题】在 Ruby 中以编程方式访问属性/方法注释【英文标题】:Access attributes/methods comments programmatically in 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
【问题讨论】:
【参考方案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) 从每个构建全局注释表的源文件中调用一个方法。
无论如何,这很混乱,而且比它的价值更麻烦。
【讨论】:
有道理。我想我会改变主意并重新考虑我的需求。谢谢以上是关于在 Ruby 中以编程方式访问属性/方法注释的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 中以编程方式访问和更新 stackview 中的 label.text 或其他 UILabel 属性