在 Elixir 宏中获取当前范围的文档
Get documentation in Elixir macro for current scope
我正在尝试编写辅助宏,它允许我在没有大量样板的情况下编写一堆 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)
returns 一样工作,如果没有文档则 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
。
我们距离解决方案仅一步之遥。谨防!私人活动区!
您要自己重新实现 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 结构,所以我编写了宏:
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)
returns 一样工作,如果没有文档则 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
。
我们距离解决方案仅一步之遥。谨防!私人活动区!
您要自己重新实现 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。