Skip to content

Commit

Permalink
Deprecate Plug.Builder.builder_opts/0
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Feb 12, 2022
1 parent ae38e00 commit e9cc51c
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 121 deletions.
62 changes: 23 additions & 39 deletions lib/plug/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ defmodule Plug.Builder do
When used, the following options are accepted by `Plug.Builder`:
* `:log_on_halt` - accepts the level to log whenever the request is halted
* `:init_mode` - the environment to initialize the plug's options, one of
`:compile` or `:runtime`. Defaults `:compile`.
* `:log_on_halt` - accepts the level to log whenever the request is halted
* `:copy_opts_to_assign` - copy the options given to the Plug to the given assign
## Plug behaviour
Internally, `Plug.Builder` implements the `Plug` behaviour, which means both
Expand Down Expand Up @@ -149,9 +152,23 @@ defmodule Plug.Builder do
end
end

plug_builder_call =
if assign = builder_opts[:copy_opts_to_assign] do
quote do
defp plug_builder_call(conn, opts) do
unquote(conn) = Plug.Conn.assign(conn, unquote(assign), opts)
unquote(body)
end
end
else
quote do
defp plug_builder_call(unquote(conn), opts), do: unquote(body)
end
end

quote do
unquote_splicing(compile_time)
defp plug_builder_call(unquote(conn), opts), do: unquote(body)
unquote(plug_builder_call)
end
end

Expand Down Expand Up @@ -213,45 +230,12 @@ defmodule Plug.Builder do
defp expand_alias(other, _env), do: other

@doc """
Annotates a plug will receive the options given
to the current module itself as arguments.
Imagine the following plug:
defmodule MyPlug do
use Plug.Builder
plug :inspect_opts, builder_opts()
Using `builder_opts/0` is deprecated.
defp inspect_opts(conn, opts) do
IO.inspect(opts)
conn
end
end
When plugged as:
plug MyPlug, custom: :options
It will print `[custom: :options]` as the builder options
were passed to the inner plug.
Note you only pass `builder_opts()` to **function plugs**.
You cannot use `builder_opts()` with module plugs because
their options are evaluated at compile time. If you need
to pass `builder_opts()` to a module plug, you can wrap
the module plug in function. To be precise, do not do this:
plug Plug.Parsers, builder_opts()
Instead do this:
plug :custom_plug_parsers, builder_opts()
defp custom_plug_parsers(conn, opts) do
Plug.Parsers.call(conn, Plug.Parsers.init(opts))
end
Instead use `:copy_opts_to_assign` on `use Plug.Builder`.
"""
# TODO: Deprecate me in future releases
@doc deprecated: "Pass :copy_opts_to_assign on \"use Plug.Builder\""
defmacro builder_opts() do
quote do
Plug.Builder.__builder_opts__(__MODULE__)
Expand Down
2 changes: 1 addition & 1 deletion lib/plug/parsers/multipart.ex
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ defmodule Plug.Parsers.MULTIPART do
## Multipart

