UPDATE:
I originally forgot to mention that my MainPlug
is a Plug.Router
. I only realized it while responding to Overmind, below. I’ve now updated the initial message to make it clear.
Hello,
I’m working on a project where I have a main Plug that acts as the public API of the library. This module is a Plug.Router
and it includes its own plug pipeline. Users are supposed to use it in their routers and forward traffic to it. Let’s call it MyPackage.MainPlug
.
This main plug router then uses a number of child plugs to get some work done. These must be separate plugs, because the idea is that users can still use these building blocks to create their own router, if they need some custom logic.
My problem is that I can’t find a way to forward options to the nested “child” plugs.
For example, this is my Phoenix router, where I forward to my plug and pass some options to it:
defmodule MyPhoenixApp.Web.Router do
use MyPhoenixApp.Web, :router
pipeline :api do
plug :accepts, ["json"]
end
scope path: "/stuff" do
pipe_through :api
forward "/", MyPackage.MainPlug, my_options: [foo: "foo", bar: "bar"]
end
end
In the next snippet are my main plug (first) and the child plug (second):
defmodule MyPackage.MainPlug do
use Plug.Router
plug MyPackage.ChildPlug
def init(opts) do
opts # == [my_options: [foo: "foo", bar: "bar"]]
end
def call(conn, opts) do
# opts == [my_options: [foo: "foo", bar: "bar"]]
conn = Plug.Conn.assign(conn, :my_options, opts[:my_options])
# do more stuff with conn
end
end
defmodule MyPackage.ChildPlug do
def init(opts) do
opts # == []
end
def call(conn, opts) do
# I need the extra options here!
conn
end
end
My problem is that I want to be able to customize ChildPlug
from the outside, but I can’t find a clear way to do it.
As you can see, in MainPlug.call/2
I’m assigning the options to the conn
just as an example, but it doesn’t really help me because, by then, ChildPlug
has already been run.
I guess I could avoid the plug
macro and invoke the child plugs directly in MainPlug.call/2
, for example:
defmodule MyPackage.MainPlug do
use Plug.Router
alias MyPackage.PrivatePlug
def init(opts) do
opts # == [my_options: [foo: "foo", bar: "bar"]]
end
def call(conn, opts) do
conn = PrivatePlug.call(conn, PrivatePlug.init(opts))
# do more stuff with conn
end
end
That would work, but I’d lose the nice performance gain of being able to manipulate the opts
in the init
function ahead of time.
Also, this seems a common enough requirement that hopefully there is already a clean API to do what I need.
Thanks!