lib/mix/tasks/ecto.ots.gen.migration.ex

defmodule Mix.Tasks.Ecto.Ots.Gen.Migration do
  use Mix.Task

  import Macro, only: [camelize: 1, underscore: 1]
  import Mix.Generator
  import Mix.Ecto
  import Mix.EctoTablestore

  @shortdoc "Generate a migration to create a table of tablestore"

  @aliases [
    r: :repo
  ]

  @switches [
    change: :string,
    repo: [:string, :keep]
  ]

  @moduledoc """
  Generate a migration to create a table of tablestore.

  The repository must be set under `:ecto_repos` in the current app configuration,
  or given via the `-r` option:

  ## Example 1

    1, Add configuration:

      config :your_app, ecto_repos: [EctoTablestore.TestRepo]

    2, Run:

      mix ecto.ots.gen.migration create_posts

  ## Example 2

    1, Run:

      mix ecto.ots.gen.migration create_posts -r EctoTablestore.TestRepo

  The generated migration filename will be prefixed with the current timestamp in UTC which is
  used for versioning and ordering.

  By default, the migration will be generated to the "priv/tablestore/YOUR_REPO/migrations"
  directory of the current application but it can be configured to be any subdirectory of `priv`
  by specifying the `:priv` key under the repository configuration.

  ## Command line options

    * `-r`, `--repo` - the repo to generate migration for
  """

  @doc false
  def run(args) do
    no_umbrella!("ecto.ots.gen.migration")

    repos = parse_repo(args)

    Enum.map(repos, fn repo ->
      case OptionParser.parse!(args, strict: @switches, aliases: @aliases) do
        {opts, [name]} ->
          ensure_repo(repo, args)

          path = Path.join(source_repo_priv(repo), "migrations")

          base_name = "#{underscore(name)}.exs"

          file = Path.join(path, "#{timestamp()}_#{base_name}")
          unless File.dir?(path), do: create_directory(path)

          fuzzy_path = Path.join(path, "*_#{base_name}")

          if Path.wildcard(fuzzy_path) != [] do
            Mix.raise(
              "migration can't be created, there is already a migration file with name #{name}."
            )
          end

          assigns = [
            mod: Module.concat([repo, Migrations, camelize(name)]),
            change: opts[:change]
          ]

          create_file(file, migration_template(assigns))

          if open?(file) and Mix.shell().yes?("Do you want to run this migration?") do
            Mix.Task.run("ecto.migrate", ["-r", inspect(repo)])
          end

          file

        {_, _} ->
          Mix.raise(
            "expected ecto.ots.gen.migration to receive the migration file name, " <>
              "got: #{inspect(Enum.join(args, " "))}"
          )
      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, ?0 + i>>
  defp pad(i), do: to_string(i)

  embed_template(:migration, """
  defmodule <%= inspect @mod %> do
    use EctoTablestore.Migration

    def change do
  <%= @change %>
    end
  end
  """)
end