Elixir和功能输入验证

时间:2016-07-14 20:39:39

标签: elixir

我正在尝试学习elixir(但主要是函数式编程)

我正在实现一个非常简单的GenServer,它基本上包含了一个条目列表。每个参数的最大条目数和最大大小(以字节为单位)受到限制(配置文件)

defmodule List do

    def init(_) do 
        {:ok, []}
    end

    def handle_call({:insert, param1, param2, param3}, from, list) do

        import Application

        param1_max_size = get_env(:app, ....)
        param2_max_size = get_env(:app, ....)
        param2_max_size = get_env(:app, ....)

        max_items_count = get_env(:app, ....)

        ## should be {:reply, {:error, :your_list_is_full}, list}  if list is full 

        ## should be {:reply, {:error, {:check_this_args_please, wrong_params_list}, list} if any param is wrong. wrong_params_list contains the offending params 

        ## should be {:reply, {:ok}, [{param1, param2, param3} | list ]} otherwise

    end

end

我知道这似乎很容易,但基本上我正试图找到一种优雅的功能性方法来做到这一点。我的头是程序性的,我总是在使用=运算符的“嵌套 - 如果 - 其他地狱”结束,如果它是C的那个。

THX

1 个答案:

答案 0 :(得分:3)

我知道用于验证多个参数的最简洁方法是Ecto及其changesets。您可能希望实现非常相似的东西。

  1. 创建包含值和验证错误的数据结构
  2. 使所有验证函数将该数据结构作为第一个参数,并返回此数据结构以便于管道化。我将以changeset的方式称呼Ecto
  3. 代码可能是这样的:

    changeset = %{data: {param1, param2, param3}, errors: []}
    
    def validate_param(changeset, number) do
      max_size = get_env(:app, :"param#{number}"
      if elem(changeset.data, number-1) < max_size do
        changeset
      else
        errors = [{:"param#{number}", elem(changeset.data, number-1)} | changeset.errors
        %{ changeset | errors: errors}
      end
    end
    
    validated_changeset = 
      changeset
      |> validate_param(1)
      |> validate_param(2)
      |> validate_param(3)
    
    case validated_changeset.errors do
      [] -> {:reply, :ok, [changeset.data | list]}
      errors -> {:reply, errors, list}
    end
    

    在Elixir中,您可以使用引号定义原子以包含特殊字符。如果没有特殊字符,则与没有引号相同:

    iex(1)> :"a b"
    :"a b"
    iex(10)> :"ab"
    :ab
    

    在内部引号中,您可以使用字符串插值,这样您就可以使用原子validate_param创建泛型:"param#{number}"

    elem函数为零索引,因此我们需要从number中减去1:elem(changeset.data, number-1)

    如果没有错误,我们将返回变更集而不做任何修改。

    错误将具有结构{field_name, field_value}。将list添加到变更集并添加另一个验证函数来检查列表长度并附加正确的错误应该很容易。

    您还可以使用validate_param调用List.foldl 3次删除重复:

    List.foldl([1, 2, 3], changeset, fn(number, changeset) -> validate_number(changeset, number) end)
    

    甚至更短:

    List.foldl([1, 2, 3], changeset, &validate_number(&2, &1))
    

    最重要的是返回与第一个参数相同类型的函数模式。它可以轻松管道,避免使用许多嵌套的ifcase语句。