Update doc #10

Merged
cevado merged 1 commits from readme-update into main 9 months ago

@ -35,6 +35,53 @@ The simple one is the weather resolver, that can get the current temperature for
The elaborated example is the Hacker News api resolver, that can query the public Hacker News api, including nested resources.
There is also a livemd file that you can open with [Livebook](https://livebook.dev/) to explore the library usage.
For reference when reading the docs follows a dummy implementation of `boto_resolver` behaviour.
### Erlang
``` erlang
-module(resolver).
-behaviour(boto_resolver).
-export([resolver_init/0, resolve/2]).
resolver_init() ->
[{resolver1, [{output, [attribute1, attribute2, attribute3]}]},
{resolver2, [{input, [attribute1]}, {output, [attribute4, attribute5, attribute6]}]}].
resolve(resolver1, Input) ->
#{attribute1 => 1, attribute2 => 2, attribute3 => 3};
resolve(resolver2, #{attribute1 := Att1}) ->
case Att1 of
1 ->
#{attribute4 => 4, attribute5 => 5, attribute6 => 6},
2 ->
#{attribute4 => 8, attribute5 => 10, attribute6 => 12}
end.
```
### Elixir
``` elixir
defmodule Resolver do
@behaviour :boto_resolver
def resolver_init() do
[
resolver1: [output: [:attribute1, :attribute2, :attribute3]],
resolver2: [input: [:attribute1], output: [:attribute4, :attribute5, :attribute6]]
]
end
def resolve(:resolver1, _input) do
%{attribute1: 1, attribute2: 2, attribute3: 3}
end
def resolve(:resolver2, %{attribute1: att}) do
case att do
1 -> %{attribute4: 4, attribute5: 5, attribute6: 6}
2 -> %{attribute4: 8, attribute5: 10, attribute6: 12}
end
end
end
```
## Contributing
The source of truth for this code is [codeberg](https://codeberg.org/cevado/boto), provide feedback and join discussion in the issues.
A mirror is kept in [sourcehut](https://git.sr.ht/~cevado/boto).

@ -1,12 +1,14 @@
{erl_opts, [debug_info]}.
{hex, [{doc, #{provider => ex_doc}}]}.
{plugins, [rebar3_ex_doc, rebar3_hex]}.
{plugins, [rebar_erl_vsn, rebar3_ex_doc, rebar3_hex]}.
{project_plugins, [rebar3_format, rebar3_lint]}.
{provider_hooks, [{pre, [{compile, {default, erl_vsn}}]}]}.
{ex_doc,
[{source_url, <<"https://git.sr.ht/~cevado/boto">>},
[{source_url, <<"https://codeberg.org/cevado/boto">>},
{extras,
[{"README.md", #{title => "Overview"}},
{"CHANGELOG.md", #{title => "Changelog"}},
{"CODE_OF_CONDUCT.md", #{title => "Code of Conduct"}},
{"LICENSE", #{title => "Licensse"}}]},
{main, "README.md"}]}.
{hex, [{doc, #{provider => ex_doc}}]}.

@ -8,7 +8,8 @@
{env, []},
{modules, []},
{licenses, ["Apache-2.0"]},
{doc, "doc"},
{excluded_paths, ["examples/"]},
{links,
#{<<"sr.ht">> => <<"https://git.sr.ht/~cevado/boto">>,
<<"codeberg">> => <<"https://codeberg.org/cevado/boto">>}}]}.
#{<<"codeberg">> => <<"https://codeberg.org/cevado/boto">>,
<<"sr.ht">> => <<"https://git.sr.ht/~cevado/boto">>}}]}.

@ -1,16 +1,26 @@
%% @doc Public api for Boto.
%% {@link boto_server} must be started, properly configured and with set of implementations of {@link boto_resolver} for boto to work.
%% @end
-module(boto).
-export([resolves/1, resolves/2, query/2]).
%% @doc Checks if the provided attribute is available in any resolver registered to boto.
%% @end
resolves(Output) ->
{ok, Graph} = boto_worker:graph(),
Vertices = digraph:vertices(Graph),
lists:member(Output, Vertices).
%% @doc Checks if the input attribute can reach the output attribute.
%% If output attribute is reacheable through a resolver that doesn't require input attributes, it will return true as well.
%% @end
resolves(Input, Output) ->
{ok, Graph} = boto_worker:graph(),
Path = digraph:get_short_path(Graph, Input, Output),
lists:member(Input, Path) and lists:member(noarg, Path).
query(A, Q) when is_map(A), is_list(Q) ->
boto_query:query(A, Q).
%% @doc Retrieves attributes present in the query using the Input attributes provided.
%% @end
query(Input, Output) when is_map(Input), is_list(Output) ->
boto_query:query(Input, Output).

@ -1,3 +1,4 @@
%% @private
-module(boto_graph).
-export([start/0, add_vertex/2, add_edge/4, path/3]).

@ -1,3 +1,4 @@
%% @private
-module(boto_query).
-export([query/2]).

@ -1,8 +1,26 @@
%% @doc Behaviour that defines a resolver for boto.
%% @end
-module(boto_resolver).
-type name() :: atom().
-type resolver() :: [{input, [atom()]} | {output, [atom()]}].
-type t() :: [{name(), resolver()}].
-export_type([name/0, resolver/0, input/0, input_attribute/0, output/0, output_attribute/0, t/0]).
-callback resolver_init() -> t().
-callback resolve(name(), map()) -> map() | [map()].
-type name() :: atom(). %% Name of a resolver.
-type resolver() :: [input() | output()]. %% Resolver settings.
%% {@link input()} and {@link output()} must be provided.
-type input() :: {input, [] | list(input_attribute())}. %% Input option.
-type input_attribute() :: atom(). %% Input attribute, those will be required to call the resolver.
-type output() :: {output, list(output_attribute())}.%% Output option.
-type output_attribute() :: atom() | {atom(), list(output_attribute())}. %% Output attribute, those will be provided by the resolver after it's called.
%% A nested attribute shouldn't require another resolver to be called.
-type t() :: list({name(), resolver()}). %% {@link resolver_init()} return, list all resolvers provided by the module implementing this behaviour.
-callback resolver_init() -> t(). %% This will be called at {@link boto_server} start to setup and index all available attributes.
-callback resolve(name(), map()) -> map() | [map()]. %% This function is called when resolving a query.
%% This function will be called only when {@link input_attribute()} are available and the query needs one of the {@link output_attribute()} either to finish traversing a required resolver or for the final output of the query.

@ -1,3 +1,5 @@
%% @doc A
%% @end
-module(boto_server).
-behaviour(gen_server).
@ -7,6 +9,9 @@
-export([child_spec/1, graph/0, start_link/1, init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3, format_status/1]).
%% @doc A
%% @end
child_spec(args) ->
#{id => boto_server,
start => {boto_server, start_link, [args]},
@ -14,12 +19,16 @@ child_spec(args) ->
restart => permanent,
type => worker}.
%% @private
graph() ->
gen_server:call(boto_server, graph).
%% @doc A
%% @end
start_link(Args) ->
gen_server:start_link({local, boto_server}, boto_server, Args, []).
%% @private
init(Args) ->
Graph = boto_graph:start(),
Table = ets:new(boto_resolvers_config, [set, protected]),
@ -32,21 +41,27 @@ init(Args) ->
[store_resolvers(R, Table) || R <- Resolvers],
{ok, State}.
%% @private
handle_call(graph, _Ref, State) ->
{reply, maps:get(graph, State), State}.
%% @private
handle_cast(_M, State) ->
{noreply, State}.
%% @private
handle_info(_M, State) ->
{noreply, State}.
%% @private
terminate(_R, _S) ->
normal.
%% @private
code_change(_Vsn, State, _Extra) ->
{ok, State}.
%% @private
format_status(Status) ->
Status.

Loading…
Cancel
Save