Best way to combine Bodyguard.permit and Repo.insert

I wanted to clean up my controllers by taking advantage of the introduction of FallbackControllers.

While migrating the following snippet, I got stuck how I am able to dynamically reload forms with parameters:

  def create(conn, %{"input" => params}) do
    changeset = Input.changeset(%Input{}, params)

    Bodyguard.permit!(Abilities, :write, conn.assigns.current_user, nil)

    case Repo.insert(changeset) do
      {:ok, _input} ->
        conn
        |> redirect(to: path(conn, :index))
      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

I’m having no problems to utilize the fallback controller to clean up my controller, resulting in the following:

  def create(conn, %{"input" => params}) do
    changeset = Sport.changeset(%Input{}, params)

    with :ok <- Bodyguard.permit(Abilities, :write, conn.assigns.current_user, nil),
         {:ok, _input} <- Repo.insert(changeset) do
       conn
       |> redirect(to: path(conn, :index))
    end
  end

My problems is reloading the right form in the fallback controller:

  def call(conn, {:error, changeset = %Changeset{}}) do
    form = if Enum.empty?(conn.path_params), do: "new", else: "edit"
    render(conn, "#{Enum.join(conn.path_info, "/")}/#{form}.html", changeset: changeset)
  end

I am therefore able to reload a simple form in a generic way. Has any of you ever tried to find a generic way to dynamically reload complex forms with parameters additional to changeset?
I could define a function for each entity but I don’t really like this approach since it’s not as great as it could be, but that should be doable.

Edit: An idea that came to my mind after posting was that I might define a function in each entity controller that’s called from the fallback controller and returns a map of such parameter names and values.

I add a hidden field to the form called phx_action and match that to figure out which form to render. You could use a token or something if you’re worried about the security implications.

Figuring out if it’s new or edit is not the problem. You can do that by the number of parameters. But sometimes I need to load some more attributes for the form, not only the changeset and I wonder how to do that best in a dynamically way

Put that in a plug, which is executed before the controller action. This way it should be available for both a successful controller action as well as a unsuccessful one using the fallback controller.