Current section

Files

Jump to
json_rpc_toolkit lib json_rpc registry.ex
Raw

lib/json_rpc/registry.ex

defmodule JSONRPC.Registry do
defmacro __using__(opts) do
namespace_separator = Keyword.get(opts, :separator, ".")
quote do
import JSONRPC.Request, except: [new: 1]
import JSONRPC.Registry
@scope [%{name: "", pre: [], scoped: [], post: []}]
@registry %{}
@namespace_separator unquote(namespace_separator)
@before_compile JSONRPC.Registry
end
end
defmacro namespace(name, do: block) do
quote do
@scope [%{name: unquote(name), pre: [], scoped: [], post: []} | @scope]
unquote(block)
@scope [
%{
hd(tl(@scope))
| scoped:
Enum.map(hd(@scope).scoped, fn {method, chain} ->
{unquote(name) <> @namespace_separator <> method,
hd(@scope).pre ++ chain ++ hd(@scope).post}
end) ++ hd(tl(@scope)).scoped
}
| tl(tl(@scope))
]
end
end
defmacro process(processor, opts \\ []) do
quote do
case hd(@scope) do
%{scoped: []} ->
@scope [
%{
hd(@scope)
| pre: hd(@scope).pre ++ [{unquote(processor), unquote(opts)}]
}
| tl(@scope)
]
_ ->
@scope [
%{
hd(@scope)
| post: hd(@scope).post ++ [{unquote(processor), unquote(opts)}]
}
| tl(@scope)
]
end
end
end
defmacro register(action, module) do
quote do
@scope [
%{
hd(@scope)
| scoped: hd(@scope).scoped ++ [{unquote(action), [{unquote(module), []}]}]
}
| tl(@scope)
]
end
end
defmacro __before_compile__(_env) do
quote do
@registry hd(@scope).scoped
|> Enum.map(fn {method, chain} ->
{method, hd(@scope).pre ++ chain ++ hd(@scope).post}
end)
|> Enum.into(%{})
def registry(), do: @registry
def init(opts), do: opts
def call(%JSONRPC.Request{method: method, halted: false} = request, opts) do
@registry
|> Map.get(method)
|> execute_chain(request, opts)
end
def call(request, _opts), do: request
def execute_chain(_, %JSONRPC.Request{halted: true} = request, _opts), do: request
def execute_chain(nil, request, _opts) do
request
|> JSONRPC.Request.set_error(JSONRPC.Error.method_not_found())
|> JSONRPC.Request.halt()
end
def execute_chain([], request, _opts), do: request
def execute_chain([{processor, opts} | tail], request, registry_opts) do
tail
|> execute_chain(
call_processor({processor, opts ++ registry_opts}, request),
registry_opts
)
end
defp call_processor({processor, opts}, %JSONRPC.Request{halted: false} = request) do
case Atom.to_charlist(processor) do
~c"Elixir." ++ _ ->
new_opts = apply(processor, :init, [opts])
apply(processor, :call, [request, new_opts])
_ ->
[
{__MODULE__, __MODULE__.__info__(:functions)}
| Enum.filter(__ENV__.functions, fn {module, _functions} -> module != Kernel end)
]
|> Enum.find(fn {module, list} -> Keyword.get(list, processor) == 2 end)
|> case do
{module, _} -> apply(module, processor, [request, opts])
_ -> raise "Processor #{processor}/2 must be defined."
end
end
end
defp call_processor(_, request), do: request
end
end
end