elixir - 获取 Elixir 宏中当前范围的文档

标签 elixir documentation metaprogramming

我正在尝试编写帮助器宏,它允许我编写一堆 Elixir 结构而无需大量样板,所以我编写了宏:

defmodule Events do
  @moduledoc false

  defmacro defevent(module, fields \\ []) do
    keys = Keyword.keys(fields)

    quote do
      defmodule unquote(module) do
        @type t :: %__MODULE__{unquote_splicing(fields)}

        defstruct unquote(keys)
      end
    end
  end
end

可以按如下方式使用:

defmofule Event do
  import Events

  defevent Foo, foo: String.t(), bar: number()
end

但是我想添加通过 @doc 模块属性向此类模块添加文档的可能性:

defmodule Events do
  @moduledoc false

  defmacro defevent(module, fields \\ []) do
    keys = Keyword.keys(fields)

    quote do
      docs = Module.delete_attribute(__MODULE__, :doc)

      defmodule unquote(module) do
        for doc <- docs do
          @moduledoc doc
        end

        @type t :: %__MODULE__{unquote_splicing(fields)}

        defstruct unquote(keys)
      end
    end
  end
end

但是它不会工作,因为 Module.get_attribute(__MODULE__, :doc) 在没有文档时返回 nil{integer(), String .t()} 如果有文档字符串。然而它错过了所有标签,因此编写如下代码:

defmofule Event do
  import Events

  @doc "Foo"
  @doc deprecated: "Bar"
  defevent Foo, foo: String.t(), bar: number()
end

仅包含 "Foo" 字符串,不带指定标签。我当前的解决方法是使用自定义属性,但这并不是最漂亮的解决方案,因为它需要非标准参数并定义自定义模块属性(这使我依赖于 use)。

defmodule Events do
  @moduledoc false

  defmacro __using__(_) do
    quote do
      import unquote(__MODULE__), only: [defevent: 1, defevent: 2]

      Module.register_attribute(__MODULE__, :eventdoc, accumulate: true)
    end
  end

  defmacro defevent(module, fields \\ []) do
    keys = Keyword.keys(fields)

    quote do
      docs = Module.delete_attribute(__MODULE__, :eventdoc)

      defmodule unquote(module) do
        for doc <- docs do
          @moduledoc doc
        end

        @type t :: %__MODULE__{unquote_splicing(fields)}

        defstruct unquote(keys)
      end
    end
  end
end

有没有办法获取当前@doc的值并分配所有属性?

最佳答案

非常有偏见的拙见:没有一个 Elixir 开发人员没有尝试增强结构。它们确实很酷、简洁、生动,但你的代码只是让事情变得过于复杂。

我相信,Elixir 核心团队也同意这个观点,所以没有公开这个功能。不管你是否有足够的勇气去解决这个问题,都可以。

这里是how documentation is built by Elixir 。请注意主要评论:

@doc false  
# Used internally to compile documentation.  
# This function is private and must be used only internally.

你还在吗?好的。

进一步深入Module.compile_definition_attributes/6,我们进入 Module.compile_doc_meta/5 .

我们距离解决方案仅一步之遥。谨防!私有(private)功能区!

您要重新实现Module.get_doc_meta/2你自己:

case :ets.take(set, {:doc, :meta}) do
  [{{:doc, :meta}, metadata, _}] -> Map.merge(existing_meta, metadata)
  [] -> existing_meta
end

也就是说,您要从 ETS 读取 {:doc, :meta} 键,其中 setdefined here .

关于elixir - 获取 Elixir 宏中当前范围的文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51883342/

相关文章:

elixir - 在 Phoenix 框架中编写测试用例,相当于 ROR

c# - 自动为 asp.net 项目中的单个 c# 文件生成文档?

c - Ruby 解释器的嵌入 API 的文档在哪里?

python - 如何在 Python 中创建 Mixin 工厂?

dart - Dart中的元编程性能

testing - Elixir 中的集成测试——如何从单元测试中过滤掉

elixir - 如何在不预加载的情况下加载关联?

c# - 许多常量的 XML 文档

c++ - GCC vs VS 匿名类型作为模板参数

elixir - 在 Elixir 中使用 pin 运算符匹配位串的模式