【问题标题】:Using module annotations as method attributes使用模块注解作为方法属性
【发布时间】:2017-04-25 03:20:05
【问题描述】:

所以这是一个有趣的问题,我查看了 elixir 中模块属性的文档,即底部的http://elixir-lang.org/getting-started/module-attributes.html,它提到它们可以用作 ExUnit 中的方法注释。

不幸的是,基本上没有关于如何实现这一点的信息,并且查看 ExUnit 代码让我迷失了方向。似乎我需要确定最接近该属性的方法才能说它们以某种方式关联(尽管可能是错误的)。

知道我可以去哪里了解这个吗?

【问题讨论】:

  • 你看过这个页面吗? elixir-lang.org/docs/master/ex_unit/ExUnit.Case.html 更具体地说,该页面的这一部分似乎与您的问题相关:elixir-lang.org/docs/master/ex_unit/… 此页面上是否有您不清楚的内容?你的问题我不是很清楚。
  • 我不知道你在展示什么,是的,ExUnit 有@tags 作为函数的注释,我的问题是他们是如何实现这一点的。我实际上解决了它弄乱的东西,现在我有另一个问题,实际上能够在编译时对这些注释做任何有用的事情。这是我的报酬:github.com/chrisjowen/annotatable
  • 好的,然后把我的评论当作你想知道的不是很清楚的标志。
  • 干杯@OnorioCatenacci 我不知道如何清除它,标题基本上说明了一切。随意提出建议。
  • “知道我可以去哪里了解这个吗?” this 究竟是什么?将注释与 ExUnit 中的测试相关联,还是注释的其他一些您不清楚的属性?似乎不能是前者,因为在您引用的文档中已经说明了这一点——您只需将注释放在测试定义之前的行上。因此,假设您还有其他问题,您究竟在哪里说我错过了?

标签: macros elixir metaprogramming


【解决方案1】:

它是这样工作的。看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 的文档,您会发现这意味着无论何时调用属性,该值都会附加到先前属性的列表中。

然后注意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

ExUnit.Runner.run_case/3中调用函数__ex_unit__/1来获取每个case里面的测试信息。

你明白了吗?使用累积属性,在宏内部调用一个函数,该函数始终获取属性的当前值并清除它,然后对该值做任何你想做的事情,因为你知道它总是在调用宏时。

我希望它足够清楚,如果您需要更多解释,请发表评论。

附言。我刚刚阅读了源代码以找出答案。很高兴知道它是如何工作的。

【讨论】:

  • 我接受这个答案,因为这几乎是正确的。我实际上已经弄清楚了这一点,并敲开了那个小库来注释任何方法。正如你所说,重要的部分是积累:register_attribute 的标签。很好的是,elixir 并不真正支持方法注释,但是该语言足够可破解,可以自己做:)
  • 哦,如果其他人对此感兴趣,还要提一件事。 ExUnit 要求调用测试宏,该宏依次检索和删除之前定义的属性。如果我们正在定义一个方法,那么将不会调用我们可以插入的宏,但幸运的是有一个模块生命周期方法我们可以使用@on_defenition(参见hexdocs.pm/elixir/Module.html
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-21
  • 2021-02-05
  • 1970-01-01
  • 2013-11-22
  • 1970-01-01
  • 1970-01-01
  • 2017-12-25
相关资源
最近更新 更多