Skip to content

Instantly share code, notes, and snippets.

@baldwindavid
Created April 2, 2021 17:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baldwindavid/780449125ac4e9d064662895a0d65703 to your computer and use it in GitHub Desktop.
Save baldwindavid/780449125ac4e9d064662895a0d65703 to your computer and use it in GitHub Desktop.
Build Stubbed Strategy for ExMachina
defmodule MyApp.Factories.BuildStubbedStrategy do
use ExMachina.Strategy, function_name: :build_stubbed
alias Ecto.Association.BelongsTo
alias Ecto.Association.Has
alias Ecto.Association.NotLoaded
alias Ecto.UUID
def handle_build_stubbed(record, _opts \\ []) do
record
|> ensure_id()
|> ensure_inserted_at()
|> ensure_updated_at()
|> stub_associations()
end
defp ensure_id(%{id: nil} = record), do: Map.put(record, :id, UUID.generate())
defp ensure_id(record), do: record
defp ensure_inserted_at(%{inserted_at: nil} = record), do: Map.put(record, :inserted_at, now())
defp ensure_inserted_at(record), do: record
defp ensure_updated_at(%{updated_at: nil} = record), do: Map.put(record, :updated_at, now())
defp ensure_updated_at(record), do: record
defp stub_associations(record) do
record.__struct__.__schema__(:associations)
|> Enum.map(&record.__struct__.__schema__(:association, &1))
|> Enum.reduce(record, fn assoc_type, record ->
assoc_name = assoc_type.field
assoc_record = Map.get(record, assoc_name)
case assoc_record do
%NotLoaded{} ->
record
_ ->
stub_association(assoc_type, assoc_name, assoc_record, record)
end
end)
end
defp stub_association(%BelongsTo{owner_key: assoc_fk_name}, assoc_name, parent, record) do
parent = handle_build_stubbed(parent)
record
|> Map.put(assoc_name, parent)
|> Map.put(assoc_fk_name, parent.id)
end
defp stub_association(%Has{cardinality: :one}, assoc_name, child, record) do
child = handle_build_stubbed(child)
Map.put(record, assoc_name, child)
end
defp stub_association(%Has{cardinality: :many}, assoc_name, children, record) do
children = Enum.map(children, fn child -> handle_build_stubbed(child) end)
Map.put(record, assoc_name, children)
end
defp now, do: DateTime.truncate(Timex.now(), :second)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment