2016-12-09 2 views
1

Итак, интересный вопрос, что я вижу в документации по атрибутам модуля в elixir, то есть http://elixir-lang.org/getting-started/module-attributes.html внизу, он упоминает, что они могут использоваться как аннотации методов, как в ExUnit.Использование аннотаций модуля в качестве атрибутов метода

К сожалению, в принципе нет информации о том, как этого достичь, и просмотр кода ExUnit только что потерял меня. Похоже, мне нужно будет определить ближайший метод к атрибуту, чтобы сказать, что они каким-то образом связаны (может быть, ошибочно).

Любая идея, где я мог бы узнать об этом?

+1

Вы просматриваете эту страницу? http://elixir-lang.org/docs/master/ex_unit/ExUnit.Case.html Более конкретно этот раздел этой страницы имеет отношение к вашему вопросу: http://elixir-lang.org/docs/master/ex_unit/ ExUnit.Case.html # module-tags Есть ли что-то на этой странице, что вам непонятно? Ваш вопрос мне не очень понятен. –

+0

Я не знаю, что вы показываете, мне да ExUnit имеет @tags, которые действуют как аннотации к функциям, мой вопрос в том, как они это достигли.Я на самом деле решаю, как он возится с вещами, и теперь у меня есть еще одна проблема, которая действительно могла сделать что-нибудь полезное с этими аннотациями во время компиляции. Здесь моя зарплата: https://github.com/chrisjowen/annotatable – Owen

+0

Хорошо, тогда возьмите мой комментарий как знак того, что не совсем понятно, что вы хотите знать. –

ответ

2

Он работает следующим образом. Посмотрите исходный код ExUnit.Case.

Вначале посмотрите на макрос __using__, так как он будет вызываться первым, когда вы используете его в тестовом примере. В частности, обратите внимание here

Enum.each [:ex_unit_tests, :tag, :describetag, :moduletag, :ex_unit_registered], 
     &Module.register_attribute(__MODULE__, &1, accumulate: true) 

Это регистрирует @tag и кучу более атрибутов, как накапливается. Прочитайте документы Module.register_attribute/3, и вы увидите, что это означает, что вызывается атрибут anytime, значение добавляется к списку предыдущих атрибутов.

Затем обратите внимание test/3 макрос, в частности here

quote bind_quoted: [var: var, contents: contents, message: message] do 
    name = ExUnit.Case.register_test(__ENV__, :test, message, []) 
    def unquote(name)(unquote(var)), do: unquote(contents) 
end 

Примечание вызов ExUnit.Case.register_test/4. Глядя на него, специально here

tag = Module.delete_attribute(mod, :tag) 

Он извлекает теги, пока здесь, и удаляет их. И при наличии метки, и название теста, он вызывает (here)

test = %ExUnit.Test{name: name, case: mod, tags: tags} 
Module.put_attribute(mod, :ex_unit_tests, test) 

который сохраняет тест вместе с тегами внутри другой атрибутов.

И наконец, обратите внимание, здесь

@doc false 
    defmacro __before_compile__(_) do 
    quote do 
     def __ex_unit__(:case) do 
     %ExUnit.TestCase{name: __MODULE__, tests: @ex_unit_tests} 
     end 
    end 
    end 

Функция __ex_unit__/1 вызывается в ExUnit.Runner.run_case/3, чтобы получить информацию о тестах внутри каждого конкретного случая.

Вы видите точку? Используйте накопленный атрибут, внутри вашего макроса вызывается функция, которая всегда получает текущее значение атрибута и очищает его, а затем делает все, что угодно, со значением, потому что вы знаете, что это всегда, когда вызывается макрос.

Я надеюсь, что было достаточно ясно, напишите комментарий, если вам нужно больше объяснений.

PS. Я просто прочитал исходный код, чтобы узнать это. Было интересно узнать, как это работает.

+0

Я принимаю ответ, так как это довольно много. Я на самом деле понял это и выбил эту небольшую библиотеку для аннотации любого метода. Как вы сказали, важная часть накапливалась: тэг register_attribute. Его довольно приятно, что эликсир на самом деле не поддерживает аннотации методов, но язык достаточно взломан, чтобы просто сделать это самостоятельно :) – Owen

+1

О, еще одна вещь, чтобы упомянуть, если кто-то еще заинтересовался этим. ExUnit требует вызова тестового макроса, который, в свою очередь, извлекает и удаляет атрибуты, определенные перед ним. Если мы определяем метод, то макрос не будет называться, который мы можем подключить, но, к счастью, существует метод жизненного цикла модуля, мы можем использовать @on_defenition (см. Https://hexdocs.pm/elixir/Module.html) – Owen

Смежные вопросы