我正在尝试编写帮助器宏,它允许我编写一堆 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}
键,其中 set
为 defined here .
关于elixir - 获取 Elixir 宏中当前范围的文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51883342/