Terence Chow
Terence Chow

Reputation: 11153

Phoenix, Does conn redirect stop all further processing?

I have a controller where I would like to add facebook page id and access token to my database so that I can post on behalf of a user.

To do this, I need to make a GET call to facebook and then with the access token I have to insert into my database.

That means I have a nested case statement because there are 2 points of errors, from facebook call and from inserting into repo:

case response_from_facebook do
   {:ok, token} ->
       case Repo.insert(token) do
            {:ok, _} -> ... redirect the user
            {:error, _} -> ... show an error message
   {:error, _} -> redirect and show an error

This is ugly imo so I was wondering if I did something like a guard if it would stop further processing:

token = case response_from_facebook do
    {:ok, token} -> token
    {error,_ } -> conn |> redirect(to: user_path(conn, :show, user_id))
end

# Does it ever try to insert into the repo if an error occurs?

Repo.update ... etc.

This would make my code cleaner to understand / read but, i'm not entirely sure if redirecting would prevent an attempt to insert into my repo. If this "guard" is not a solution, how can I prevent a multi nested case statement which can be hard as heck to read?

Upvotes: 2

Views: 177

Answers (1)

Jason Harrelson
Jason Harrelson

Reputation: 2693

A cleaner way to do this:

defmodule SomeApp.FacebookController do
  ...

  def create(conn, params) do
    get_from_facebook
    |> process_facebook(conn)
  end

  defp get_from_facebook do
    # this is a function you implement
  end

  defp process_facebook({:ok, token}, conn) do
    Repo.insert(token)
    |> process_insert(conn)
  end

  defp process_facebook({:error, error}, conn) do
    conn
    |> redirect_with_error(error) # this is a function you will write
  end

  defp process_insert({:ok, _}, conn) do
    conn 
    |> redirect(to: user_path(conn, :show, user_id))
  end

  defp process_insert({:error, error}, conn) do
    conn
    |> redirect_with_error(error) # this is a function you will write
  end

  ...
end

No undesirable effects can happen with this factoring of the logic and it is much "prettier."

Even better if you break the majority of this logic out into another module:

defmodule SomeApp.FacebookCommand do

  def call do
    make_request
    |> process_response
  end

  defp make_request do
    # whatever you are doing to request from Facebook
  end

  defp process_response({:ok, token}) do
    Repo.insert(token)
    |> process_insert(conn)
  end

  defp process_response({:error, error}) do
    {:error, error}
  end

  defp process_insert({:ok, record}) do
    {:ok, record}
  end

  defp process_insert({:error, error}) do
    {:error, error}
  end

end

And then use it in the controller:

defmodule SomeApp.FcebookController do
  ...

  def create(conn, params) do
    case SomeApp.FacebookCommand.call do
      {:ok, user} ->
        conn
        |> redirect(to: user_path(conn, :show, user.id))
      {:error, error} ->
        # redirect with error
    end
  end

  ...
end

Obviously if you are updating an existing user, then you would wriote your command where you pass the existing user into call as a parameter.'

Please keep in mind my module and function naming suck because I do not know the exactly what you are doing. Better naming is a must.

Upvotes: 1

Related Questions