I need a function with some kind of a step-by-step logic and I wonder how I can make one. Let's take a log in process on a site as an example, so I need the following logic:
1) Email is present? Yes -> Go on; No -> Return an error
2) Email has at least 5 characters? Yes -> Go on; No -> Return an error
3) Password is present? Yes -> Go on; No - Return an error
And so on ...
And to implement this, I would usually use a return
statement so that if the email is not present, I quit executing the function and make it return an error. But I can't find something similar to this in Elixir so I need an advice. The only way I can see now is to use nested conditions but maybe there is a better way?
There is no return in Elixir!!!
Elixir has no 'break out' keyword that would be equivalent to the 'return' keyword in other languages. Typically what you would do is re-structure your code so the last statement executed is the return value.
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:
state
as an input and returns {:ok, new_state}
or {:error, reason}
.{: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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With