From 145f0219c0100988c2b181e82c6e5d964a6fdef9 Mon Sep 17 00:00:00 2001 From: Justin Wood Date: Wed, 19 Feb 2025 07:40:04 -0500 Subject: [PATCH] Support ecto config Ecto has mix tasks that will attempt to start the repo. In some of these scenarios, the application will not yet have been started. Most notable are ecto.create and ecto.migrate. These are aliased for you in a brand new phoenix project under `mix test`. With this new `Provider.ecto_config/3` function, we ensure that the given config process is running, and we will also shut it down afterwards to ensure that when your application starts, we do not have an issue with the process already being started. --- README.md | 22 ++++++++++++---------- lib/provider.ex | 14 ++++++++++++++ mix.exs | 2 +- test/provider_test.exs | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1e82af4..773da52 100644 --- a/README.md +++ b/README.md @@ -97,16 +97,18 @@ defmodule MySystem.Repo do adapter: Ecto.Adapters.Postgres @impl Ecto.Repo - def init(_type, config) do - {:ok, - Keyword.merge( - config, - hostname: MySystem.Config.db_host(), - database_name: MySystem.Config.db_name(), - pool_size: MySystem.Config.db_pool_size(), - # ... - ) - } + def init(context, config) do + Provider.ecto_config(MySystem.Config, context, fn -> + {:ok, + Keyword.merge( + config, + hostname: MySystem.Config.db_host(), + database_name: MySystem.Config.db_name(), + pool_size: MySystem.Config.db_pool_size(), + # ... + ) + } + end) end end ``` diff --git a/lib/provider.ex b/lib/provider.ex index 425469a..aaf23b9 100644 --- a/lib/provider.ex +++ b/lib/provider.ex @@ -102,6 +102,7 @@ defmodule Provider do @type type :: :string | :integer | :float | :boolean @type value :: String.t() | number | boolean | nil @type data :: %{param_name => value} + @type ecto_context :: :supervisor | :runtime # ------------------------------------------------------------------------ # API @@ -129,6 +130,19 @@ defmodule Provider do end end + @spec ecto_config(module(), ecto_context(), (-> {:ok, Keyword.t()})) :: {:ok, Keyword.t()} + def ecto_config(mod, context, fun) do + mod.start_link(nil) + + ret = fun.() + + if context == :runtime do + GenServer.stop(mod) + end + + ret + end + # ------------------------------------------------------------------------ # Private # ------------------------------------------------------------------------ diff --git a/mix.exs b/mix.exs index 9f479b7..69d82a2 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Provider.MixProject do use Mix.Project - @version "0.2.0" + @version "0.2.1" def project do [ diff --git a/test/provider_test.exs b/test/provider_test.exs index d092bff..c5e9fdb 100644 --- a/test/provider_test.exs +++ b/test/provider_test.exs @@ -37,6 +37,48 @@ defmodule ProviderTest do end end + describe "ecto_config" do + test "starts and stops the server when the context is :runtime" do + System.put_env("OPT_1", "some data") + System.put_env("OPT_2", "42") + System.put_env("OPT_6", "false") + System.put_env("OPT_7", "3.14") + + pid = Process.whereis(TestModule) + assert is_nil(pid) + + config = + Provider.ecto_config(TestModule, :runtime, fn -> + {:ok, [hostname: TestModule.opt_1()]} + end) + + assert {:ok, [hostname: "some data"]} = config + + pid = Process.whereis(TestModule) + assert is_nil(pid) + end + + test "starts but not stops the server when the context is :supervisor" do + System.put_env("OPT_1", "some data") + System.put_env("OPT_2", "42") + System.put_env("OPT_6", "false") + System.put_env("OPT_7", "3.14") + + pid = Process.whereis(TestModule) + assert is_nil(pid) + + config = + Provider.ecto_config(TestModule, :supervisor, fn -> + {:ok, [hostname: TestModule.opt_1()]} + end) + + assert {:ok, [hostname: "some data"]} = config + + pid = Process.whereis(TestModule) + assert not is_nil(pid) + end + end + describe "generated module" do setup do Enum.each(1..7, &System.delete_env("OPT_#{&1}"))