Current section
Files
Jump to
Current section
Files
lib/attr_reader.ex
defmodule AttrReader do
@moduledoc """
Can define module attributes getter automatically.
"""
@reserved_attributes Module.reserved_attributes() |> Map.keys()
@doc """
Defines getters for all custom module attributes if used.
And writes getter docs.
## Examples
iex> defmodule UseAttrReaderForDoc do
...> @foo "foo"
...> use AttrReader
...> @bar :bar
...> end
iex> UseAttrReaderForDoc.foo()
"foo"
iex> UseAttrReaderForDoc.bar()
:bar
"""
@spec __using__(any) ::
{:@, [{:context, AttrReader} | {:import, Kernel}, ...],
[{:before_compile, [...], [...]}, ...]}
defmacro __using__(_opts \\ nil) do
quote do
@before_compile AttrReader
end
end
@spec __before_compile__(atom | %{:module => atom, optional(any) => any}) :: list
defmacro __before_compile__(env) do
attributes_in(env.module)
|> REnum.reject(&(&1 in @reserved_attributes))
|> REnum.map(fn attribute ->
quote do
@doc """
Gets @#{unquote(attribute)}.
## Examples
iex> #{unquote(env.module)}.#{unquote(attribute)}()
#{unquote(Module.get_attribute(env.module, attribute))}
"""
def unquote(attribute)() do
unquote(Module.get_attribute(env.module, attribute))
end
end
end)
end
@doc """
Sets module attribute and defines getter.
And writes getter docs.
## Examples
iex> defmodule AttrReaderMacroForDoc do
...> AttrReader.define @foo
...> AttrReader.define @bar, "bar"
...> AttrReader.define @baz, :baz
...> end
iex> AttrReaderMacroForDoc.foo()
nil
iex> AttrReaderMacroForDoc.bar()
"bar"
iex> AttrReaderMacroForDoc.baz()
:baz
"""
@spec define(tuple, any) :: {:__block__, [], [{:@, [...], [...]} | {:def, [...], [...]}, ...]}
defmacro define(attribute, value \\ nil) do
attr_key = elem(attribute, 2) |> RList.first() |> elem(0)
quote do
@attar_value unquote(value)
@doc """
Gets @#{unquote(attr_key)}.
## Examples
iex> #{unquote(__MODULE__)}.#{unquote(attr_key)}()
#{unquote(value)}
"""
def unquote(attr_key)() do
@attar_value
end
end
end
defp attributes_in(module) when is_atom(module) do
{set, _} = :elixir_module.data_tables(module)
:ets.select(set, [{{:"$1", :_, :_}, [{:is_atom, :"$1"}], [:"$1"]}])
end
end