Skip to content

Commit 2243235

Browse files
committed
Merge pull request #1229 from alco/1132-doc-module-attr
Provide reference documentation for module attributes
2 parents fc9506e + a724571 commit 2243235

File tree

4 files changed

+305
-4
lines changed

4 files changed

+305
-4
lines changed

lib/elixir/lib/module.ex

Lines changed: 282 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,296 @@ defmodule Module do
77
end
88
end
99

10-
@moduledoc """
10+
@moduledoc %B'''
1111
This module provides many functions to deal with modules during
1212
compilation time. It allows a developer to dynamically attach
1313
documentation, add, delete and register attributes and so forth.
1414
15-
After the module is compiled, using many of the functions in
15+
After a module is compiled, using many of the functions in
1616
this module will raise errors, since it is out of their purpose
1717
to inspect runtime data. Most of the runtime data can be inspected
1818
via the `__info__(attr)` function attached to each compiled module.
19+
20+
## Module attributes
21+
22+
Each module can be decorated with one or more attributes. The following ones
23+
are currently defined by Elixir:
24+
25+
* `@after_compile`
26+
27+
A hook that will be invoked right after the current module is compiled.
28+
29+
Accepts a module or a tuple `{ <module>, <function atom> }`. The function
30+
must take two arguments: the module environment and its bytecode.
31+
When just a module is provided, the function is assumed to be
32+
`__after_compile__/2`.
33+
34+
**Example**
35+
36+
defmodule M do
37+
@after_compile __MODULE__
38+
39+
def __after_compile__(env, _bytecode) do
40+
IO.inspect env
41+
end
42+
end
43+
44+
* `@before_compile`
45+
46+
A hook that will be invoked before the module is compiled.
47+
48+
Accepts a module or a tuple `{ <module>, <function/macro atom> }`. The
49+
function/macro must take one argument: the module environment. If it's a
50+
macro, its returned value will be injected at the end of the module definition
51+
before the compilation starts.
52+
53+
When just a module is provided, the function/macro is assumed to be
54+
`__before_compile__/1`.
55+
56+
**Example**
57+
58+
defmodule M do
59+
@before_compile __MODULE__
60+
61+
defmacro __before_compile__(_env) do
62+
quote do
63+
def hello, do: "world"
64+
end
65+
end
66+
end
67+
68+
* `@behaviour` (notice the british spelling)
69+
70+
Specify an OTP or user-defined behaviour.
71+
72+
**Example**
73+
74+
defmodule M do
75+
@behaviour gen_event
76+
77+
# ...
78+
end
79+
80+
* `@compile`
81+
82+
Define options for module compilation that are passed to the Erlang
83+
compiler.
84+
85+
Accepts an atom, a tuple, or a list of atoms and tuples.
86+
87+
See http://www.erlang.org/doc/man/compile.html for the list of supported
88+
options.
89+
90+
**Example**
91+
92+
defmodule M do
93+
@compile { :inline, myfun: 1 }
94+
95+
def myfun(arg) do
96+
to_binary(arg)
97+
end
98+
end
99+
100+
* `@doc`
101+
102+
Provide documentation for the function or macro that follows the
103+
attribute.
104+
105+
Accepts a string (often a heredoc) or `false` where `@doc false` will
106+
make the function/macro invisible to the documentation extraction tools
107+
like ExDoc.
108+
109+
Can be invoked more than once.
110+
111+
**Example**
112+
113+
defmodule M do
114+
@doc "Hello world"
115+
def hello do
116+
"world"
117+
end
118+
119+
@doc """
120+
Sum.
121+
"""
122+
def sum(a, b) do
123+
a + b
124+
end
125+
end
126+
127+
* `@file`
128+
129+
Change the filename used in stacktraces for the function or macro that
130+
follows the attribute.
131+
132+
Accepts a string. Can be used more than once.
133+
134+
**Example**
135+
136+
defmodule M do
137+
@doc "Hello world"
138+
@file "hello.ex"
139+
def hello do
140+
"world"
141+
end
142+
end
143+
144+
* `@moduledoc`
145+
146+
Provide documentation for the current module.
147+
148+
Accepts a string (which is often a heredoc) or `false` where
149+
`@moduledoc false` will make the module invisible to the
150+
documentation extraction tools like ExDoc.
151+
152+
**Example**
153+
154+
defmodule M do
155+
@moduledoc """
156+
A very useful module
157+
"""
158+
end
159+
160+
161+
* `@on_definition`
162+
163+
A hook that will be invoked after each function or macro in the current
164+
module is defined. This makes it easy to annotate and customize
165+
functions.
166+
167+
Accepts a module or a tuple `{ <module>, <function atom> }`. The function
168+
must take 6 arguments:
169+
170+
- the module environment
171+
- kind: `:def`, `:defp`, `:defmacro`, or `:defmacrop`
172+
- function/macro name
173+
- list of quoted arguments
174+
- list of quoted guards
175+
- quoted function body
176+
177+
If the function/macro being defined has multiple clauses, the hook will
178+
be called for each clause.
179+
180+
When just a module is provided, the function is assumed to be
181+
`__on_definition__/6`.
182+
183+
Note that you can't provide the current module to `@on_definition`
184+
because the hook function will not be defined in time.
185+
186+
**Example**
187+
188+
defmodule H do
189+
def on_def(_env, kind, name, args, guards, body) do
190+
IO.puts "Defining #{kind} named #{name} with args:"
191+
IO.inspect args
192+
IO.puts "and guards"
193+
IO.inspect guards
194+
IO.puts "and body"
195+
IO.puts Macro.to_binary(body)
196+
end
197+
end
198+
199+
defmodule M do
200+
@on_definition { H, :on_def }
201+
202+
def hello(arg) when is_binary(arg) or is_list(arg) do
203+
"Hello" <> to_binary(arg)
204+
end
205+
206+
def hello(_) do
207+
:ok
208+
end
209+
end
210+
211+
* `@on_load`
212+
213+
A hook that will be invoked whenever the module is loaded.
214+
215+
Accepts a function atom of a function in the current module. The function
216+
must have arity 0 (no arguments) and has to return `:ok`, otherwise the
217+
loading of the module will be aborted.
218+
219+
**Example**
220+
221+
defmodule M do
222+
@on_load :load_check
223+
224+
def load_check do
225+
if some_condition() do
226+
:ok
227+
else
228+
nil
229+
end
230+
end
231+
232+
def some_condition do
233+
false
234+
end
235+
end
236+
237+
* `@vsn`
238+
239+
Specify the module version. Accepts any valid Elixir value.
240+
241+
**Example**
242+
243+
defmodule M do
244+
@vsn "1.0"
245+
end
246+
247+
The following attributes are part of typespecs and are also reserved by
248+
Elixir:
249+
250+
* `@type` - defines a type to be used in `@spec`
251+
* `@spec` - provides a specification for a function
252+
* `@callback` - provides a specification for the behavior callback
253+
* `@export_type` - informs which types can be exported
254+
* `@opaque` - defines an opaque type to be used in `@spec`
255+
256+
In addition to the built-in attributes outlined above, custom attributes may
257+
also be added. A custom attribute is any valid identifier prefixed with an
258+
`@` and followed by a valid Elixir value:
259+
260+
defmodule M do
261+
@custom_attr [some: "stuff"]
262+
end
263+
264+
For more advanced options available when defining custom attributes, see
265+
`register_attribute/3`.
266+
267+
## Runtime information about a module
268+
269+
It is possible to query a module at runtime to find out which functions and
270+
macros it defines, extract its docstrings, etc. See `__info__/1`.
271+
272+
'''
273+
274+
@doc """
275+
Provides runtime information about functions and macros defined by the
276+
module, enables docstring extraction, etc.
277+
278+
Each module gets an `__info__/1` function when it's compiled. The function
279+
takes one of the following atoms:
280+
281+
* `:functions` - keyword list of public functions along with their arities
282+
283+
* `:macros` - keyword list of public macros along with their arities
284+
285+
* `:docs` - list of all docstrings attached to functions and macros
286+
using the `@doc` attribute
287+
288+
* `:moduledoc` - tuple `{ <line>, <doc> }` where `line` is the line on
289+
which module definition starts and `doc` is the string
290+
attached to the module using the `@moduledoc` attribute
291+
292+
* `:module` - module name (`Module == Module.__info__(:module)`)
293+
294+
In addition to the above, you may also pass to `__info__/1` any atom supported
295+
by Erlang's `module_info` function which also gets defined for each compiled
296+
module. See http://erlang.org/doc/reference_manual/modules.html#id74571 for
297+
more information.
19298
"""
299+
def __info__(kind)
20300

21301
@doc """
22302
Check if a module is open, i.e. it is currently being defined

