Commandex - Make complex actions a first class data type

Heh, I’ve thought about it, but it breaks protocols in the way I envision them being used.

Commandex currently lets you write a theoretical phoenix JSON API like this:

def register_user(conn, params) do
  params
  |> RegisterUser.run()
  |> Respondable.to_json(conn)
end

def login_user(conn, params) do
  params
  |> LoginUser.run()
  |> Respondable.to_json(conn)
end

Where the protocol implementation might look something like:

defimpl YourApp.Respondable, for: YourApp.RegisterUser do
  import Phoenix.Controller

  def to_json(%{success: true, data: %{user: user}}, conn) do
    json(conn, %{token: Auth.sign_token(user), user: user})
  end

  def to_json(%{success: false, error: %{user: :already_exists}, conn) do
    conn
    |> put_status(:bad_request)
    |> json(%{error: "Email is already taken."})
  end
end

defimpl YourApp.Respondable, for: YourApp.LoginUser do
  import Phoenix.Controller

  def to_json(%{success: true, data: %{user: user}}, conn) do
    json(conn, %{token: Auth.sign_token(user), user: user})
  end

  def to_json(%{success: false, error: %{login: :unauthorized}, conn) do
    conn
    |> put_status(:unauthorized)
    |> json(%{error: "Invalid password given."})
  end
end

The trick is having a consistent naming structure while maintaining the flexibility of different kinds of business logic in protocols. Both of those protocol implementations could call out to a shared Response.successful_auth_response(conn, user) function. But they certainly don’t have to, and more importantly, you aren’t sticking all of this logic in the controller.