docs: backfill TOC + decision trees, fix review findings
- Add ## Contents and ## Decision Tree to all 10 existing pattern files - Fix embed_as/1 semantics inversion in types.md (:self → :dump) - Fix fabricated __meta__.changes reference in changesets.md - Fix default primary key type (:integer → :id) in schemas.md - Combine @impl subsections into single "Minimal Callback Annotation"
This commit is contained in:
+9
-9
@@ -135,21 +135,21 @@ end
|
||||
|
||||
When a custom type is used inside an `embeds_one` or `embeds_many` field, Ecto calls `embed_as/1` to decide whether to pass the value through `dump/1` or treat it as its own serialized form. The callback receives the embed format (`:json` by default) and returns either `:self` or `:dump`.
|
||||
|
||||
- `:self` — the value is used as-is when exporting embedded data (dump is still called for DB storage)
|
||||
- `:dump` — `dump/1` is always called, even in embedded contexts
|
||||
- `:self` — the in-memory value is used as-is without calling `dump/1`; appropriate when the runtime representation is already JSON-compatible (scalars, plain maps)
|
||||
- `:dump` — `dump/1` is called to serialize the value before encoding; needed when the runtime representation (e.g., a struct) is not directly JSON-serializable
|
||||
|
||||
`use Ecto.Type` provides a default implementation that returns `:self`. Override it when your type must always run `dump/1` to produce its storable form, even when nested inside an embedded schema.
|
||||
`use Ecto.Type` provides a default implementation that returns `:self`. Override it to return `:dump` when your type holds an Elixir struct or other value that cannot be directly encoded to JSON.
|
||||
|
||||
```elixir
|
||||
defmodule EctoURI do
|
||||
use Ecto.Type
|
||||
|
||||
# Override to ensure dump/1 is called in embedded contexts
|
||||
def embed_as(_format), do: :self
|
||||
# Override: %URI{} is not JSON-serializable, so run dump/1 in embedded contexts
|
||||
def embed_as(_format), do: :dump
|
||||
end
|
||||
```
|
||||
|
||||
**Why:** When Ecto builds embedded documents for export (e.g., storing a JSON blob), it needs to know whether to trust the in-memory value or to re-serialize it. If your type holds state in an Elixir struct that cannot be stored directly (like `%URI{}`), returning `:self` without a proper `dump/1` would persist the raw struct map rather than your intended shape. Overriding `embed_as/1` makes the contract explicit.
|
||||
**Why:** When Ecto builds embedded documents for export (e.g., storing a JSON blob), it needs to know whether to trust the in-memory value or to re-serialize it. If your type holds state in an Elixir struct that cannot be stored directly (like `%URI{}`), the default `:self` passes the raw struct to the JSON encoder — which either raises or produces garbage like `%{__struct__: "Elixir.URI", host: ..., ...}`. Returning `:dump` ensures `dump/1` converts it to a clean map first.
|
||||
|
||||
**Anti-pattern:** Assuming the default `:self` is correct for a type whose in-memory and storable representations differ, then debugging mysterious embedded schema corruption:
|
||||
```elixir
|
||||
@@ -199,8 +199,8 @@ defmodule EctoURI do
|
||||
def dump(%URI{} = uri), do: {:ok, Map.from_struct(uri)}
|
||||
def dump(_), do: :error
|
||||
|
||||
# Explicit: always pass through dump/1 when exporting embedded values
|
||||
def embed_as(_format), do: :self
|
||||
# Explicit: ensure dump/1 runs in embedded contexts to produce a clean map
|
||||
def embed_as(_format), do: :dump
|
||||
end
|
||||
```
|
||||
|
||||
@@ -209,7 +209,7 @@ end
|
||||
**Don't use this when:**
|
||||
- Your type is never used in embedded schemas (the callback has no effect)
|
||||
- The in-memory and storable representations are the same (plain maps, scalars)
|
||||
- You intentionally want to skip `dump/1` in embedded contexts (then return `:dump` and document why)
|
||||
- You intentionally want to skip `dump/1` in embedded contexts (the default `:self` already does this)
|
||||
|
||||
**Over-application example:**
|
||||
```elixir
|
||||
|
||||
Reference in New Issue
Block a user