I was simply stating that I couldn’t find any evidence to support this observation:
why/how it doesn’t force the other controller actions to have it as an argument.
My observation was that customizing the arguments by overriding action/2
forces an update of the parameter list of every action function within that controller because they all have the same arity, argument order and types.
Any controller includes something like this:
use RumblWeb, :controller
which injects code
# from rumbl/lib/rumbl_web.ex
def controller do
quote do
use Phoenix.Controller, namespace: RumblWeb
import Plug.Conn
import RumblWeb.Gettext
alias RumblWeb.Router.Helpers, as: Routes
end
end
then
use Phoenix.Controller, namespace: RumblWeb
injects even more code phoenix/lib/phoenix/controller.ex at 19b0b01e2fc751901e06fcd74db1fc5b39672d26 · phoenixframework/phoenix · GitHub
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
import Phoenix.Controller
# TODO v2: No longer automatically import dependencies
import Plug.Conn
use Phoenix.Controller.Pipeline, opts
plug :put_new_layout, {Phoenix.Controller.__layout__(__MODULE__, opts), :app}
plug :put_new_view, Phoenix.Controller.__view__(__MODULE__)
end
end
then
use Phoenix.Controller.Pipeline, opts
injects among other things this code phoenix/lib/phoenix/controller/pipeline.ex at 19b0b01e2fc751901e06fcd74db1fc5b39672d26 · phoenixframework/phoenix · GitHub
@doc false
def init(opts), do: opts
@doc false
def call(conn, action) when is_atom(action) do
conn = update_in conn.private,
&(&1 |> Map.put(:phoenix_controller, __MODULE__)
|> Map.put(:phoenix_action, action))
phoenix_controller_pipeline(conn, action)
end
@doc false
def action(%Plug.Conn{private: %{phoenix_action: action}} = conn, _options) do
apply(__MODULE__, action, [conn, conn.params])
end
defoverridable [init: 1, call: 2, action: 2]
init/1
and call/2
define a module plug that adds the conn.private.phoenix_controller
and conn.private.phoenix_controller
values to the Plug.Conn
struct.
action/2
is the controller function normally used to extract conn.private.phoenix_action
and conn.params
to call the appropriate controller action function with
apply(__MODULE__, action, [conn, conn.params])
But because action/2
is listed as defoverridable
you are free to provide your own implementation of action/2
inside your controller module to invoke the action functions whichever way you wish.
Now you may be wondering
def call(conn, action) when is_atom(action) do
where is action
coming from?
As far as I can tell it originates from router.ex
(Phoenix.Router.scope/2
):
scope path: "/api/v1", as: :api_v1, alias: API.V1 do
get "/pages/:id", PageController, :show
end
get(path, plug, plug_opts, options \\ [])
If GET /api/v1/pages/:id
then
API.V1.PageController
→plug
:show
→plug_opts
See also: