Packages
maru
0.2.9
0.14.0-pre.1
0.13.2
0.13.1
0.13.0
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
REST-like API micro-framework for elixir inspired by grape.
Current section
Files
Jump to
Current section
Files
lib/maru/builder.ex
defmodule Maru.Builder do
alias Maru.Router.Resource
alias Maru.Router.Endpoint
alias Maru.Router.Path
@methods [:get, :post, :put, :patch, :delete, :head, :options]
defmacro __using__(_) do
quote do
use Maru.Helpers.Response
import Maru.Builder.Namespaces
import Plug.Builder, only: [plug: 1, plug: 2]
import unquote(__MODULE__)
Module.register_attribute __MODULE__, :plugs, accumulate: true
Module.register_attribute __MODULE__, :maru_router_plugs, accumulate: true
Module.register_attribute __MODULE__, :endpoints, accumulate: true
@version nil
@resource %Resource{}
@param_context []
@desc nil
@group []
@exception nil
@before_compile unquote(__MODULE__)
def init(_), do: []
end
end
defmacro __before_compile__(%Macro.Env{module: module}=env) do
plugs = Module.get_attribute(module, :plugs)
maru_router_plugs = Module.get_attribute(module, :maru_router_plugs)
pipeline = [
if Maru.Config.is_server?(module) do
[{Maru.Plugs.NotFound, [], true}]
else [] end,
maru_router_plugs,
[{:endpoint, [], true}, {Maru.Plugs.Prepare, [], true}],
if Maru.Config.is_server?(module) do
[{Plug.Parsers, [parsers: [:urlencoded, :multipart, :json], pass: ["*/*"], json_decoder: Poison], true}]
else [] end,
plugs,
] |> Enum.concat
{conn, body} = Plug.Builder.compile(env, pipeline, [])
quote do
for i <- @endpoints do
Module.eval_quoted __MODULE__, (i |> Code.eval_quoted |> elem(0) |> Endpoint.dispatch), [], __ENV__
end
defp endpoint(conn, _), do: conn
case @version do
nil ->
def call(unquote(conn), _) do
unquote(body)
end
{v, opts} ->
@v v
@opts opts
def call(unquote(conn)=c, _) do
unquote(conn) = unquote(conn) |> Maru.Plugs.Version.call(Maru.Plugs.Version.init(@opts))
case unquote(conn).private.maru_version do
@v -> unquote(body)
_ -> c
end
end
end
if Module.defines? __MODULE__, {:error, 2} do
defoverridable [call: 2]
def call(conn, opts) do
try do
super(conn, opts)
rescue
e -> error(conn, e)
end
end
end
if Mix.env == :dev do
def __endpoints__, do: @endpoints |> Code.eval_quoted |> elem(0)
def __routers__, do: @maru_router_plugs
def __version__ do
case @version do
{v, _} -> v
nil -> nil
end
end
end
end
end
defmacro maru_router_plug(plug, opts \\ []) do
quote do
@maru_router_plugs {unquote(plug), unquote(opts), true}
end
end
Module.eval_quoted __MODULE__, (for method <- @methods do
quote do
defmacro unquote(method)(path \\ "", [do: block]) do
%{ method: unquote(method) |> to_string |> String.upcase,
path: path |> Path.split,
block: block |> Macro.escape,
} |> endpoint
end
end
end)
defp endpoint(ep) do
quote do
@endpoints %Endpoint{
method: unquote(ep.method), desc: @desc,
path: @resource.path ++ unquote(ep.path),
param_context: @resource.param_context ++ @param_context,
block: unquote(ep.block),
} |> Macro.escape
@param_context []
@desc nil
end
end
defmacro params(block) do
quote do
import Maru.Builder.Namespaces, only: []
import Maru.Builder.Params
@group []
unquote(block)
import Maru.Builder.Params, only: []
import Maru.Builder.Namespaces
end
end
defmacro version(v, opts) do
key = opts |> Keyword.get :using, :path
key in [:path, :param, :accept_version_header] || raise "unsupported version type: #{inspect key}"
quote do
@version {unquote(v), unquote(opts)}
end
end
defmacro helpers([do: block]) do
quote do
unquote(block)
end
end
defmacro helpers({_, _, mod}) do
quote do
import unquote(Module.concat mod)
end
end
defmacro desc(desc) do
quote do
@desc unquote(desc)
end
end
defmacro mount({_, _, mod}) do
module = Module.concat mod
quote do
@maru_router_plugs {Maru.Plugs.Router, [router: unquote(module), resource: @resource], true}
end
end
end