在ecto中实现自定义包装器类型

时间:2016-12-09 12:35:35

标签: elixir phoenix-framework ecto

我希望有一个自定义类型,可以将原子来回转换为数字(因此可以很容易地将它们存储在数据库中)。

我尝试实现简单的Ecto Type。

defmodule Kpsz.PressenceState do
  @behaviour Ecto.Type
  @moduledoc """
  Representation of pressence state. It will store data as integers and convert them to atoms
  """

  @bindings %{
    1 => :unknown,
    2 => :wanted,
    3 => :confirmed
  }

  @reverse_bindings  Enum.map(@bindings,fn  {key, value} ->
    {value, key}
  end)

  def reduce(index) when is_number(index), do: @bindings[index]
  def reduce(atom) when is_atom(atom), do: @reverse_bindings[atom]

  def type, do: :integer

  def cast(arg) when is_number(arg), do: {:ok, arg}
  def cast(arg) when is_atom(arg), do: {:ok, reduce(arg)}
  def cast(_), do: :error

  def load(integer) when is_number(integer), do: reduce(integer)
  def load(_), do: :error

  def dump(atom) when is_atom(atom), do: reduce(atom)
  def dump(_), do: :error
end

但它在插入时抱怨:

Server: localhost:4000 (http)
Request: POST /pressence
** (exit) an exception was raised:
    ** (Ecto.ChangeError) value `2` for `Kpsz.Pressence.state` in `insert` does not match type Kpsz.PressenceState
        (ecto) lib/ecto/repo/schema.ex:629: Ecto.Repo.Schema.dump_field!/6
        (ecto) lib/ecto/repo/schema.ex:638: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
        (stdlib) lists.erl:1263: :lists.foldl/3
        (ecto) lib/ecto/repo/schema.ex:636: Ecto.Repo.Schema.dump_fields!/5
        (ecto) lib/ecto/repo/schema.ex:585: Ecto.Repo.Schema.dump_changes!/6
        (ecto) lib/ecto/repo/schema.ex:190: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
        (ecto) lib/ecto/repo/schema.ex:614: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
        (ecto) lib/ecto/adapters/sql.ex:508: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3
        (db_connection) lib/db_connection.ex:1063: DBConnection.transaction_run/4
        (db_connection) lib/db_connection.ex:987: DBConnection.run_begin/3
        (db_connection) lib/db_connection.ex:667: DBConnection.transaction/3
        (kpsz) web/controllers/pressence_controller.ex:7: Kpsz.PressenceController.assign/2
        (kpsz) web/controllers/pressence_controller.ex:1: Kpsz.PressenceController.action/2
        (kpsz) web/controllers/pressence_controller.ex:1: Kpsz.PressenceController.phoenix_controller_pipeline/2
        (kpsz) lib/kpsz/endpoint.ex:1: Kpsz.Endpoint.instrument/4
        (kpsz) lib/phoenix/router.ex:261: Kpsz.Router.dispatch/2
        (kpsz) web/router.ex:1: Kpsz.Router.do_call/2
        (kpsz) lib/kpsz/endpoint.ex:1: Kpsz.Endpoint.phoenix_pipeline/1
        (kpsz) lib/plug/debugger.ex:123: Kpsz.Endpoint."call (overridable 3)"/2
        (kpsz) lib/kpsz/endpoint.ex:1: Kpsz.Endpoint.call/2

这就是我想要的方式:

def create(user, event, repo \\ Repo) do
  changeset = Pressence.changeset(%Pressence{}, %{event_id: event, user_id: user, state: :wanted})
  repo.insert(changeset)
end

1 个答案:

答案 0 :(得分:0)

您的dumpload函数都应该返回{:ok, value}而不仅仅是您正在做的值。

您可以查看文档here。相关部分是:

dump(type, value, dumper \\ &dump/2)
dump(t, term, (t, term -> {:ok, term} | :error)) :: {:ok, term} | :error