1

我正在尝试编写帮助宏,它允许我编写一堆 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分配了所有属性的电流值?

4

1 回答 1

2

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

我相信,Elixir 核心团队同意这个观点,这也是为什么这个功能没有暴露出来的原因。不管你是否有足够的勇气去解决它,你去吧。

这是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

也就是说,您要从此处定义{:doc, :meta}的 ETS 中set读取密钥。

于 2018-08-17T04:34:10.693 回答