Current section

Files

Jump to
drill lib tasks drill.ex
Raw

lib/tasks/drill.ex

defmodule Mix.Tasks.Drill do
@moduledoc """
Runs all seeder modules under "priv/YOUR_REPO/seeds. Needs the Repo as an argument.
$ mix drill -r MyApp.Repo
Setting seeds directory is similar to [Ecto migration](https://hexdocs.pm/ecto_sql/Mix.Tasks.Ecto.Migrate.html),
1. "YOUR_REPO" is the last segment in your repository name. E.g. MyApp.MyRepo will use
"priv/my_repo/seeds".
2. You can configure a repository to use another directory by specifying the :priv key under the repository configuration.
The "seeds" part will be automatically appended to it. For instance, to use "priv/custom_repo/seeds":
config :my_app, MyApp.Repo, priv: "priv/custom_repo"
3. You can also set the directory by adding the following to your config:
config :drill, :directory, "seeds"
"""
@shortdoc "Seeding task"
use Mix.Task
import Mix.Ecto
alias Drill.Seeder
alias Drill.Utils
alias Ecto.Migrator
@impl Mix.Task
def run(args) do
repo = parse_repo(args) |> hd()
ensure_repo(repo, [])
Migrator.with_repo(repo, fn repo ->
Migrator.run(repo, :up, all: true)
seed(repo)
end)
end
def seed(repo) do
otp_app = Application.get_env(:drill, :otp_app)
if otp_app,
do: IO.warn("Setting otp_app is deprecated. It will now be inferred from the repo.")
task_timeout = Application.get_env(:drill, :timeout, 600_000)
seed_dir = Application.get_env(:drill, :directory, "seeds")
seeder_modules = Seeder.list_seeder_modules(repo, seed_dir)
Mix.shell().info("Arranging modules by dependencies")
seeder_modules =
seeder_modules
|> Utils.sort_seeders_by_deps()
|> warn_not_able_to_run_seeders(seeder_modules)
Task.async(fn ->
Enum.reduce(seeder_modules, %Drill.Context{}, fn seeder, ctx ->
Mix.shell().info("#{seeder} started")
entries = Drill.build_entries(seeder, ctx)
key = seeder.context_key()
constraints = seeder.constraints()
on_conflict = seeder.on_conflict()
source = seeder.schema()
autogenerated_fields = seeder.autogenerate()
entries = Utils.merge_autogenerated_fields_to_entries(autogenerated_fields, entries)
{_, result} = insert_all(repo, source, entries, constraints, on_conflict)
seeds = Map.put(ctx.seeds, key, result)
Mix.shell().info("#{seeder} finished")
%{ctx | seeds: seeds}
end)
end)
|> Task.await(task_timeout)
Mix.shell().info("Drill seeded successfully")
end
defp insert_all(repo, source, entries, [], _) do
repo.insert_all(source, entries, returning: true)
end
defp insert_all(repo, source, entries, constraints, on_conflict) do
repo.insert_all(source, entries,
on_conflict: on_conflict,
returning: true,
conflict_target: constraints
)
end
defp warn_not_able_to_run_seeders(arranged_seeders, all_seeders) do
unmatched_seeders = all_seeders -- arranged_seeders
if Enum.empty?(unmatched_seeders) do
arranged_seeders
else
Mix.shell().info(
"Unable to run seeders due to deps not found: #{inspect(unmatched_seeders)}"
)
arranged_seeders
end
end
end