Packages

An ecto plugin for recursively merging records and it's associations

Current section

Files

Jump to
ecto_merge lib ecto_merge.ex
Raw

lib/ecto_merge.ex

defmodule EctoMerge do
@moduledoc """
This module holds helper funcions for recursively
merging ecto records ands it's associations.
"""
import __MODULE__.Validator, only: [validate_mergeable!: 1]
import __MODULE__.AssociationMerger, only: [merge_assocs: 4]
import Ecto.Query, only: [from: 2]
alias __MODULE__.Utils
alias Ecto.{Changeset, Multi}
@doc "Merges a list of records and it's associations recursively"
def merge!(repo, records) do
validate_mergeable!(records)
schema = get_schema(records)
ids = Enum.map(records, &(&1.id))
changeset = merge_into_changeset(repo, records)
{:ok, %{result: result}} =
Multi.new
|> Multi.insert(:result, changeset)
|> Multi.delete_all(:deleted, from(r in schema, where: r.id in ^ids))
|> repo.transaction
result
end
defp merge_into_changeset(repo, records) do
schema = get_schema(records)
assocs = schema.__schema__(:associations)
assocs_info = Enum.map(assocs, &schema.__schema__(:association, &1))
base_changeset =
schema
|> struct()
|> schema.changeset(%{})
Enum.reduce(records, base_changeset, &(
&2
|> merge_plain_attrs(&1)
|> merge_assocs(&1, assocs_info, repo)
))
end
defp merge_plain_attrs(changeset, record) do
plain_attrs =
record
|> Map.from_struct
|> Map.delete(:id)
|> Utils.cleanup_changes
Changeset.change(changeset, plain_attrs)
end
defp get_schema([head | _tail]), do: get_schema(head)
defp get_schema(%{__struct__: schema}), do: schema
end