Packages
maru
0.11.4
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
@moduledoc """
Generate functions.
For all modules:
Two functions will be generated.
`__routes__/0` for returning routes.
`endpoint/2` for execute endpoint block.
For test environments:
these functions generated:
`__version__/0`, `__mounted_modules__/0`, `__plugs__/0`
For plug modules:
You can define a plug module by `use Maru.Router, make_plug: true` or
`config :maru, MyPlugModule` within config.exs.
Two functions `init/1` and `call/2` will be generated for making this module
a `Plug`. The function `endpoint/2` will be called by this `Plug`.
"""
@doc false
defmacro __using__(opts) do
make_plug = opts |> Keyword.get(:make_plug, false)
warning_keys = Keyword.drop(opts, [:make_plug]) |> Keyword.keys
quote do
Maru.Utils.warning_unknown_opts(__MODULE__, unquote(warning_keys))
use Maru.Helpers.Response
require Maru.Struct.Parameter
require Maru.Struct.Resource
require Maru.Struct.Plug
import Maru.Builder.Namespaces
import Maru.Builder.Methods
import Maru.Builder.Exceptions
import Maru.Builder.DSLs, except: [params: 2]
Module.register_attribute __MODULE__, :plugs_before, accumulate: true
Module.register_attribute __MODULE__, :routes, accumulate: true
Module.register_attribute __MODULE__, :endpoints, accumulate: true
Module.register_attribute __MODULE__, :mounted, accumulate: true
Module.register_attribute __MODULE__, :shared_params, accumulate: true
Module.register_attribute __MODULE__, :exceptions, accumulate: true
Module.register_attribute __MODULE__, :mounted_modules, accumulate: true
@extend nil
@resource %Maru.Struct.Resource{}
@desc nil
@parameters []
@plugs []
@func_id 0
@make_plug unquote(make_plug) or not is_nil(Application.get_env(:maru, __MODULE__))
@test (
case Application.get_env(:maru, :test) do
true -> true
false -> false
nil -> Mix.env == :test
end
)
@before_compile unquote(__MODULE__)
end
end
@doc false
defmacro __before_compile__(%Macro.Env{module: module}=env) do
current_routes = Module.get_attribute(module, :routes) |> Enum.reverse
mounted_routes = Module.get_attribute(module, :mounted) |> Enum.reverse
extend_opts = Module.get_attribute(module, :extend)
extended = Maru.Builder.Extend.take_extended(
current_routes ++ mounted_routes, extend_opts
)
all_routes = current_routes ++ mounted_routes ++ extended
mounted_modules = Module.get_attribute(module, :mounted_modules)
exceptions =
Module.get_attribute(module, :exceptions)
|> Enum.reverse
|> Enum.map(&Maru.Builder.Exceptions.make_rescue_block/1)
|> List.flatten
endpoints_block =
Module.get_attribute(module, :endpoints)
|> Enum.reverse
|> Enum.map(&Maru.Builder.Endpoint.dispatch/1)
[ unless Enum.empty?(exceptions) do
quote do
def __error_handler__(func) do
fn ->
try do
func.()
rescue
unquote(exceptions)
end
end
end
end
end,
endpoints_block,
if Module.get_attribute(module, :test) do
quote do
def __version__, do: Maru.Struct.Resource.get_version
def __routes__, do: unquote(Macro.escape(current_routes))
def __mounted_modules__, do: unquote(mounted_modules)
def __plugs__, do: @resource.plugs
end
else
quote do
def __routes__, do: unquote(Macro.escape(all_routes))
end
end,
if Module.get_attribute(module, :make_plug) do
Maru.Builder.PlugRouter.__before_compile__(env, all_routes)
end,
]
end
@doc false
def make_routes_block(routes, env, version_adapter) do
Enum.map(routes, fn route ->
Maru.Builder.Route.dispatch(route, env, version_adapter)
end)
end
@doc false
def make_method_not_allowed_block(routes, version_adapter) do
Enum.group_by(routes, fn route -> {route.version, route.path, route.mount_link} end)
|> Enum.map(fn {{version, path, mount_link}, routes} ->
unless Enum.any?(routes, fn i -> i.method == {:_, [], nil} end) do
Maru.Builder.Route.dispatch_405(version, path, mount_link, version_adapter)
end
end)
end
end