defmodule Mix.Tasks.Drill do @moduledoc """ Runs all seeder modules that `use` Drill. Needs the Repo as an argument. $ mix drill --r MyApp.Repo Be sure to set the `otp_app` that contains the seeder modules in your config: config :drill, :otp_app, :my_app """ @shortdoc "Seeding task" use Mix.Task import Mix.Ecto 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.fetch_env!(:drill, :otp_app) {:ok, modules} = :application.get_key(otp_app, :modules) seeder_modules = Enum.filter(modules, fn module -> Drill in (module.__info__(:attributes)[:behaviour] || []) end) 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 = seeder.run(ctx) key = seeder.context_key() constraints = seeder.constraints() source = seeder.schema() autogenerated_fields = seeder.autogenerate() entries = Utils.merge_autogenerated_fields_to_entries(autogenerated_fields, entries) {_, result} = insert_all(repo, source, entries, constraints) seeds = Map.put(ctx.seeds, key, result) Mix.shell().info("#{seeder} finished") %{ctx | seeds: seeds} end) end) |> Task.await() 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) do repo.insert_all(source, entries, on_conflict: :replace_all, 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