Доступ к атрибутам / методам комментариев программно в Ruby

StackOverflow https://stackoverflow.com/questions/2948328

Вопрос

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

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

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

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

Это дает нам метод 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|
    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 метод, и что кодовая база много Лучше проверено, чем это, поэтому, если вы собираетесь использовать его в производстве, я бы украл код Джима вместо моего.

Другие советы

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

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

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

 get_comment :MyClass, :go_go_go

Вы будете преобразовывать символы на строки, предположим, что исходный файл является MyClass.rb и поиск в нем для матча на шаблоне Comment-Def-Def-Method_NAME.

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

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

Между тем, есть «стандартный» драгоценный камень method_source Это решает некоторые из этих вопросов:

https://github.com/banister/method_source.

Set.instance_method(:merge).comment

Set.instance_method(:merge).source

Это также приходит с недавними рельсами (рельсы> = 5,0) Версии и используется PRY под капотом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top