NoD*_*ame 30 elixir phoenix-framework
我需要一个具有某种逐步逻辑的函数,我想知道如何制作一个.我们以站点上的登录过程为例,所以我需要以下逻辑:
1)电子邮件存在?是的 - >继续; 否 - >返回错误
2)电子邮件至少有5个字符?是的 - >继续; 否 - >返回错误
3)密码存在?是的 - >继续; 否 - 返回错误
等等 ...
为了实现这一点,我通常会使用一个return语句,以便如果电子邮件不存在,我退出执行该函数并使其返回错误.但是我在Elixir找不到类似的东西,所以我需要一个建议.我现在能看到的唯一方法是使用嵌套条件,但也许有更好的方法?
sas*_*ric 28
这是一个有趣的问题,因为您需要执行多个检查,提前退出,并在此过程中转换某些状态(连接).我通常按如下方式处理此问题:
state输入作为输入并返回{:ok, new_state}或{:error, reason}.{:error, reason}或者{:ok, last_returned_state}所有检查都成功.让我们先看看泛型函数:
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
Run Code Online (Sandbox Code Playgroud)
现在,我们可以使用它如下:
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
Run Code Online (Sandbox Code Playgroud)
或者将所有检查移动到命名的私有函数,然后执行:
perform_checks(conn, [
&check_mail_presence/1,
&check_mail_format/1,
...
])
Run Code Online (Sandbox Code Playgroud)
您还可以查看elixir-pipes,它可能会帮助您用管道表达它.
最后,在Phoenix/Plug的上下文中,您可以将检查声明为一系列插件并在第一次出错时停止.
ewH*_*ewH 18
我知道这个问题很老,但我遇到了同样的情况,发现从Elixir 1.2开始,你也可以使用with使你的代码非常易读的语句.do:如果所有子句匹配,则执行该块,否则将暂停该块,并返回不匹配的值.
例
defmodule MyApp.UserController do
use MyApp.Web, :controller
def create(conn, params) do
valid =
with {:ok} <- email_present?(params["email"]),
{:ok} <- email_proper_length?(params["email"),
{:ok} <- password_present?(params["password"]),
do: {:ok} #or just do stuff here directly
case valid do
{:ok} -> do stuff and render ok response
{:error, error} -> render error response
end
end
defp email_present?(email) do
case email do
nil -> {:error, "Email is required"}
_ -> {:ok}
end
end
defp email_proper_length?(email) do
cond do
String.length(email) >= 5 -> {:ok}
true -> {:error, "Email must be at least 5 characters"}
end
end
defp password_present?(password) do
case email do
nil -> {:error, "Password is required"}
_ -> {:ok}
end
end
end
Run Code Online (Sandbox Code Playgroud)
您正在寻找的是我称之为"提前退出"的内容.很久以前,当我开始使用F#中的函数式编程时,我遇到了同样的问题.我得到的答案可能是有益的:
这也是对这个问题的一个很好的讨论(尽管它也是F#):
http://fsharpforfunandprofit.com/posts/recipe-part2/
TL; DR将您的函数构造为一系列函数,每个函数获取并返回一个原子元组和要检查的密码字符串.原子将是:ok或:error.像这样:
defmodule Password do
defp password_long_enough?({:ok = a, p}) do
if(String.length(p) > 6) do
{:ok, p}
else
{:error,p}
end
end
defp starts_with_letter?({:ok = a, p}) do
if(String.printable?(String.first(p))) do
{:ok, p}
else
{:error,p}
end
end
def password_valid?(p) do
{:ok, _} = password_long_enough?({:ok,p}) |> starts_with_letter?
end
end
Run Code Online (Sandbox Code Playgroud)
你会像这样使用它:
iex(7)> Password.password_valid?("ties")
** (FunctionClauseError) no function clause matching in Password.starts_with_letter?/1
so_test.exs:11: Password.starts_with_letter?({:error, "ties"})
so_test.exs:21: Password.password_valid?/1
iex(7)> Password.password_valid?("tiesandsixletters")
{:ok, "tiesandsixletters"}
iex(8)> Password.password_valid?("\x{0000}abcdefg")
** (MatchError) no match of right hand side value: {:error, <<0, 97, 98, 99, 100, 101, 102, 103>>}
so_test.exs:21: Password.password_valid?/1
iex(8)>
Run Code Online (Sandbox Code Playgroud)
当然,您需要构建自己的密码测试,但一般原则仍然适用.
编辑:Zohaib Rauf 就这个想法做了一篇非常广泛的博客文章.非常值得一读.
| 归档时间: |
|
| 查看次数: |
9608 次 |
| 最近记录: |