Доступ к атрибутам / методам комментариев программно в Ruby - PullRequest
4 голосов
/ 01 июня 2010

Есть ли способ для программного доступа к комментариям метода? или атрибут комментария?

Я хотел бы использовать его в качестве описания метода в документации, которую я не хочу использовать как статическую или сгенерированную с помощью 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

Ответы [ 3 ]

13 голосов
/ 01 июня 2010

Нет, вы не можете сделать это.

Весь смысл комментариев в том, что они не часть программы! Если вам нужна строка, которая является частью вашей программы, просто используйте вместо нее строку.

В большинстве реализаций Ruby комментарии уже отбрасываются в лексере, что означает, что они даже не достигают парсера , не говоря уже о интерпретаторе или компиляторе. Когда код запускается, комментарии уже давно исчезли & hellip; Фактически, в реализациях, таких как Rubinius или YARV, в которых используется компилятор, просто невозможно сохранить комментарии в скомпилированном исполняемом файле, поэтому, даже если они не были выброшены лексером или парсером, все равно не будет возможности сообщить их среде выполнения.

Итак, в основном ваш единственный шанс - проанализировать исходный файл Ruby для извлечения комментариев. Однако, как я упоминал выше, вы не можете просто взять любой парсер, потому что большинство существующих парсеров выбрасывают комментарии. (Который, опять же, является целым пунктом комментариев, так что нет ничего плохого в том, что парсер выбрасывает их.) Однако, есть парсеры Ruby, которые сохраняют комментарии, особенно те, которые используются в таких инструментах, как RDoc или YARD.

YARD особенно интересен, поскольку он также содержит механизм запросов, который позволяет вам искать и отфильтровывать документацию на основе некоторых мощных предикатов, таких как имя класса, имя метода, теги YARD, версия API, сигнатура типа и т. Д.

Однако, если вы do в конечном итоге используете 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

Примечание: это довольно забавно. Например, нет блокировки, поэтому, если два потока определяют методы для одного и того же класса одновременно, документация может быть испорчена. (Т.е.: строка документа может быть приписана неверному методу или потеряна.)

Я считаю, что rake делает то же самое с его desc методом, и что кодовая база на намного лучше протестирована, чем эта, поэтому, если вы намереваетесь использовать ее в производстве, я бы укради код Джима вместо моего.

1 голос
/ 06 ноября 2018

Между тем, существует "стандартный" гем method_source, который решает некоторые из этих проблем:

https://github.com/banister/method_source

Set.instance_method(:merge).comment

Set.instance_method(:merge).source

Также поставляется с последними версиями Rails ( railties> = 5.0 ) и используется Pry под капотом.

1 голос
/ 01 июня 2010

Комментарии (обычно) выбрасываются лексером и недоступны в таблицах символов для Ruby во время выполнения .

Я думаю, что самое близкое, что вы могли бы сделать, это либо

(a) Реализуйте get_comment таким образом, чтобы он на лету создавал регулярное выражение и искал в исходном файле совпадение. Вам нужно изменить ваш синтаксис следующим образом ...

 get_comment :MyClass, :go_go_go

Вы должны преобразовать символы в строки, предположив, что исходным файлом является myclass.rb, и найти в нем совпадение по шаблону comment-def-method_name.

(b) Иметь метод, вызываемый из каждого исходного файла, который создал глобальную таблицу комментариев.

Несмотря на это, это грязно и больше хлопот, чем стоит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...