From 2578cc5d3a2b0665ba6a10cd4a98d4115fe15077 Mon Sep 17 00:00:00 2001 From: Ricardo Carvalho Santos Date: Fri, 10 Jul 2026 13:59:57 -0300 Subject: [PATCH] Support module plugs with options in pipe_through A module plug given with options as a {plug, opts} tuple raised at compile time: pipe_through [:api, {MyPlug, greeting: "hey"}] ** (ArgumentError) errors were found at the given arguments: * 1st argument: not an atom build_pipes/2 mapped every entry to {plug, [], true}, so a bare atom (function plug, pipeline, or module) worked, but a {plug, opts} tuple was passed through as the plug itself, which Plug.Builder then tried to treat as an atom. Map {plug, opts} tuples to {plug, opts, true} and keep the existing behaviour for bare entries. Closes #6711 --- lib/phoenix/router.ex | 14 +++++++++++- test/phoenix/router/pipeline_test.exs | 31 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/phoenix/router.ex b/lib/phoenix/router.ex index 7313ecad65..0d06d1a415 100644 --- a/lib/phoenix/router.ex +++ b/lib/phoenix/router.ex @@ -816,7 +816,14 @@ defmodule Phoenix.Router do end defp build_pipes(name, pipe_through) do - plugs = pipe_through |> Enum.reverse() |> Enum.map(&{&1, [], true}) + plugs = + pipe_through + |> Enum.reverse() + |> Enum.map(fn + {module, opts} -> {module, opts, true} + plug -> {plug, [], true} + end) + opts = [init_mode: Phoenix.plug_init_mode(), log_on_halt: :debug] {conn, body} = Plug.Builder.compile(__ENV__, plugs, opts) @@ -1036,6 +1043,11 @@ defmodule Phoenix.Router do pipe_through [:require_authenticated_user, :my_browser_pipeline] + A module plug may also be given, optionally with a tuple of `{plug, opts}` + to pass options to it: + + pipe_through [:browser, MyApp.Plugs.Locale, {MyApp.Plugs.Feature, flag: :beta}] + ## Multiple invocations `pipe_through/1` can be invoked multiple times within the same scope. Each diff --git a/test/phoenix/router/pipeline_test.exs b/test/phoenix/router/pipeline_test.exs index 3a404f4754..8fb719eb85 100644 --- a/test/phoenix/router/pipeline_test.exs +++ b/test/phoenix/router/pipeline_test.exs @@ -174,4 +174,35 @@ defmodule Phoenix.Router.PipelineTest do end end end + + test "pipe_through with module plug and options" do + defmodule ModulePlugRouter do + use Phoenix.Router + + defmodule GreetingPlug do + def init(opts), do: opts + + def call(conn, opts) do + Plug.Conn.assign(conn, :greeting, Keyword.fetch!(opts, :greeting)) + end + end + + pipeline :api do + plug :put_assign, "api" + end + + scope "/" do + pipe_through [:api, {GreetingPlug, greeting: "Hey there"}] + get "/hello", SampleController, :index + end + + defp put_assign(conn, value) do + assign(conn, :stack, [value | conn.assigns[:stack] || []]) + end + end + + conn = call(ModulePlugRouter, :get, "/hello") + assert conn.assigns[:greeting] == "Hey there" + assert conn.assigns[:stack] == ["api"] + end end