defmodule Routex do @moduledoc """ > #### `use Routex` {: .info} > When use'd this module generates a Routext backend module and > a configuration struct using the `configure/2` callbacks of > the extensions provided in `opts`. > > See also: [Routex Extensions](EXTENSIONS.md). **Example** iex> defmodule MyApp.RtxBackend do ...> use Routex, ...> extensions: [ ...> Routex.Extension.VerifiedRoutes, ...> Routex.Extension.AttrGetters, ...> ], ...> bar: [some_opts: "value"] ...> end iex> IO.inspect(%MyApp.RtxBackend{}) %MyApp.RtxBackend{ bar: [some_opts: "value"], extensions: [Routex.Extension.VerifiedRoutes, Routex.Extension.AttrGetters], verified_sigil_routex: "~l", verified_sigil_original: "~o" } """ alias Routex.Processing @typedoc """ A Routex backend module """ @type t :: module @spec __using__(opts :: list) :: Macro.output() defmacro __using__(opts) do opts = process_opts(opts, __CALLER__) extensions = Keyword.get(opts, :extensions, []) # Cheat by adding the struct fields to the map as the actual struct is # not yet defined config = opts |> Map.new() |> Map.put(:__struct__, __CALLER__.module) quote do defstruct unquote(config |> Map.to_list() |> Macro.escape()) @typedoc """ A Routext backend struct """ @type config :: struct() @spec config :: config def config, do: unquote(Macro.escape(config)) @spec extensions :: [module] def extensions, do: unquote(Macro.escape(extensions)) end end defp process_opts(opts, env) do {opts, _} = Code.eval_quoted(opts, [], env) for extension <- opts[:extensions], extension != [], reduce: opts do acc -> ext_mod = Macro.expand_once(extension, env) conf_mod = env.module Processing.exec_when_defined(conf_mod, ext_mod, :configure, acc, [acc, conf_mod]) end end end