View Source EctoDiff (ecto_diff v0.5.0)
Generates a data structure describing the difference between two ecto structs.
For details on how to generate an EctoDiff.t/0
struct, see: diff/2
.
For details on what the generated struct looks like, see: EctoDiff.t/0
.
Link to this section Summary
Types
Configurable options for diff/3
.
The type of change for a given struct.
A keyword list or a map which specifies an override from an Ecto schema to the desired primary key, for use in comparing structs.
A struct field or list of fields used to define a simple or composite primary key.
A field defined on a struct.
Describes all changes made during an insert or update operation.
Functions
Returns an EctoDiff.t/0
describing the difference between two given ecto structs.
An alternate form of diff/2
which allows options to be specified.
Link to this section Types
@type diff_opts() :: [{:overrides, overrides()}]
Configurable options for diff/3
.
options
Options
:overrides
- A keyword list or map which provides a reference from a struct to a key (or list of keys) on that struct which will be used as the primary key (simple or composite) for diffing.
@type effect() :: :added | :deleted | :changed | :replaced
The type of change for a given struct.
Each EctoDiff.t/0
struct will have a field describing what happened to the given Ecto schema struct.
The values can be one of the following:
:added
- This struct is new and was not present previously. This happens when the primary struct, or an associatedstruct, was added during an update or insert.
:deleted
- This struct was previously present, but no longer is.:changed
- This struct existed previously and still does, but some of its fields and/or associations have changed.:replaced
- This struct was replaced with a completely new one. This happens withbelongs_to
orembeds_one
associations with the `on_replace: :nilify` option set.
@type overrides() :: [{module(), primary_key()}] | %{required(module()) => primary_key()}
A keyword list or a map which specifies an override from an Ecto schema to the desired primary key, for use in comparing structs.
Structs that are not specified will be compared using their default primary key.
examples
Examples:
[{Pet, :refid}, {Skill, :id}]
or
%{Skill => :refid, Owner => [:id, :refid]}
@type primary_key() :: struct_field() | [struct_field()]
A struct field or list of fields used to define a simple or composite primary key.
@type struct_field() :: atom()
A field defined on a struct.
@type t() :: %EctoDiff{ changes: %{required(struct_field()) => any()}, current: Ecto.Schema.t(), effect: effect(), previous: Ecto.Schema.t(), primary_key: %{required(struct_field()) => any()}, struct: atom() }
Describes all changes made during an insert or update operation.
The following fields should be considered public:
struct
- The module atom of the ecto schema being diffed.primary_key
- The primary key(s) of the ecto struct. This is amap
of all primary keys in case of composite keys. For most common use-cases this will just be the map%{id: id}
.changes
- Amap
representing all changes made. The keys will be fields and associations defined in the ecto schema, but only fields and associations with changes will be present. For changed fields, the value will be atuple
representing the previous and new values (i.e.{previous, new}
). For associations, the value will be anotherEctoDiff.t/0
struct for cardinality "one" associations, or a list ofEctoDiff.t/0
structs for cardinality "many" associations.effect
- The type of change for this ecto struct. Seeeffect/0
for details.previous
- The previous struct itself.current
- The current (new) struct itself.
Link to this section Functions
@spec diff(Ecto.Schema.t() | nil, Ecto.Schema.t() | nil) :: {:ok, t()} | {:ok, :unchanged}
Returns an EctoDiff.t/0
describing the difference between two given ecto structs.
The "previous" struct can be nil
, to represent an insert operation.
examples
Examples
A new struct being inserted:
iex> {:ok, pet} = %{name: "Spot"} |> Pet.new() |> Repo.insert()
iex> {:ok, diff} = EctoDiff.diff(nil, pet)
iex> diff
#EctoDiff<
struct: Pet,
primary_key: %{id: 1},
effect: :added,
previous: #Pet<>,
current: #Pet<>,
changes: %{
id: {nil, 1},
name: {nil, "Spot"}
}
>
A struct being updated:
iex> {:ok, pet} = %{name: "Spot"} |> Pet.new() |> Repo.insert()
iex> {:ok, updated_pet} = pet |> Pet.update(%{name: "McFluffFace"}) |> Repo.update()
iex> {:ok, diff} = EctoDiff.diff(pet, updated_pet)
iex> diff
#EctoDiff<
struct: Pet,
primary_key: %{id: 1},
effect: :changed,
previous: #Pet<>,
current: #Pet<>,
changes: %{
name: {"Spot", "McFluffFace"}
}
>
A nested has_many association being updated:
iex> {:ok, initial_pet} =
...> %{name: "Spot", skills: [%{name: "Eating"}, %{name: "Sleeping"}, %{name: "Scratching"}]}
...> |> Pet.new()
...> |> Repo.insert()
iex> [eating_id, sleeping_id, _scratching_id] = Enum.map(initial_pet.skills, & &1.id)
iex> {:ok, updated_pet} =
...> initial_pet
...> |> Pet.update(%{skills: [%{id: eating_id}, %{id: sleeping_id, level: 2}, %{name: "Meowing"}]})
...> |> Repo.update()
iex> {:ok, diff} = EctoDiff.diff(initial_pet, updated_pet)
iex> diff
#EctoDiff<
struct: Pet,
primary_key: %{id: 1},
effect: :changed,
previous: #Pet<>,
current: #Pet<>,
changes: %{
skills: [
#EctoDiff<
struct: Skill,
primary_key: %{id: 2},
effect: :changed,
previous: #Skill<>,
current: #Skill<>,
changes: %{
level: {1, 2}
}
>,
#EctoDiff<
struct: Skill,
primary_key: %{id: 3},
effect: :deleted,
previous: #Skill<>,
current: #Skill<>,
changes: %{}
>,
#EctoDiff<
struct: Skill,
primary_key: %{id: 4},
effect: :added,
previous: #Skill<>,
current: #Skill<>,
changes: %{
id: {nil, 4},
pet_id: {nil, 1},
name: {nil, "Meowing"}
}
>
]
}
>
Using an override to specify a composite primary key:
iex> {:ok, pet} = %{name: "Spot", type: "Calico"} |> Pet.new() |> Repo.insert()
iex> {:ok, diff} = EctoDiff.diff(nil, pet, overrides: %{Pet => [:name, :type]})
iex> diff
#EctoDiff<
struct: Pet,
primary_key: %{name: "Spot", type: "Calico"},
effect: :added,
previous: #Pet<>,
current: #Pet<>,
changes: %{
id: {nil, 1},
name: {nil, "Spot"},
type: {"Cat", "Calico"}
}
>
@spec diff(Ecto.Schema.t() | nil, Ecto.Schema.t() | nil, diff_opts()) :: {:ok, t()} | {:ok, :unchanged}
An alternate form of diff/2
which allows options to be specified.
See diff_opts/0
for available options.