Packages
igniter
0.5.18
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/igniter/libs/ecto.ex
defmodule Igniter.Libs.Ecto do
@moduledoc "Codemods & utilities for working with Ecto"
import Macro, only: [camelize: 1, underscore: 1]
@known_repos [
Ecto.Repo,
AshPostgres.Repo,
AshSqlite.Repo,
AshMysql.Repo
]
@doc """
Generates a new migration file for the given repo.
## Options
- `:body` - the body of the migration
- `:timestamp` - the timestamp to use for the migration.
Primarily useful for testing so you know what the filename will be.
- `:on_exists` - what to do if the migration *module* already exists. Options are:
- `:increment` - Calls this function again, but with an increasing number at the end, until it finds a free name. (default)
- `:skip` - do nothing
- `:overwrite` - overwrites the file
- `{:error, error}` - adds an issue to the igniter that prevents writing and displays to the user
- `{:warning, warning}` - adds a warning to the igniter that allows writing but displays to the user
"""
@spec gen_migration(Igniter.t(), repo :: module(), name :: String.t(), opts :: Keyword.t()) ::
Igniter.t()
def gen_migration(igniter, repo, name, opts \\ []) do
# getting the repos configuration will be a bit harder here
# we'd need to look in config files or in the repos init or in the repo function?
# not worth it for now
path =
Path.join(
"priv/#{repo |> Module.split() |> List.last() |> Macro.underscore()}",
"migrations"
)
base_name = "#{underscore(name)}.exs"
file = Path.join(path, "#{opts[:timestamp] || timestamp()}_#{base_name}")
igniter = Igniter.include_glob(igniter, Path.join(path, "**/*.exs"))
body =
opts[:body] ||
"""
def change do
# your migration here
end
"""
module = Module.concat([repo, Migrations, camelize(name)])
case Igniter.Project.Module.module_exists(igniter, module) do
{true, igniter} ->
case Keyword.get(opts, :on_exists, :increment) do
:skip ->
igniter
:increment ->
name
|> String.split("_", trim: true)
|> List.last()
|> Integer.parse()
|> case do
{integer, ""} when is_integer(integer) ->
gen_migration(igniter, repo, name <> "_#{integer + 1}", opts)
_ ->
gen_migration(igniter, repo, name <> "_1", opts)
end
:overwrite ->
Igniter.create_new_file(
igniter,
file,
"""
defmodule #{inspect(module)} do
use Ecto.Migration
#{body}
end
""",
on_exists: :overwrite
)
{:error, error} ->
Igniter.add_issue(igniter, error)
{:warning, error} ->
Igniter.add_warning(igniter, error)
end
{false, igniter} ->
Igniter.create_new_file(igniter, file, """
defmodule #{inspect(module)} do
use Ecto.Migration
#{body}
end
""")
end
end
@doc """
Selects a repo module from the list of available repos.
## Options
* `:label` - The label to display to the user when selecting the repo
"""
@spec select_repo(Igniter.t(), Keyword.t()) :: {Igniter.t(), nil | module()}
def select_repo(igniter, opts \\ []) do
label = Keyword.get(opts, :label, "Which repo should be used?")
case list_repos(igniter) do
{igniter, []} ->
{igniter, nil}
{igniter, [repo]} ->
{igniter, repo}
{igniter, repos} ->
{igniter, Igniter.Util.IO.select(label, repos, display: &inspect/1)}
end
end
@doc "Lists all the ecto repos in the project"
@spec list_repos(Igniter.t()) :: {Igniter.t(), [module()]}
def list_repos(igniter) do
Igniter.Project.Module.find_all_matching_modules(igniter, fn _mod, zipper ->
move_to_repo_use(zipper) != :error
end)
end
defp move_to_repo_use(zipper) do
Igniter.Code.Function.move_to_function_call(zipper, :use, [1, 2], fn zipper ->
Enum.any?(@known_repos, fn repo ->
Igniter.Code.Function.argument_equals?(
zipper,
0,
repo
)
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)
end