defmodule Mix.Tasks.Attached.Gen.Migration do
@shortdoc "Generates a migration for an attachment field"
@moduledoc """
Generates an Ecto migration for an `attached` field on a schema.
$ mix attached.gen.migration MyApp.Accounts.User avatar
Adds an `avatar_attached_original_id` column to the schema's table
(respects `config :attached, default_foreign_key_suffix: "_original_id"`).
"""
use Mix.Task
import Mix.Generator
@impl true
def run(args) do
case args do
[schema_module, field] ->
generate(schema_module, field)
_ ->
Mix.shell().error("""
Usage: mix attached.gen.migration Schema field
Example:
mix attached.gen.migration MyApp.Accounts.User avatar
""")
end
end
defp generate(schema_module, field) do
migrations_path = Path.join(["priv", "repo", "migrations"])
File.mkdir_p!(migrations_path)
module_parts = String.split(schema_module, ".")
schema_name = List.last(module_parts) |> Macro.underscore()
table_name = schema_name <> "s"
timestamp = timestamp()
name = "add_#{field}_attached_original_to_#{table_name}"
filename = "#{timestamp}_#{name}.exs"
content = one_migration(schema_module, table_name, field, name)
path = Path.join(migrations_path, filename)
create_file(path, content)
Mix.shell().info("Run `mix ecto.migrate` to apply.")
end
defp one_migration(schema_module, table_name, field, name) do
app = Mix.Project.config()[:app]
migration_module =
"#{app |> to_string() |> Macro.camelize()}.Repo.Migrations.#{Macro.camelize(name)}"
fk_suffix = Application.get_env(:attached, :default_foreign_key_suffix, "_attached_original_id")
fk_col = "#{field}#{fk_suffix}"
"""
defmodule #{migration_module} do
use Ecto.Migration
def change do
# #{schema_module} — attached :#{field}
alter table(:#{table_name}) do
add :#{fk_col}, references(:attached_originals, type: :binary_id, on_delete: :restrict)
end
create index(:#{table_name}, [:#{fk_col}])
end
end
"""
end
defp timestamp do
{{y, m, d}, {hh, mm, ss}} = :calendar.universal_time()
"#{y}#{pad(m)}#{pad(d)}#{pad(hh)}#{pad(mm)}#{pad(ss)}"
end
defp pad(i) when i < 10, do: "0#{i}"
defp pad(i), do: "#{i}"
end