I’m currently trying to make a small integration layer between Ecto.Schema and mongodb_driver. So far I’ve managed to make it work, but I got stuck when dealing with custom types, namely they don’t transform when applying the changeset.
Let’s say I have the following setup:
defmodule TestSchema do
use Ecto.Schema
import Ecto.Changeset
embedded_schema do
field(:custom, Ecto.Enum, values: [test: 1, other: 2])
end
def changeset(%__MODULE__{} = schema, attrs) do
schema
|> cast(attrs, [:custom])
|> validate_required([:custom])
end
end
I’m using apply_action!/2 to transform the data from a changeset to a valid structure. The issue is that I get:
I’ve tried to play with the embed_as option provided by Ecto.Enum but it doesn’t seem to make any difference.
Is there something I am missing to make this work?
PS: I have control over the dump process, so I can write functionality to dump/load these custom types, but it feels like ecto should do this by default.
That expectation is wrong. Ecto types deal with 3 representations of data.
External data format (usually user input)
Runtime data format
Database storage format
The act of casting data in ecto takes data in 1. (or 2.) and transforms to 2. That is what you’re getting returned here expectedly. The act of applying a changeset only goes from changeset → plain schema struct. Schema structs generally hold values in the 2. format (the result of valid changes in changesets).
You want to look at Ecto.Type.dump / Ecto.Type.embedded_dump to go from 2. → 3.
This is exactly is what I tried at first, it doesn’t seem that that option does anything. apply_action/2 calls apply_changes/1 under the hood, so it’s basically the same function:
def apply_action(%Changeset{} = changeset, action) when is_atom(action) do
if changeset.valid? do
{:ok, apply_changes(changeset)}
else
{:error, %Changeset{changeset | action: action}}
end
end
OK, makes sense, but then it means that using ecto without a database implies that you will be missing a part of features, such as custom types, is that correct?
I find it quite strange that ecto types offer functionality for embedded dumps, but at the same time doesn’t offer a way to do that officially(without going into ecto internals and implementing it yourself), unless I am missing something, like a analogue to apply_changes/1 but which does the dump?