Current section

Files

Jump to
ecto lib mix ecto.ex
Raw

lib/mix/ecto.ex

defmodule Mix.Ecto do
# Conveniences for writing Mix.Tasks in Ecto.
@moduledoc false
@doc """
Parses the repository option from the given list.
If no repo option is given, we get one from the environment.
"""
@spec parse_repo([term]) :: [Ecto.Repo.t]
def parse_repo(args) do
parse_repo(args, [])
end
defp parse_repo([key, value|t], acc) when key in ~w(--repo -r) do
parse_repo t, [Module.concat([value])|acc]
end
defp parse_repo([_|t], acc) do
parse_repo t, acc
end
defp parse_repo([], []) do
app = Mix.Project.config[:app]
cond do
repos = Application.get_env(app, :ecto_repos) ->
repos
Map.has_key?(Mix.Project.deps_paths, :ecto) ->
Mix.shell.error """
warning: could not find repositories for application #{inspect app}.
You can avoid this warning by passing the -r flag or by setting the
repositories managed by this application in your config/config.exs:
config #{inspect app}, ecto_repos: [...]
The configuration may be an empty list if it does not define any repo.
"""
[]
true ->
[]
end
end
defp parse_repo([], acc) do
Enum.reverse(acc)
end
@doc """
Ensures the given module is a repository.
"""
@spec ensure_repo(module, list) :: Ecto.Repo.t | no_return
def ensure_repo(repo, args) do
Mix.Task.run "loadpaths", args
unless "--no-compile" in args do
Mix.Project.compile(args)
end
case Code.ensure_compiled(repo) do
{:module, _} ->
if function_exported?(repo, :__adapter__, 0) do
repo
else
Mix.raise "Module #{inspect repo} is not an Ecto.Repo. " <>
"Please configure your app accordingly or pass a repo with the -r option."
end
{:error, error} ->
Mix.raise "Could not load #{inspect repo}, error: #{inspect error}. " <>
"Please configure your app accordingly or pass a repo with the -r option."
end
end
@doc """
Ensures the given repository is started and running.
"""
@spec ensure_started(Ecto.Repo.t, Keyword.t) :: {:ok, pid, [atom]} | no_return
def ensure_started(repo, opts) do
{:ok, _} = Application.ensure_all_started(:ecto)
{:ok, apps} = repo.__adapter__.ensure_all_started(repo, :temporary)
pool_size = Keyword.get(opts, :pool_size, 1)
case repo.start_link(pool_size: pool_size) do
{:ok, pid} ->
{:ok, pid, apps}
{:error, {:already_started, _pid}} ->
{:ok, nil, apps}
{:error, error} ->
Mix.raise "Could not start repo #{inspect repo}, error: #{inspect error}"
end
end
@doc """
Ensures the given repository's migrations path exists on the file system.
"""
@spec ensure_migrations_path(Ecto.Repo.t) :: Ecto.Repo.t | no_return
def ensure_migrations_path(repo) do
with false <- Mix.Project.umbrella?,
path = Path.relative_to(migrations_path(repo), Mix.Project.app_path),
false <- File.dir?(path),
do: raise_missing_migrations(path, repo)
repo
end
defp raise_missing_migrations(path, repo) do
Mix.raise """
Could not find migrations directory #{inspect path}
for repo #{inspect repo}.
This may be because you are in a new project and the
migration directory has not been created yet. Creating an
empty directory at the path above will fix this error.
If you expected existing migrations to be found, please
make sure your repository has been properly configured
and the configured path exists.
"""
end
@doc """
Restarts the app if there was any migration command.
"""
@spec restart_apps_if_migrated([atom], list()) :: :ok
def restart_apps_if_migrated(_apps, []), do: :ok
def restart_apps_if_migrated(apps, [_|_]) do
# Silence the logger to avoid application down messages.
Logger.remove_backend(:console)
for app <- Enum.reverse(apps) do
Application.stop(app)
end
for app <- apps do
Application.ensure_all_started(app)
end
:ok
after
Logger.add_backend(:console, flush: true)
end
@doc """
Gets the migrations path from a repository.
"""
@spec migrations_path(Ecto.Repo.t) :: String.t
def migrations_path(repo) do
Path.join(build_repo_priv(repo), "migrations")
end
@doc """
Returns the private repository path relative to the source.
"""
def source_repo_priv(repo) do
repo.config()[:priv] || "priv/#{repo |> Module.split |> List.last |> Macro.underscore}"
end
@doc """
Returns the private repository path inside build.
"""
def build_repo_priv(repo) do
Application.app_dir(Keyword.fetch!(repo.config(), :otp_app), source_repo_priv(repo))
end
@doc """
Asks if the user wants to open a file based on ECTO_EDITOR.
"""
@spec open?(binary) :: boolean
def open?(file) do
editor = System.get_env("ECTO_EDITOR") || ""
if editor != "" do
:os.cmd(to_charlist(editor <> " " <> inspect(file)))
true
else
false
end
end
@doc """
Gets a path relative to the application path.
Raises on umbrella application.
"""
def no_umbrella!(task) do
if Mix.Project.umbrella? do
Mix.raise "Cannot run task #{inspect task} from umbrella application"
end
end
@doc """
Returns `true` if module implements behaviour.
"""
def ensure_implements(module, behaviour, message) do
all = Keyword.take(module.__info__(:attributes), [:behaviour])
unless [behaviour] in Keyword.values(all) do
Mix.raise "Expected #{inspect module} to implement #{inspect behaviour} " <>
"in order to #{message}"
end
end
end