This is an interesting problem because you need to perform multiple checks, exit early, and in the process transform some state (connection). I typically approach this problem as follows:
- I implement each check as a function which takes
state
as an input and returns {:ok, new_state}
or {:error, reason}
.
- Then, I build a generic function that will invoke a list of check functions, and return either the first encountered
{:error, reason}
or {:ok, last_returned_state}
if all checks succeeded.
Let's see the generic function first:
defp perform_checks(state, []), do: {:ok, state}
defp perform_checks(state, [check_fun | remaining_checks]) do
case check_fun.(state) do
{:ok, new_state} -> perform_checks(new_state, remaining_checks)
{:error, _} = error -> error
end
end
Now, we can use it as follows:
perform_checks(conn, [
# validate mail presence
fn(conn) -> if (...), do: {:error, "Invalid mail"}, else: {:ok, new_conn} end,
# validate mail format
fn(conn) -> if (...), do: {:error, "Invalid mail"}, else: {:ok, new_conn} end,
...
])
|> case do
{:ok, state} -> do_something_with_state(...)
{:error, reason} -> do_something_with_error(...)
end
Or alternatively move all checks to named private functions and then do:
perform_checks(conn, [
&check_mail_presence/1,
&check_mail_format/1,
...
])
You could also look into the elixir-pipes which might help you express this with pipeline.
Finally, in the context of Phoenix/Plug, you could declare your checks as a series of plugs and halt on first error.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…