Packages
ash_postgres
2.5.8
2.10.0
2.9.1
2.9.0
2.8.0
2.7.0
2.6.32
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.12
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.22
2.5.21
2.5.20
2.5.19
2.5.18
2.5.17
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.1
2.3.0
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.19
2.1.18
2.1.17
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
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.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
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
1.5.30
1.5.29
1.5.28
1.5.27
1.5.26
1.5.25
1.5.24
1.5.23
1.5.22
1.5.21
1.5.20
1.5.19
1.5.18
1.5.17
1.5.16
1.5.15
1.5.14
1.5.13
1.5.12
1.5.11
1.5.10
1.5.9
1.5.8
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.0
1.3.68
1.3.67
1.3.66
1.3.65
1.3.64
1.3.63
1.3.62
1.3.61
1.3.60
1.3.59
1.3.58
1.3.56
1.3.55
1.3.54
1.3.53
1.3.52
1.3.51
1.3.50
1.3.49
1.3.48
1.3.47
1.3.46
1.3.45
1.3.44
1.3.43
1.3.42
1.3.41
1.3.40
1.3.39
1.3.38
1.3.37
1.3.36
1.3.35
1.3.34
1.3.33
1.3.32
1.3.31
1.3.30
1.3.29
1.3.28
1.3.26
1.3.25
1.3.24
1.3.23
1.3.22
1.3.21
1.3.20
1.3.19
1.3.18
1.3.17
1.3.16
1.3.15
1.3.14
1.3.12
1.3.11
1.3.10
1.3.9
1.3.8
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-rc.4
1.3.0-rc.3
1.3.0-rc.2
1.3.0-rc.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.1
1.2.0-rc.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.9
1.0.0-rc.8
1.0.0-rc.7
1.0.0-rc.6
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
1.0.0-pre.3
1.0.0-pre.2
1.0.0-pre.1
1.0.0-pre.0
0.43.0
0.42.0-rc.7
0.42.0-rc.6
0.42.0-rc.5
0.42.0-rc.4
0.42.0-rc.3
0.42.0-rc.1
0.42.0-rc.0
0.41.7
0.41.6
0.41.5
0.41.4
0.41.3
0.41.2
0.41.1
0.41.0-rc0
retired
0.41.0-rc.9
0.41.0-rc.8
0.41.0-rc.7
0.41.0-rc.6
0.41.0-rc.5
0.41.0-rc.4
0.41.0-rc.3
0.41.0-rc.2
0.41.0-rc.1
0.41.0-rc.0
0.40.11
0.40.10
0.40.9
0.40.8
0.40.7
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0-rc5
0.40.0-rc4
0.40.0-rc3
0.40.0-rc2
0.40.0-rc1
0.39.0-rc0
0.38.11
0.38.10
0.38.9
0.38.8
0.38.7
0.38.6
0.38.5
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.8
0.37.7
0.37.6
0.37.5
0.37.4
0.37.3
0.37.2
0.37.1
0.37.0
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.5
0.35.4
0.35.3
0.35.1
0.34.5
0.34.4
0.34.3
0.34.2
0.34.1
0.34.0
0.33.1
0.33.0
0.32.2
0.32.1
0.32.0
0.31.1
0.30.1
0.29.6
0.29.5
0.29.4
0.29.3
0.29.2
0.29.1
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.1
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.2
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.1
0.2.0
0.1.4
0.1.3
0.1.2
0.1.1
0.1.1-rc.2
0.1.1-rc.1
0.1.1-rc.0
0.1.0
The PostgreSQL data layer for Ash Framework
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/ash_postgres.install.ex
if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.AshPostgres.Install do
@moduledoc "Installs AshPostgres. Should be run with `mix igniter.install ash_postgres`"
@shortdoc @moduledoc
require Igniter.Code.Common
require Igniter.Code.Function
use Igniter.Mix.Task
@impl true
def info(_argv, _source) do
%Igniter.Mix.Task.Info{
schema: [
yes: :boolean,
repo: :string
],
aliases: [
y: :yes,
r: :repo
]
}
end
@impl true
def igniter(igniter, argv) do
opts = options!(argv)
repo =
case opts[:repo] do
nil ->
Igniter.Project.Module.module_name(igniter, "Repo")
repo ->
Igniter.Project.Module.parse(repo)
end
otp_app = Igniter.Project.Application.app_name(igniter)
igniter
|> Igniter.Project.Formatter.import_dep(:ash_postgres)
|> setup_aliases()
|> setup_repo_module(otp_app, repo, opts)
|> configure_config(otp_app, repo)
|> configure_dev(otp_app, repo)
|> configure_runtime(otp_app, repo)
|> configure_test(otp_app, repo)
|> setup_data_case()
|> Igniter.Project.Application.add_new_child(repo,
after: fn mod ->
case Module.split(mod) do
[_, "Telemetry"] -> true
_ -> false
end
end
)
|> Spark.Igniter.prepend_to_section_order(:"Ash.Resource", [:postgres])
|> Ash.Igniter.codegen("initialize")
end
defp setup_aliases(igniter) do
is_ecto_setup = &Igniter.Code.Common.nodes_equal?(&1, "ecto.setup")
is_ecto_create_or_migrate =
fn zipper ->
Igniter.Code.Common.nodes_equal?(zipper, "ecto.create --quiet") or
Igniter.Code.Common.nodes_equal?(zipper, "ecto.create") or
Igniter.Code.Common.nodes_equal?(zipper, "ecto.migrate --quiet") or
Igniter.Code.Common.nodes_equal?(zipper, "ecto.migrate")
end
igniter
|> Igniter.Project.TaskAliases.modify_existing_alias(
"test",
&Igniter.Code.List.remove_from_list(&1, is_ecto_create_or_migrate)
)
|> Igniter.Project.TaskAliases.modify_existing_alias(
"test",
&Igniter.Code.List.replace_in_list(
&1,
is_ecto_setup,
Sourceror.parse_string!("\"ash.setup\"")
)
)
|> Igniter.Project.TaskAliases.add_alias("test", ["ash.setup --quiet", "test"],
if_exists: {:prepend, "ash.setup --quiet"}
)
|> run_seeds_on_setup()
end
defp run_seeds_on_setup(igniter) do
if Igniter.exists?(igniter, "priv/repo/seeds.exs") do
igniter
|> Igniter.Project.TaskAliases.add_alias("setup", "ash.setup",
if_exists: {:replace_or_append, "ecto.setup", "ash.setup"}
)
|> Igniter.Project.TaskAliases.add_alias("setup", "run priv/repo/seeds.exs",
if_exists: :append
)
else
Igniter.Project.TaskAliases.add_alias(igniter, "setup", "ash.setup")
end
end
defp configure_config(igniter, otp_app, repo) do
Igniter.Project.Config.configure(
igniter,
"config.exs",
otp_app,
[:ecto_repos],
[repo],
updater: fn zipper ->
Igniter.Code.List.prepend_new_to_list(
zipper,
repo
)
end
)
end
defp configure_runtime(igniter, otp_app, repo) do
default_runtime = """
import Config
if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
raise \"\"\"
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
\"\"\"
config #{inspect(otp_app)}, #{inspect(repo)},
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end
"""
igniter
|> Igniter.create_or_update_elixir_file("config/runtime.exs", default_runtime, fn zipper ->
if Igniter.Project.Config.configures_key?(zipper, otp_app, [repo, :url]) do
zipper
else
patterns = [
"""
if config_env() == :prod do
__cursor__()
end
""",
"""
if :prod == config_env() do
__cursor__()
end
"""
]
zipper
|> Igniter.Code.Common.move_to_cursor_match_in_scope(patterns)
|> case do
{:ok, zipper} ->
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:=,
2,
fn call ->
Igniter.Code.Function.argument_matches_pattern?(
call,
0,
{:database_url, _, ctx} when is_atom(ctx)
)
end
) do
{:ok, _zipper} ->
zipper
|> Igniter.Project.Config.modify_configuration_code(
[repo, :url],
otp_app,
{:database_url, [], nil}
)
|> Igniter.Project.Config.modify_configuration_code(
[repo, :pool_size],
otp_app,
Sourceror.parse_string!("""
String.to_integer(System.get_env("POOL_SIZE") || "10")
""")
)
|> then(&{:ok, &1})
_ ->
Igniter.Code.Common.add_code(zipper, """
database_url =
System.get_env("DATABASE_URL") ||
raise \"\"\"
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
\"\"\"
config #{inspect(otp_app)}, Helpdesk.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
""")
end
:error ->
Igniter.Code.Common.add_code(zipper, """
if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
raise \"\"\"
environment variable DATABASE_URL is missing.
For example: ecto://USER:PASS@HOST/DATABASE
\"\"\"
config #{inspect(otp_app)}, Helpdesk.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end
""")
end
end
end)
end
defp configure_dev(igniter, otp_app, repo) do
igniter
|> Igniter.Project.Config.configure_new("dev.exs", otp_app, [repo, :username], "postgres")
|> Igniter.Project.Config.configure_new("dev.exs", otp_app, [repo, :password], "postgres")
|> Igniter.Project.Config.configure_new("dev.exs", otp_app, [repo, :hostname], "localhost")
|> Igniter.Project.Config.configure_new(
"dev.exs",
otp_app,
[repo, :database],
"#{otp_app}_dev"
)
|> Igniter.Project.Config.configure_new(
"dev.exs",
otp_app,
[repo, :show_sensitive_data_on_connection_error],
true
)
|> Igniter.Project.Config.configure_new("dev.exs", otp_app, [repo, :pool_size], 10)
end
defp configure_test(igniter, otp_app, repo) do
database =
{:<<>>, [],
[
"#{otp_app}_test",
{:"::", [],
[
{{:., [], [Kernel, :to_string]}, [from_interpolation: true],
[
{{:., [], [{:__aliases__, [alias: false], [:System]}, :get_env]}, [],
["MIX_TEST_PARTITION"]}
]},
{:binary, [], Elixir}
]}
]}
|> Sourceror.to_string()
|> Sourceror.parse_string!()
igniter
|> Igniter.Project.Config.configure_new("test.exs", otp_app, [repo, :username], "postgres")
|> Igniter.Project.Config.configure_new("test.exs", otp_app, [repo, :password], "postgres")
|> Igniter.Project.Config.configure_new("test.exs", otp_app, [repo, :hostname], "localhost")
|> Igniter.Project.Config.configure_new(
"test.exs",
otp_app,
[repo, :database],
{:code, database}
)
|> Igniter.Project.Config.configure_new(
"test.exs",
otp_app,
[repo, :pool],
Ecto.Adapters.SQL.Sandbox
)
|> Igniter.Project.Config.configure_new("test.exs", otp_app, [repo, :pool_size], 10)
|> Igniter.Project.Config.configure_new("test.exs", :ash, [:disable_async?], true)
|> Igniter.Project.Config.configure_new("test.exs", :logger, [:level], :warning)
end
defp setup_data_case(igniter) do
module_name = Igniter.Project.Module.module_name(igniter, "DataCase")
default_data_case_contents = ~s|
@moduledoc """
This module defines the setup for tests requiring
access to the application's data layer.
You may define functions here to be used as helpers in
your tests.
Finally, if the test case interacts with the database,
we enable the SQL sandbox, so changes done to the database
are reverted at the end of every test. If you are using
PostgreSQL, you can even run database tests asynchronously
by setting `use #{inspect(module_name)}, async: true`, although
this option is not recommended for other databases.
"""
use ExUnit.CaseTemplate
using do
quote do
alias #{inspect(Igniter.Project.Module.module_name(igniter, "Repo"))}
import Ecto
import Ecto.Changeset
import Ecto.Query
import #{inspect(Igniter.Project.Module.module_name(igniter, "DataCase"))}
end
end
setup tags do
pid = Ecto.Adapters.SQL.Sandbox.start_owner!(#{inspect(Igniter.Project.Module.module_name(igniter, "Repo"))}, shared: not tags[:async])
on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
:ok
end
|
igniter
|> Igniter.Project.Module.find_and_update_or_create_module(
module_name,
default_data_case_contents,
# do nothing if already exists
fn zipper -> {:ok, zipper} end,
path: Igniter.Project.Module.proper_location(igniter, module_name, :test_support)
)
end
defp setup_repo_module(igniter, otp_app, repo, opts) do
{exists?, igniter} = Igniter.Project.Module.module_exists(igniter, repo)
if exists? do
Igniter.Project.Module.find_and_update_module!(
igniter,
repo,
fn zipper ->
case Igniter.Code.Module.move_to_use(zipper, Ecto.Repo) do
{:ok, _} ->
{:ok,
zipper
|> set_otp_app(otp_app)
|> Sourceror.Zipper.top()
|> use_ash_postgres_instead_of_ecto()
|> Sourceror.Zipper.top()
|> remove_adapter_option()}
_ ->
case Igniter.Code.Module.move_to_use(zipper, AshPostgres.Repo) do
{:ok, _} ->
{:ok, zipper}
_ ->
{:error,
"""
Repo module #{inspect(repo)} existed, but was not an `Ecto.Repo` or an `AshPostgres.Repo`.
Please re-run the AshPostgres installer with the `--repo` option to specify a repo.
"""}
end
end
end
)
else
{min_pg_version, notice} = min_pg_version_and_notice(repo)
Igniter.Project.Module.create_module(
igniter,
repo,
AshPostgres.Igniter.default_repo_contents(
otp_app,
Keyword.put(opts, :min_pg_version, min_pg_version)
)
)
|> Igniter.add_notice(notice)
end
|> Igniter.Project.Module.find_and_update_module!(
repo,
&configure_installed_extensions_function/1
)
|> Igniter.Project.Module.find_and_update_module!(
repo,
&configure_prefer_transaction_function/1
)
|> then(fn igniter ->
{min_pg_version, notice} = min_pg_version_and_notice(repo)
igniter
|> Igniter.Project.Module.find_and_update_module!(
repo,
&configure_min_pg_version_function(
&1,
repo,
min_pg_version || Version.parse!("16.0.0"),
opts
)
)
|> Igniter.add_notice(notice)
end)
end
defp min_pg_version_and_notice(repo) do
min_pg_version = get_min_pg_version()
notice =
if min_pg_version do
"""
A `min_pg_version/0` function has been defined
in `#{inspect(repo)}` as `#{min_pg_version}`.
This was based on running `postgres -V`.
You may wish to update this configuration. It should
be set to the lowest version that your application
expects to be run against.
"""
else
"""
A `min_pg_version/0` function has been defined in
`#{inspect(repo)}` automatically.
You may wish to update this configuration. It should
be set to the lowest version that your application
expects to be run against.
"""
end
{min_pg_version, notice}
end
defp get_min_pg_version do
case System.cmd("postgres", ["-V"]) do
{"postgres (PostgreSQL) " <> version_and_text, 0} ->
version_and_text
|> String.split(~r/\s+/, parts: 2, trim: true)
|> Enum.at(0)
|> String.split(".", trim: true)
|> case do
[major, minor, patch | _] -> Version.parse!("#{major}.#{minor}.#{patch}")
[major, minor] -> Version.parse!("#{major}.#{minor}.0")
[major] -> Version.parse!("#{major}.0.0")
_ -> nil
end
_ ->
nil
end
rescue
_ ->
nil
end
defp use_ash_postgres_instead_of_ecto(zipper) do
with {:ok, zipper} <- Igniter.Code.Module.move_to_module_using(zipper, Ecto.Repo),
{:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, Ecto.Repo),
{:ok, zipper} <-
Igniter.Code.Function.update_nth_argument(zipper, 0, fn zipper ->
{:ok, Igniter.Code.Common.replace_code(zipper, AshPostgres.Repo)}
end) do
zipper
else
_ ->
zipper
end
end
defp remove_adapter_option(zipper) do
with {:ok, zipper} <- Igniter.Code.Module.move_to_module_using(zipper, AshPostgres.Repo),
{:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, AshPostgres.Repo),
{:ok, zipper} <-
Igniter.Code.Function.update_nth_argument(zipper, 1, fn values_zipper ->
Igniter.Code.Keyword.remove_keyword_key(values_zipper, :adapter)
end) do
zipper
else
_ ->
zipper
end
end
defp set_otp_app(zipper, otp_app) do
with {:ok, zipper} <- Igniter.Code.Module.move_to_module_using(zipper, AshPostgres.Repo),
{:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, AshPostgres.Repo),
{:ok, zipper} <-
Igniter.Code.Function.update_nth_argument(zipper, 0, fn zipper ->
{:ok, Igniter.Code.Common.replace_code(zipper, AshPostgres.Repo)}
end),
{:ok, zipper} <-
Igniter.Code.Function.update_nth_argument(zipper, 1, fn values_zipper ->
values_zipper
|> Igniter.Code.Keyword.set_keyword_key(:otp_app, otp_app, fn x -> {:ok, x} end)
end) do
zipper
else
_ ->
zipper
end
end
defp configure_installed_extensions_function(zipper) do
case Igniter.Code.Function.move_to_def(zipper, :installed_extensions, 0) do
{:ok, zipper} ->
case Igniter.Code.Common.move_right(zipper, &Igniter.Code.List.list?/1) do
{:ok, zipper} ->
Igniter.Code.List.append_new_to_list(zipper, "ash-functions")
:error ->
{:error, "installed_extensions/0 doesn't return a list"}
end
_ ->
{:ok,
Igniter.Code.Common.add_code(zipper, """
def installed_extensions do
# Add extensions here, and the migration generator will install them.
["ash-functions"]
end
""")}
end
end
defp configure_prefer_transaction_function(zipper) do
case Igniter.Code.Function.move_to_def(zipper, :prefer_transaction?, 0) do
{:ok, zipper} ->
{:ok, zipper}
_ ->
{:ok,
Igniter.Code.Common.add_code(zipper, """
# Don't open unnecessary transactions
# will default to `false` in 4.0
def prefer_transaction? do
false
end
""")}
end
end
defp configure_min_pg_version_function(zipper, _repo, version, _opts) do
case Igniter.Code.Function.move_to_def(zipper, :min_pg_version, 0) do
{:ok, zipper} ->
{:ok, zipper}
_ ->
{:ok,
Igniter.Code.Common.add_code(zipper, """
def min_pg_version do
%Version{major: #{version.major}, minor: #{version.minor}, patch: #{version.patch}}
end
""")}
end
end
end
else
defmodule Mix.Tasks.AshPostgres.Install do
@moduledoc "Installs AshPostgres into a project. Should be called with `mix igniter.install ash_postgres`"
@shortdoc @moduledoc
use Mix.Task
def run(_argv) do
Mix.shell().error("""
The task 'ash_postgres.install' requires igniter to be run.
Please install igniter and try again.
For more information, see: https://hexdocs.pm/igniter
""")
exit({:shutdown, 1})
end
end
end