Skip to content

Commit

Permalink
improvement: make igniter optional
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Dec 19, 2024
1 parent db941cc commit a5e3eb7
Show file tree
Hide file tree
Showing 8 changed files with 1,304 additions and 1,243 deletions.
46 changes: 23 additions & 23 deletions lib/data_layer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,6 @@ defmodule AshPostgres.DataLayer do
A postgres data layer that leverages Ecto's postgres capabilities.
"""

require Igniter.Code.Common

use Spark.Dsl.Extension,
sections: @sections,
verifiers: [
Expand Down Expand Up @@ -1966,7 +1964,7 @@ defmodule AshPostgres.DataLayer do

fields_to_upsert =
upsert_fields --
Keyword.keys(Enum.at(changesets, 0).atomics) -- keys
(Keyword.keys(Enum.at(changesets, 0).atomics) -- keys)

fields_to_upsert
|> Enum.uniq()
Expand Down Expand Up @@ -3067,31 +3065,33 @@ defmodule AshPostgres.DataLayer do
end
end

def install(igniter, module, Ash.Resource, _path, argv) do
table_name =
module
|> Module.split()
|> List.last()
|> Macro.underscore()
|> Inflex.pluralize()
if Code.ensure_loaded?(Igniter) do
def install(igniter, module, Ash.Resource, _path, argv) do
table_name =
module
|> Module.split()
|> List.last()
|> Macro.underscore()
|> Inflex.pluralize()

{options, _, _} = OptionParser.parse(argv, switches: [repo: :string])
{options, _, _} = OptionParser.parse(argv, switches: [repo: :string])

repo =
case options[:repo] do
nil ->
Igniter.Project.Module.module_name(igniter, "Repo")
repo =
case options[:repo] do
nil ->
Igniter.Project.Module.module_name(igniter, "Repo")

repo ->
Igniter.Project.Module.parse(repo)
end
repo ->
Igniter.Project.Module.parse(repo)
end

igniter
|> Spark.Igniter.set_option(module, [:postgres, :table], table_name)
|> Spark.Igniter.set_option(module, [:postgres, :repo], repo)
end
igniter
|> Spark.Igniter.set_option(module, [:postgres, :table], table_name)
|> Spark.Igniter.set_option(module, [:postgres, :repo], repo)
end

def install(igniter, _, _, _), do: igniter
def install(igniter, _, _, _), do: igniter
end

@impl true
def rollback(resource, term) do
Expand Down
276 changes: 139 additions & 137 deletions lib/igniter.ex
Original file line number Diff line number Diff line change
@@ -1,170 +1,172 @@
defmodule AshPostgres.Igniter do
@moduledoc "Codemods and utilities for working with AshPostgres & Igniter"
if Code.ensure_loaded?(Igniter) do
defmodule AshPostgres.Igniter do
@moduledoc "Codemods and utilities for working with AshPostgres & Igniter"

@doc false
def default_repo_contents(otp_app, name, opts \\ []) do
min_pg_version = get_min_pg_version(name, opts)
@doc false
def default_repo_contents(otp_app, name, opts \\ []) do
min_pg_version = get_min_pg_version(name, opts)

"""
use AshPostgres.Repo, otp_app: #{inspect(otp_app)}
"""
use AshPostgres.Repo, otp_app: #{inspect(otp_app)}
def min_pg_version do
%Version{major: #{min_pg_version.major}, minor: #{min_pg_version.minor}, patch: #{min_pg_version.patch}}
end
def min_pg_version do
%Version{major: #{min_pg_version.major}, minor: #{min_pg_version.minor}, patch: #{min_pg_version.patch}}
end
# Don't open unnecessary transactions
# will default to `false` in 4.0
def prefer_transaction? do
false
end
# Don't open unnecessary transactions
# will default to `false` in 4.0
def prefer_transaction? do
false
end
def installed_extensions do
# Add extensions here, and the migration generator will install them.
["ash-functions"]
def installed_extensions do
# Add extensions here, and the migration generator will install them.
["ash-functions"]
end
"""
end
"""
end

def table(igniter, resource) do
igniter
|> Spark.Igniter.get_option(resource, [:postgres, :table])
|> case do
{igniter, {:ok, value}} when is_binary(value) or is_nil(value) ->
{:ok, igniter, value}
def table(igniter, resource) do
igniter
|> Spark.Igniter.get_option(resource, [:postgres, :table])
|> case do
{igniter, {:ok, value}} when is_binary(value) or is_nil(value) ->
{:ok, igniter, value}

{igniter, _} ->
{:error, igniter}
{igniter, _} ->
{:error, igniter}
end
end
end

def repo(igniter, resource) do
igniter
|> Spark.Igniter.get_option(resource, [:postgres, :repo])
|> case do
{igniter, {:ok, value}} when is_atom(value) ->
{:ok, igniter, value}
def repo(igniter, resource) do
igniter
|> Spark.Igniter.get_option(resource, [:postgres, :repo])
|> case do
{igniter, {:ok, value}} when is_atom(value) ->
{:ok, igniter, value}

{igniter, _} ->
{:error, igniter}
{igniter, _} ->
{:error, igniter}
end
end
end

def add_postgres_extension(igniter, repo_name, extension) do
Igniter.Project.Module.find_and_update_module!(igniter, repo_name, fn zipper ->
case Igniter.Code.Function.move_to_def(zipper, :installed_extensions, 0) do
{:ok, zipper} ->
case Igniter.Code.List.append_new_to_list(zipper, extension) do
{:ok, zipper} ->
{:ok, zipper}

_ ->
{:warning,
"Could not add installed extension #{inspect(extension)} to #{inspect(repo_name)}.installed_extensions/0"}
end

_ ->
zipper = Sourceror.Zipper.rightmost(zipper)
def add_postgres_extension(igniter, repo_name, extension) do
Igniter.Project.Module.find_and_update_module!(igniter, repo_name, fn zipper ->
case Igniter.Code.Function.move_to_def(zipper, :installed_extensions, 0) do
{:ok, zipper} ->
case Igniter.Code.List.append_new_to_list(zipper, extension) do
{:ok, zipper} ->
{:ok, zipper}

_ ->
{:warning,
"Could not add installed extension #{inspect(extension)} to #{inspect(repo_name)}.installed_extensions/0"}
end

_ ->
zipper = Sourceror.Zipper.rightmost(zipper)

code = """
def installed_extensions do
[#{inspect(extension)}]
end
"""

{:ok, Igniter.Code.Common.add_code(zipper, code)}
end
end)
end

code = """
def installed_extensions do
[#{inspect(extension)}]
def select_repo(igniter, opts \\ []) do
label = Keyword.get(opts, :label, "Which repo should be used?")
generate = Keyword.get(opts, :generate?, false)

case list_repos(igniter) do
{igniter, []} ->
if generate do
repo = Igniter.Project.Module.module_name(igniter, "Repo")
otp_app = Igniter.Project.Application.app_name(igniter)

igniter =
Igniter.Project.Module.create_module(
igniter,
repo,
default_repo_contents(otp_app, repo, opts),
opts
)

{igniter, repo}
else
{igniter, nil}
end
"""

{:ok, Igniter.Code.Common.add_code(zipper, code)}
end
end)
end

def select_repo(igniter, opts \\ []) do
label = Keyword.get(opts, :label, "Which repo should be used?")
generate = Keyword.get(opts, :generate?, false)

case list_repos(igniter) do
{igniter, []} ->
if generate do
repo = Igniter.Project.Module.module_name(igniter, "Repo")
otp_app = Igniter.Project.Application.app_name(igniter)

igniter =
Igniter.Project.Module.create_module(
igniter,
repo,
default_repo_contents(otp_app, repo, opts),
opts
)

{igniter, [repo]} ->
{igniter, repo}
else
{igniter, nil}
end

{igniter, [repo]} ->
{igniter, repo}
{igniter, repos} ->
{igniter, Owl.IO.select(repos, label: label, render_as: &inspect/1)}
end
end

{igniter, repos} ->
{igniter, Owl.IO.select(repos, label: label, render_as: &inspect/1)}
def list_repos(igniter) do
Igniter.Project.Module.find_all_matching_modules(igniter, fn _mod, zipper ->
move_to_repo_use(zipper) != :error
end)
end
end

def list_repos(igniter) do
Igniter.Project.Module.find_all_matching_modules(igniter, fn _mod, zipper ->
move_to_repo_use(zipper) != :error
end)
end
defp move_to_repo_use(zipper) do
Igniter.Code.Function.move_to_function_call(zipper, :use, [1, 2], fn zipper ->
Igniter.Code.Function.argument_equals?(
zipper,
0,
AshPostgres.Repo
)
end)
end

defp move_to_repo_use(zipper) do
Igniter.Code.Function.move_to_function_call(zipper, :use, [1, 2], fn zipper ->
Igniter.Code.Function.argument_equals?(
zipper,
0,
AshPostgres.Repo
)
end)
end
@doc false
def get_min_pg_version(name, opts) do
if opts[:yes] do
%Version{major: 13, minor: 0, patch: 0}
else
lead_in = """
Generating #{inspect(name)}
@doc false
def get_min_pg_version(name, opts) do
if opts[:yes] do
%Version{major: 13, minor: 0, patch: 0}
else
lead_in = """
Generating #{inspect(name)}
What is the minimum PostgreSQL version you will be using?
What is the minimum PostgreSQL version you will be using?
AshPostgres uses this information when generating queries and migrations,
to choose the best available features for your version of PostgreSQL.
"""

AshPostgres uses this information when generating queries and migrations,
to choose the best available features for your version of PostgreSQL.
"""
format_request =
"""
Please enter the version in the format major.minor.patch (e.g. 13.4.0)
format_request =
"""
Please enter the version in the format major.minor.patch (e.g. 13.4.0)
Default: 16.0.0
Default: 16.0.0
"""

"""
prompt =
if opts[:invalid_loop?] do
format_request
else
"#{lead_in}\n\n#{format_request}"
end

prompt =
if opts[:invalid_loop?] do
format_request
else
"#{lead_in}\n\n#{format_request}"
prompt
|> String.trim_trailing()
|> Mix.shell().prompt()
|> String.trim()
|> case do
"" -> "16.0.0"
input -> input
end
|> Version.parse()
|> case do
{:ok, version} -> version
:error -> get_min_pg_version(name, Keyword.put(opts, :invalid_loop?, true))
end

prompt
|> String.trim_trailing()
|> Mix.shell().prompt()
|> String.trim()
|> case do
"" -> "16.0.0"
input -> input
end
|> Version.parse()
|> case do
{:ok, version} -> version
:error -> get_min_pg_version(name, Keyword.put(opts, :invalid_loop?, true))
end
end
end
Expand Down
Loading

0 comments on commit a5e3eb7

Please sign in to comment.