defp parse_multipart(conn, {m2p, {module, fun, args}, header_opts, opts}) do
# TODO: Remove me on 2.0.
# TODO: This is deprecated. Remove me on Plug 2.0.
limit = apply(module, fun, args)
parse_multipart(conn, {m2p, limit, header_opts, opts})
end
Expand Down
73 changes: 5 additions & 68 deletions lib/plug/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ defmodule Plug.Router do
`:match` is responsible for finding a matching route which is
then forwarded to `:dispatch`. This means users can easily hook
into the router mechanism and add behaviour before match, before
dispatch, or after both. All of the options given to `use Plug.Router`
are forwarded to `Plug.Builder`. See the `Plug.Builder` module
for more information on the `plug` macro and on the available options.
dispatch, or after both. See the `Plug.Builder` module for more
information.
## Routes
Expand Down Expand Up @@ -198,72 +197,10 @@ defmodule Plug.Router do
`conn.assigns` (or `conn.private`) to configure their behaviour
based on the matched route.
## Routes compilation
## `use` options
All routes are compiled to a match function that receives
three arguments: the method, the request path split on `/`
and the connection. Consider this example:
match "/foo/bar", via: :get do
send_resp(conn, 200, "hello world")
end
It is compiled to:
defp match("GET", ["foo", "bar"], conn) do
send_resp(conn, 200, "hello world")
end
This means guards can be given to `match`:
match "/foo/bar/:baz" when byte_size(baz) <= 3, via: :get do
send_resp(conn, 200, "hello world")
end
After a match is found, the block given as `do/end` is stored
as a function in the connection. This function is then retrieved
and invoked in the `dispatch` plug.
## Routes options
Sometimes you may want to customize how a route behaves during dispatch.
This can be done by accessing the `opts` variable inside the route:
defmodule AppRouter do
use Plug.Router
plug :match
plug :dispatch, content: "hello world"
get "/hello" do
send_resp(conn, 200, opts[:content])
end
match _ do
send_resp(conn, 404, "oops")
end
end
This is particularly useful when used with `Plug.Builder.builder_opts/0`.
`builder_opts/0` allows us to pass options received when initializing
`AppRouter` to a specific plug, such as dispatch itself. So if instead of:
plug :dispatch, content: "hello world"
we do:
plug :dispatch, builder_opts()
now the content can be given when starting the router, like this:
Plug.Cowboy.http AppRouter, [content: "hello world"]
Or as part of a pipeline like this:
plug AppRouter, content: "hello world"
In a nutshell, `builder_opts()` allows us to pass the options given
when initializing the router to a `dispatch`.
All of the options given to `use Plug.Router` are forwarded to
`Plug.Builder`. See the `Plug.Builder` module for more information.
## Telemetry
Expand Down
11 changes: 10 additions & 1 deletion lib/plug/ssl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,15 @@ defmodule Plug.SSL do
@impl true
def init(opts) do
host = Keyword.get(opts, :host)

case host do
{:system, _} ->
IO.warn "Using {:system, host} as your Plug.SSL host is deprecated. Pass nil or a string instead."

_ ->
:ok
end

rewrite_on = Plug.RewriteOn.init(Keyword.get(opts, :rewrite_on))
log = Keyword.get(opts, :log, :info)
exclude = Keyword.get(opts, :exclude, ["localhost"])
Expand Down Expand Up @@ -373,7 +382,7 @@ defmodule Plug.SSL do
defp host(nil, host), do: host
defp host(host, _) when is_binary(host), do: host
defp host({mod, fun, args}, host), do: host(apply(mod, fun, args), host)
# TODO: Deprecate this format
# TODO: Remove me once the deprecation is removed.
defp host({:system, env}, host), do: host(System.get_env(env), host)

defp qs(""), do: ""
Expand Down
25 changes: 13 additions & 12 deletions test/plug/builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ defmodule Plug.BuilderTest do
end

defmodule Sample do
use Plug.Builder
use Plug.Builder, copy_opts_to_assign: :stack

plug :fun, parent: builder_opts()
plug Module, :opts
plug Module, %{opts: builder_opts()}
plug :fun, :step1
plug Module, :step2
plug Module, :step3

def fun(conn, opts) do
stack = [{:fun, opts} | conn.assigns[:stack]]
Expand Down Expand Up @@ -89,18 +89,19 @@ defmodule Plug.BuilderTest do
end

test "builds plug stack in the order" do
conn = assign(conn(:get, "/"), :stack, [])
conn = conn(:get, "/")

assert Sample.call(conn, []).assigns[:stack] == [
call: {:init, %{opts: []}},
call: {:init, :opts},
fun: [parent: []]
call: {:init, :step3},
call: {:init, :step2},
fun: :step1
]

assert Sample.call(conn, :parent).assigns[:stack] == [
call: {:init, %{opts: :parent}},
call: {:init, :opts},
fun: [parent: :parent]
assert Sample.call(conn, [:initial]).assigns[:stack] == [
{:call, {:init, :step3}},
{:call, {:init, :step2}},
{:fun, :step1},
:initial
]
end

Expand Down

0 comments on commit e9cc51c

Please sign in to comment.