radzserg
radzserg

Reputation: 1396

Ecto 3 - Start Repo in custom CLI task

If you need access to DB for you mix task - you needed to start your ecto repo manually. While we worked with ecto 2 we had ensure_started function.

So custom CLI task with ecto 2 looks like

defmodule App.Task do
  use Mix.Task
  import Mix.Ecto

  def run(args) do
    repos = parse_repo(args)

    Enum.each repos, fn repo ->
      ensure_repo(repo, args)
      ensure_started(repo, []) # was deleted from ecto 3

ensure_started has been deleted from ecto 3. I tried a few approaches but they didn't work for me. Does anybody know a workaround for ecto 3?

Updated

as @starbelly mentioned ensure_started has migrated to Mix.EctoSQL so we need to add extra import Mix.EctoSQL to make it work

defmodule App.Cli.Task do
  import Mix.Ecto
  import Mix.EctoSQL

  def start_ecto do
    repos = parse_repo([])

    Enum.each(repos, fn repo ->
      ensure_repo(repo, [])
      {:ok, _pid, _apps} = ensure_started(repo, [])
    end)
  end
end

Upvotes: 4

Views: 2209

Answers (4)

Tahseen Siddiqui
Tahseen Siddiqui

Reputation: 21

For Ecto 3.x use Repo.start_link() or Mix.Task.run('app.start')

This question/answer might help. How to get data from Ecto in a custom mix task

Upvotes: 1

wiser
wiser

Reputation: 179

As Dennis pointed out Mix.EctoSQL.ensure_started/2 is a private function since ecto_sql 3.1.2, so we can't use it anymore.

Assuming you have an OTP application defined, which this mix task is part of, what you could do is

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task

  def run(args) do
    {:ok, _started} = Application.ensure_all_started(:your_otp_app_name)
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      repo.all(Friends.Person)
    end)
  end
end

Essentially start the whole OTP application, so that all the dependencies (including the repos) would be started as well.

Upvotes: 2

Dennis Beatty
Dennis Beatty

Reputation: 96

None of these approaches will work anymore in the latest version of ecto_sql (3.1.2) because Mix.EctoSQL.ensure_started/2 was a private function, not meant for use outside of Ecto.

As per Jose, the correct approach is to use

Mix.Task.run("app.start")

So the example @starbelly shared would be modified to be

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task

  def run(args) do
    Mix.Task.run("app.start")
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      repo.all(Friends.Person)
    end)
  end
end

The big drawback to this approach is it will start your entire application, including any background processes you have. As a result, you may need to modify your application.ex file to not start those if started by a mix task.

Upvotes: 3

starbelly
starbelly

Reputation: 254

The function you're trying to import and call has been moved into ecto_sql. You should import Mix.EctoSQL for ensure_started/2

Example:

defmodule Mix.Tasks.Friends.Hello do
  use Mix.Task
  import Mix.Ecto
  import Mix.EctoSQL

  def run(args) do
    repos = parse_repo(args)

    Enum.each(repos, fn repo ->
      ensure_repo(repo, args)
      {:ok, _pid, _apps} = ensure_started(repo, [])
      Friends.Repo.all(Friends.Person)
    end)
  end
end

Upvotes: 3

Related Questions