Reputation: 23586
I am trying to write helper macro that will allow me to write bunch of Elixir structures without lot of boilerplate, so I have written macro:
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
Which can be used as follows:
defmofule Event do
import Events
defevent Foo, foo: String.t(), bar: number()
end
However I would like to add possibility to add documentation to such module via @doc
module attribute:
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
However it will not work as Module.get_attribute(__MODULE__, :doc)
returns either nil
when there is no documentation or {integer(), String.t()}
if there is docstring. However it miss all tags, so writing code like:
defmofule Event do
import Events
@doc "Foo"
@doc deprecated: "Bar"
defevent Foo, foo: String.t(), bar: number()
end
Will contain only "Foo"
string without specified tags. My current workaround is using custom attribute, but this isn't prettiest solution out there as it requires non-standard parameter and defining custom module attribute (which makes me dependant on 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
Is there any way to get values of the current @doc
with all attributes assigned?
Upvotes: 2
Views: 422
Reputation: 121010
Very biased humble opinion: there is no one single Elixir developer who did not try to enhance structs. They are cool, succinct and vivid as they are, and your code just overcomplicates things.
I believe, Elixir core team agrees with this point of view, that is why this functionality is not exposed. Whether you are brave enough to tackle it nevertheless, here you go.
Here is how documentation is built by Elixir. Please note the leading comment:
@doc false
# Used internally to compile documentation.
# This function is private and must be used only internally.
Are you still in? OK.
Digging into the Module.compile_definition_attributes/6
further, we get into Module.compile_doc_meta/5
.
We are one step from the solution. Beware! Private functions area!
You are to reimplement Module.get_doc_meta/2
yourself:
case :ets.take(set, {:doc, :meta}) do
[{{:doc, :meta}, metadata, _}] -> Map.merge(existing_meta, metadata)
[] -> existing_meta
end
That said, you are to read the {:doc, :meta}
key from ETS where set
is defined here.
Upvotes: 3