lib/ex_unit/lib/ex_unit/callbacks.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ defmodule ExUnit.Callbacks do
1212
and `teardown_all` share their own context in a similar way, but this one
1313
provides an ExUnit.TestCase record associated with the `:case` key.
1414
15-
If you return { :ok, <keyword list> } from `setup` or `teardown`, the keyword
15+
If you return `{ :ok, <keyword list> }` from `setup` or `teardown`, the keyword
1616
list will get merged into the context that will be available in all
1717
subsequent `setup`, `test`, or `teardown` calls.
1818
19-
Similarly, returning { :ok, <keyword list> } from `setup_all` or
19+
Similarly, returning `{ :ok, <keyword list> }` from `setup_all` or
2020
`teardown_all` will merge the keyword list into the context that will be
2121
available in all subsequent `setup_all` or `teardown_all` calls.
2222

lib/iex/lib/iex/helpers.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,26 @@ defmodule IEx.Helpers do
9999
h Enum.all?/2
100100
h Enum.all?
101101
"""
102+
# Special case for `h AnyModule.__info__/1`
103+
defmacro h({ :/, _, [{ { :., _, [_mod, :__info__] }, _, [] }, 1] }) do
104+
quote do
105+
IEx.Introspection.h(Module, :__info__, 1)
106+
end
107+
end
108+
102109
defmacro h({ :/, _, [{ { :., _, [mod, fun] }, _, [] }, arity] }) do
103110
quote do
104111
IEx.Introspection.h(unquote(mod), unquote(fun), unquote(arity))
105112
end
106113
end
107114

115+
# Special case for `h AnyModule.__info__`
116+
defmacro h({ { :., _, [_mod, :__info__] }, _, [] }) do
117+
quote do
118+
IEx.Introspection.h(Module, :__info__, 1)
119+
end
120+
end
121+
108122
defmacro h({ { :., _, [mod, fun] }, _, [] }) do
109123
quote do
110124
IEx.Introspection.h(unquote(mod), unquote(fun))

lib/iex/test/iex/helpers_test.exs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ defmodule IEx.HelpersTest do
4343
== "* def pwd()\n\nPrints the current working directory.\n\n"
4444
end
4545

46+
test "h helper __info__" do
47+
h_output_module = capture_io(fn -> h Module.__info__ end)
48+
assert capture_io(fn -> h Module.UnlikelyTo.Exist.__info__ end) == h_output_module
49+
assert capture_io(fn -> h Module.UnlikelyTo.Exist.__info__/1 end) == h_output_module
50+
assert capture_io(fn -> h __info__ end) == "No docs for __info__ have been found\n"
51+
end
52+
4653
test "t helper" do
4754
assert capture_io(fn -> t ExUnit end) == "No types for ExUnit have been found\n"
4855

0 commit comments

Comments
 (0)