Skip to content

Instantly share code, notes, and snippets.

@DanCouper
Last active May 10, 2020 13:40
Show Gist options
  • Save DanCouper/84b4e15306896d0260179f4c9ee35c1c to your computer and use it in GitHub Desktop.
Save DanCouper/84b4e15306896d0260179f4c9ee35c1c to your computer and use it in GitHub Desktop.
collection schemas
defmodule Collection.Artist do
use Ecto.Schema
schema "artists" do
field(:name, :string)
end
def changeset(struct, params) do
struct
|> Ecto.Changeset.cast(params, [:name])
|> Ecto.Changeset.validate_required([:name])
end
end
defmodule Collection do
alias Collection.{Release, Repo}
@type track :: %{title: String.t(), artists: [String.t()]}
@spec add_release(%{
label: String.t(),
title: String.t(),
tracks: [track()],
year: integer()
}) :: any
def add_release(release_map) do
%Release{}
|> Release.changeset(release_map)
|> Repo.insert!()
end
end
defmodule Collection.Release do
use Ecto.Schema
import Ecto.Query, only: [from: 2]
schema "releases" do
field(:title, :string)
field(:year, :integer)
field(:label, :string)
field(:notes, :string)
many_to_many(:tracks, Collection.Track, join_through: "tracks_releases")
many_to_many(:artists, Collection.Artist, join_through: "releases_artists")
end
def changeset(struct, params) do
struct
|> Ecto.Changeset.cast(params, [:title, :year, :label, :notes])
|> Ecto.Changeset.validate_required([:title, :year])
|> Ecto.Changeset.validate_number(:year, greater_than: 1940, less_than: 2020)
|> Ecto.Changeset.put_assoc(:tracks, get_or_insert_tracks(params[:tracks]))
|> Ecto.Changeset.put_assoc(:artists, get_or_insert_artists(params[:tracks]))
end
def get_or_insert_tracks(tracks) do
Collection.Repo.insert_all(Collection.Track, tracks, on_conflict: :nothing)
track_titles = Enum.map(tracks, fn %{title: title} -> title end)
tracks_query = from t in Collection.Track, where: t.title in ^track_titles
Collection.Repo.all(tracks_query)
end
def get_or_insert_artists([head | rest] = _tracks) do
%{artists: head_artists} = head
release_artists =
if Enum.all?(rest, fn %{artists: artists} -> artists == head_artists end) do
Enum.map(head_artists, fn artist -> %{name: artist} end)
else
[%{name: "Various Artists"}]
end
Collection.Repo.insert_all(
Collection.Artist,
release_artists,
on_conflict: :nothing
)
artists_query = from a in Collection.Artist, where: a.name in ^release_artists
Collection.Repo.all(artists_query)
end
end
defmodule Collection.Track do
use Ecto.Schema
import Ecto.Query, only: [from: 2]
schema "tracks" do
field(:title, :string)
field(:notes, :string)
many_to_many(:artists, Collection.Artist, join_through: "tracks_artists")
end
def changeset(struct, params) do
struct
|> Ecto.Changeset.cast(params, [:title, :notes])
|> Ecto.Changeset.validate_required([:title])
|> Ecto.Changeset.put_assoc(:artists, get_or_insert_artists(params[:artists]))
end
def get_or_insert_artists(artists) do
Collection.Repo.insert_all(
Collection.Artist,
Enum.map(artists, fn artist -> %{name: artist} end),
on_conflict: :nothing
)
artists_query = from a in Collection.Artist, where: a.name in ^artists
Collection.Repo.all(artists_query)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment