Packages
plug
0.8.3
1.20.3
1.20.2
1.20.1
retired
1.20.0
retired
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.15.6
1.15.5
1.15.4
1.15.3
1.15.2
1.15.1
1.15.0
1.14.2
1.14.1
1.14.0
1.13.6
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
retired
1.13.0
retired
1.12.1
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.0
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.14.0
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
Compose web applications with functions
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/plug/builder.ex
defmodule Plug.Builder do
alias Plug.Conn
@moduledoc """
Conveniences for building plugs.
This module can be used into a module in order to build
a plug pipeline:
defmodule MyApp do
use Plug.Builder
plug Plug.Logger
plug :hello, upper: true
def hello(conn, opts) do
body = if opts[:upper], do: "WORLD", else: "world"
send_resp(conn, 200, body)
end
end
Multiple plugs can be defined with the `plug/2` macro, forming a
pipeline. `Plug.Builder` also imports the `Plug.Conn` module, making
functions like `send_resp/3` available.
## Plug behaviour
Internally, `Plug.Builder` implements the `Plug` behaviour, which means
both `init/1` and `call/2` functions are defined. By implementing the
Plug API, `Plug.Builder` guarantees this module can be handed to a web
server or used as part of another pipeline.
## Halting a Plug pipeline
A Plug pipeline can be halted with `Plug.Conn.halt/1`. The builder will
prevent further plugs downstream from being invoked and return the current
connection.
"""
@type plug :: module | atom
@doc false
defmacro __using__(_) do
quote do
@behaviour Plug
def init(opts) do
opts
end
def call(conn, opts) do
plug_builder_call(conn, opts)
end
defoverridable [init: 1, call: 2]
import Plug.Conn
import Plug.Builder, only: [plug: 1, plug: 2]
Module.register_attribute(__MODULE__, :plugs, accumulate: true)
@before_compile Plug.Builder
end
end
@doc false
defmacro __before_compile__(env) do
plugs = Module.get_attribute(env.module, :plugs)
if plugs == [] do
raise "not plugs have been defined in #{__MODULE__}"
end
{conn, body} = Plug.Builder.compile(plugs)
quote do
defp plug_builder_call(unquote(conn), _), do: unquote(body)
end
end
@doc """
A macro that stores a new plug.
"""
defmacro plug(plug, opts \\ []) do
quote do
@plugs {unquote(plug), unquote(opts), true}
end
end
@doc """
Compiles a plug pipeline.
It expects a reversed pipeline (with the last plug coming first)
and returns a tuple containing the reference to the connection
as first argument and the compiled quote pipeline.
"""
@spec compile([{plug, Plug.opts}]) :: {Macro.t, Macro.t}
def compile(pipeline) do
conn = quote do: conn
{conn, Enum.reduce(pipeline, conn, "e_plug(init_plug(&1), &2))}
end
defp init_plug({plug, opts, guard}) do
case Atom.to_char_list(plug) do
'Elixir.' ++ _ ->
init_module_plug(plug, opts, guard)
_ ->
init_fun_plug(plug, opts, guard)
end
end
defp init_module_plug(plug, opts, guard) do
opts = plug.init(opts)
if function_exported?(plug, :call, 2) do
{:call, plug, opts, guard}
else
raise ArgumentError, message: "#{inspect plug} plug must implement call/2"
end
end
defp init_fun_plug(plug, opts, guard) do
{:fun, plug, opts, guard}
end
defp quote_plug({:call, plug, opts, guard}, acc) do
call = quote do: unquote(plug).call(conn, unquote(Macro.escape(opts)))
quote do
case unquote(compile_guard(call, guard)) do
%Conn{halted: true} = conn -> conn
%Conn{} = conn -> unquote(acc)
_ -> raise "expected #{unquote(inspect plug)}.call/2 to return a Plug.Conn"
end
end
end
defp quote_plug({:fun, plug, opts, guard}, acc) do
call = quote do: unquote(plug)(conn, unquote(Macro.escape(opts)))
quote do
case unquote(compile_guard(call, guard)) do
%Conn{halted: true} = conn -> conn
%Conn{} = conn -> unquote(acc)
_ -> raise "expected #{unquote(plug)}/2 to return a Plug.Conn"
end
end
end
defp compile_guard(call, true) do
call
end
defp compile_guard(call, guard) do
quote do
case true do
true when unquote(guard) -> unquote(call)
true -> conn
end
end
end
end