Пользовательские типы Ecto: как представить поле в модели как определенный тип?

У меня есть схема, где я хочу, чтобы одно из полей было представлено в виде %Cm{value: 1.0} (для сантиметровых единиц).

Я определил этот тип обычая:

defmodule Db.Types.Cm do
  alias Units.Cm

  @behavior Ecto.Type
  def type, do: :float

  def cast(%Cm{value: integer}) when is_integer(integer) do
    Cm.new(integer / 1.0)
  end

  def cast(val = %Cm{value: float}) when is_float(float) do
    val
  end

  def cast(number) when is_float(number), do: Cm.new(number)
  def cast(number) when is_integer(number), do: Cm.new(number / 1.0)
  def cast(_), do: :error

  def load(float) when is_float(float), do: Cm.new(float)

  def dump(%Cm{value: float}) when is_float(float), do: float
  def dump(%Cm{value: integer}) when is_integer(integer), do: (integer / 1.0)
  def dump(_), do: :error
end

Следуя этим рекомендациям из документов ( https://hexdocs.pm/ecto/Ecto.Type.html):

  • type должен вывести имя типа БД
  • cast должен получить любой тип и вывести ваш собственный тип Ecto
  • load должен получить тип БД и вывести ваш собственный тип Ecto
  • dump должен получить ваш собственный тип Ecto и вывести тип DB

И следующая схема:

defmodule Db.Block do
  schema "blocks" do
    field :max_depth, Types.Cm
    timestamps()
  end

  @fields ~w(max_depth)a

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @fields)
  end
end

Теперь я пытаюсь сохранить блоки в БД:

defmodule Db.BlockHelpers do
  def new_block(attributes \\ %{}) do
    block = Dict.merge(%{
      max_depth: 2
    }, attributes)

    %Block{}
    |> Block.changeset(block)
    |> Repo.insert!
  end
end

iex> new_block()
...> new_block(%{max_depth: Units.Cm.new(5.0)})

Однако я продолжаю получать ошибки:

 ** (CaseClauseError) no case clause matching: %Units.Cm{value: 2.0}

Я пробовал различные комбинации подходов, но, похоже, не могу понять это правильно. Так что я не уверен на 100%, что понимаю документацию.

В конце концов, я хочу иметь возможность передать модель структуры формы %Block{max_depth: %Units.Cm{value: 1.0}}где значение cm хранится в базе данных в виде числа с плавающей запятой (postgres).

1 ответ

Решение

Правильный ответ, предоставленный Догбертом: возвращался value в отличие от {:ok, value},

Другие вопросы по тегам