Packages
ecto
0.2.2
3.14.1
3.14.0
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.1
3.13.0
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.2
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.4
3.8.3
3.8.2
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.2
3.6.1
3.6.0
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.1
3.0.0-rc.0
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.2.0-rc.1
2.2.0-rc.0
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.1.0-rc.5
2.1.0-rc.4
2.1.0-rc.3
2.1.0-rc.2
2.1.0-rc.1
2.1.0-rc.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-beta.2
2.0.0-beta.1
2.0.0-beta.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.12.0-rc
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
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.0
A toolkit for data mapping and language integrated query for Elixir
Current section
Files
Jump to
Current section
Files
lib/ecto/migrator.ex
defmodule Ecto.Migrator do
@moduledoc """
This module provides the migration API.
## Example
defmodule MyApp.MigrationExample do
use Ecto.Migration
def up do
"CREATE TABLE users(id serial PRIMARY_KEY, username text)"
end
def down do
"DROP TABLE users"
end
end
Ecto.Migrator.up(Repo, 20080906120000, MyApp.MigrationExample)
"""
@type strategy :: [all: true, to: non_neg_integer, step: non_neg_integer]
@doc """
Runs an up migration on the given repository.
"""
@spec up(Ecto.Repo.t, integer, Module.t) :: :ok | :already_up | no_return
def up(repo, version, module) do
commands = List.wrap(module.up)
repo.adapter.migrate_up(repo, version, commands)
end
@doc """
Runs a down migration on the given repository.
"""
@spec down(Ecto.Repo.t, integer, Module.t) :: :ok | :missing_up | no_return
def down(repo, version, module) do
commands = List.wrap(module.down)
repo.adapter.migrate_down(repo, version, commands)
end
@doc """
Apply migrations in a directory to a repository with given strategy.
A strategy must be pass as an option. The available strategy types are:
* `:all` runs all available if `true`
* `:step` runs the specific number of migrations
* `:to` runs all until the supplied version is reached
"""
@spec run(Ecto.Repo.t, binary, atom, strategy) :: [integer]
def run(repo, directory, direction, opts) do
cond do
opts[:all] ->
run_all(repo, directory, direction)
to = opts[:to] ->
run_to(repo, directory, direction, to)
step = opts[:step] ->
run_step(repo, directory, direction, step)
true ->
raise ArgumentError, message: "expected one of :all, :to, or :step strategies"
end
end
defp run_to(repo, directory, direction, target) do
within_target_version? = fn
{version, _}, target, :up ->
version <= target
{version, _}, target, :down ->
version >= target
end
pending_in_direction(repo, directory, direction)
|> Enum.take_while(&(within_target_version?.(&1, target, direction)))
|> migrate(direction, repo)
end
defp run_step(repo, directory, direction, count) do
pending_in_direction(repo, directory, direction)
|> Enum.take(count)
|> migrate(direction, repo)
end
defp run_all(repo, directory, direction) do
pending_in_direction(repo, directory, direction)
|> migrate(direction, repo)
end
defp pending_in_direction(repo, directory, :up) do
versions = repo.adapter.migrated_versions(repo)
migrations_for(directory) |>
Enum.filter(fn {version, _file} ->
not (version in versions)
end)
end
defp pending_in_direction(repo, directory, :down) do
versions = repo.adapter.migrated_versions(repo)
migrations_for(directory) |>
Enum.filter(fn {version, _file} ->
version in versions
end)
|> :lists.reverse
end
defp migrations_for(directory) do
Path.join(directory, "*")
|> Path.wildcard
|> Enum.filter(&Regex.match?(~r"\d+_.+\.exs$", &1))
|> attach_versions
end
defp attach_versions(files) do
Enum.map(files, fn(file) ->
{integer, _} = Integer.parse(Path.basename(file))
{integer, file}
end)
end
defp migrate(migrations, direction, repo) do
ensure_no_duplication(migrations)
Enum.map migrations, fn {version, file} ->
{mod, _bin} =
Enum.find(Code.load_file(file), fn {mod, _bin} ->
function_exported?(mod, :__migration__, 0)
end) || raise_no_migration_in_file(file)
# TODO: Use logger in the future
file = Path.relative_to_cwd(file)
case direction do
:up ->
IO.puts IO.ANSI.escape("%{green}* running %{yellow}UP %{reset}#{file}")
up(repo, version, mod)
:down ->
IO.puts IO.ANSI.escape("%{green}* running %{yellow}DOWN %{reset}#{file}")
down(repo, version, mod)
end
version
end
end
defp ensure_no_duplication([{version, _} | t]) do
if List.keyfind(t, version, 0) do
raise Ecto.MigrationError, message: "migrations can't be executed, version #{version} is duplicated"
else
ensure_no_duplication(t)
end
end
defp ensure_no_duplication([]), do: :ok
defp raise_no_migration_in_file(file) do
raise Ecto.MigrationError, message: "file #{Path.relative_to_cwd(file)} does not contain any Ecto.Migration"
end
end