Reputation: 11153
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
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