Hello, all.
Could you please explain to me how to organize functions and types into groups using rebar3_ex_doc?
Before I start, I would like to thank @starbelly for his help.
But I can’t find a solution yet.
From the description for grouping, I can’t figure out what the filtering function should look like.
Using the example of public_key
module from OTP27.
...
23 -module(public_key).
24 -moduledoc """
25 API module for public-key infrastructure.
...
44 -moduledoc(#{titles =>
45 [{type, <<"Common">>},
46 {type,<<"Keys">>},
47 {type,<<"PEM files">>},
48 {type,<<"Certificates">>},
49 {type,<<"Certificate Revocation">>},
50 {type,<<"Test Data">>},
51 {function,<<"PEM API">>},
52 {function,<<"Key API">>},
...
198 -doc(#{title => <<"Keys">>}).
199 -doc """
200 Can be provided together with a custom private key, that specifies a key fun, to
201 provide additional options understood by the fun.
202 """.
203 -type custom_key_opts() :: [term()].
Next, we see in the documentation here public_key:
I’m adding something similar to my code
...
-moduledoc(#{titles =>
[{type, <<"My Type Group">>},
{function,<<"My API Group">>},
...
-doc(#{title => <<"My Type Group">>}).
-doc """
Description.
""".
-type my_custom_type() :: term().
...
-doc(#{title => <<"My API Group">>}).
-doc """
Description.
""".
-spec my_fun() -> ok.
my_fun() ->
ok.
But I see (There is no grouping):
Based on this Grouping functions and callbacks from ex_doc:
Functions and callbacks inside a module can also be organized in groups. This is done via the :groups_for_docs
configuration which is a keyword list of group titles and filtering functions that receive the documentation metadata of functions as argument. The metadata received will also contain :module
, :name
, :arity
and :kind
to help identify which entity is currently being processed.
and on this rebar3 config script
and on this EEP-48: Documentation storage and format
I wrote a script and it doesn’t work.
The contents of the script:
{ok, Modules} = application:get_key(my_app, modules).
Titles = lists:foldl(
fun(Module, Acc) ->
case code:get_doc(Module) of
%% see eep-48
{ok, {docs_v1, _, erlang, _, _, #{titles := Ts}, _}} ->
Acc ++ Ts;
_ -> % otherwise
Acc
end
end,
[],
Modules).
Groups = {groups_for_docs,
lists:foldl(
fun({type, Title}, Acc) ->
Acc ++ [{"Types: " ++ binary_to_list(Title),
fun({{Kind, _, _},_ ,_ ,_ , Metadata}) ->
Kind == type andalso
maps:get(title, Metadata, undefined) =:= Title
end}];
({callback, Title}, Acc) ->
Acc ++ [{"Callbacks: " ++ binary_to_list(Title),
fun({{Kind, _, _}, _, _, _, Metadata}) ->
Kind == callback andalso
maps:get(title, Metadata, undefined) =:= Title
end}];
({function, Title}, Acc) ->
Acc ++ [{binary_to_list(Title),
fun({{Kind, _, _}, _, _, _, Metadata}) ->
Kind == function andalso
maps:get(title, Metadata, undefined) =:= Title
end}]
end,
[],
Titles)}.
{ex_doc, ExDocConf} = lists:keyfind(ex_doc, 1, CONFIG).
NewExDocConf = lists:keystore(groups_for_docs, 1, ExDocConf, Groups).
lists:keystore(ex_doc, 1, CONFIG, {ex_doc, NewExDocConf}).
The contents of the error:
❯ rebar3 ex_doc
===> Error evaluating configuration script at "rebar.config.script":
{error,{2,file,
{error,{badmatch,undefined},
[{erl_eval,expr,6,[{file,"erl_eval.erl"},{line,652}]},
{file,eval_stream2,6,[{file,"file.erl"},{line,2908}]},
{file,script,2,[{file,"file.erl"},{line,2465}]},
{rebar_config,consult_and_eval,2,
[{file,"/home/username/.cache/yay/rebar3-git/src/rebar3/apps/rebar/src/rebar_config.erl"},
{line,320}]},
{rebar_config,consult_file_,1,
[{file,"/home/username/.cache/yay/rebar3-git/src/rebar3/apps/rebar/src/rebar_config.erl"},
{line,263}]},
{rebar_config,consult_file,1,
[{file,"/home/username/.cache/yay/rebar3-git/src/rebar3/apps/rebar/src/rebar_config.erl"},
{line,245}]},
{rebar3,init_config,0,
[{file,"/home/username/.cache/yay/rebar3-git/src/rebar3/apps/rebar/src/rebar3.erl"},
{line,230}]},
{rebar3,run,1,
[{file,"/home/username/.cache/yay/rebar3-git/src/rebar3/apps/rebar/src/rebar3.erl"},
{line,100}]}]}}}
I also tried to execute commands from the script separately in the REPL.
Separately, it works, but it does not pass erl_eval.
A keyword list of group titles and filtering functions looks like this
23> {ok, A} = file:consult("rebar.config").
24> {ex_doc, ExDocConf} = lists:keyfind(ex_doc, 1, A).
{ex_doc,[{proglang,erlang},
{extras,[{"CHANGELOG.md",#{title => "Changelog"}},
{"README.md",#{title => "Overview"}},
{"LICENSE.md",#{title => "License"}}]},
...]}
25> NewExDocConf = lists:keystore(groups_for_docs, 1, ExDocConf, Groups).
[{proglang,erlang},
{extras,[{"CHANGELOG.md",#{title => "Changelog"}},
{"README.md",#{title => "Overview"}},
{"LICENSE.md",#{title => "License"}}]},
...
{groups_for_docs,[{"API Group1",
#Fun<erl_eval.42.39164016>},
{"API Group2",#Fun<erl_eval.42.39164016>},
{"API Group3",#Fun<erl_eval.42.39164016>},
...
{"Types: Type Group1",#Fun<erl_eval.42.39164016>},
{"Types: Type Group2",#Fun<erl_eval.42.39164016>},
{"Types: Type Group3",#Fun<erl_eval.42.39164016>}]}]
27> lists:keystore(ex_doc, 1, A, {ex_doc, NewExDocConf}).
I suppose that’s exactly what it {"API Group2",#Fun<erl_eval.42.39164016>}
should look like.
But something is wrong with the functions.
I would like to consider two variants:
- (rebar.config + rebar.config.script)
- (rebar.config + rebar.config.script + docs.config)
For the first variant:
The contents of the rebar.config
file for rebar3_ex_doc
:
...
8 {ex_doc, [{proglang, erlang},
9 {extras, [{"CHANGELOG.md", #{title => "Changelog"}},
10 {"README.md", #{title => "Overview"}},
11 {"LICENSE.md", #{title => "License"}}
12 ]},
13 {logo, "/path/to/logo.png"},
14 {homepage_url, "url"},
15 {source_url, "url"}
16 ]}.
...
The contents of the rebar.config.script
file for rebar3_ex_doc
are described above.
For the second variant:
The contents of the rebar.config
file for rebar3_ex_doc
:
...
8 {ex_doc, "docs.config"}.
...
The contents of the docs.config
file for rebar3_ex_doc
:
1 {proglang, erlang}.
2 {extras, [{<<"CHANGELOG.md">>, #{title => <<"Changelog">>}},
3 {<<"README.md">>, #{title => <<"Overview">>}},
4 {<<"LICENSE.md">>, #{title => <<"License">>}}
5 ]}.
6 {logo, <<"/path/to/logo.png">>}.
7 {homepage_url, <<"url">>}.
8 {source_url, <<"url">>}.
The contents of the rebar.config.script
file for rebar3_ex_doc
are described above.
Since working with CONFIG
binding for the first variant will give the ex_doc configuration from rebar.config
like this:
...
{ex_doc, ExDocConf} = lists:keyfind(ex_doc, 1, CONFIG).
NewExDocConf = lists:keystore(groups_for_docs, 1, ExDocConf, Groups).
lists:keystore(ex_doc, 1, CONFIG, {ex_doc, NewExDocConf}).
Then working with CONFIG
binding for the second variant will give the ex_doc configuration from rebar.config
like this:
[...
{ex_doc, "docs.config"}.
...]
That is, the docs.config
file need to be overwritten from the rebar.config.script
script.