Packages
ash
3.4.56
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
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.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.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.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
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.0
2.2.0
2.1.0
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
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash/resource/igniter.ex
if Code.ensure_loaded?(Igniter) do
defmodule Ash.Resource.Igniter do
@moduledoc """
Codemods for working with Ash.Resource modules
## Important Details
This interrogates *the source code* of a resource, not its ultimate compiled state.
What this means, is that things like `defines_attribute` will not return `true` if
the attribute is added by an extension. Only if it appears literally in the source code
of the resource or one of its `Spark.Dsl.Fragment`s.
"""
@doc "List all resource modules found in the project"
def list_resources(igniter) do
Igniter.Project.Module.find_all_matching_modules(igniter, fn _mod, zipper ->
zipper
|> Igniter.Code.Module.move_to_use(resource_mods(igniter))
|> case do
{:ok, _} ->
true
_ ->
false
end
end)
end
@doc "Gets the domain from the given resource module"
@spec domain(Igniter.t(), Ash.Resource.t()) ::
{:ok, Igniter.t(), Ash.Domain.t()} | {:error, Igniter.t()}
def domain(igniter, resource) do
case Igniter.Project.Module.find_module(igniter, resource) do
{:ok, {igniter, _source, zipper}} ->
with {:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper),
{:ok, zipper} <-
Igniter.Code.Module.move_to_use(zipper, resource_mods(igniter)),
{:ok, zipper} <-
Igniter.Code.Function.move_to_nth_argument(zipper, 1),
{:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :domain),
module when not is_nil(module) <-
Igniter.Project.Module.to_module_name(zipper.node) do
{:ok, igniter, module}
else
_ ->
{:error, igniter}
end
{:error, igniter} ->
{:error, igniter}
end
end
def resource_mods(igniter) do
app_name = Igniter.Project.Application.app_name(igniter)
[Ash.Resource | List.wrap(Application.get_env(app_name, :base_resources))]
end
@doc "Adds the given code block to the block of the resource specified"
def add_block(igniter, resource, block, chunk) do
Igniter.Project.Module.find_and_update_module!(igniter, resource, fn zipper ->
with {:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
block,
1
),
{:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do
{:ok, Igniter.Code.Common.add_code(zipper, chunk)}
else
_ ->
block_with_chunk = """
#{block} do
#{chunk}
end
"""
{:ok, Igniter.Code.Common.add_code(zipper, block_with_chunk)}
end
end)
end
@doc "Adds a bypass to the top of the resource's `policies` block"
def add_bypass(igniter, resource, condition, body) do
Igniter.Project.Module.find_and_update_module!(igniter, resource, fn zipper ->
with {:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:policies,
1
),
{:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do
bypass =
quote do
bypass unquote(condition) do
unquote(body)
end
end
|> Sourceror.to_string()
|> Sourceror.parse_string!()
{:ok, Igniter.Code.Common.add_code(zipper, bypass, placement: :before)}
else
_ ->
bypass =
quote do
policies do
bypass unquote(condition) do
unquote(body)
end
end
end
|> Sourceror.to_string()
|> Sourceror.parse_string!()
{:ok, Igniter.Code.Common.add_code(zipper, bypass)}
end
end)
end
@doc "Adds a policy to the bottom of the resource's `policies` block"
def add_policy(igniter, resource, condition, body) do
Igniter.Project.Module.find_and_update_module!(igniter, resource, fn zipper ->
with {:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:policies,
1
),
{:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do
policy =
quote do
policy unquote(condition) do
unquote(body)
end
end
|> Sourceror.to_string()
|> Sourceror.parse_string!()
{:ok, Igniter.Code.Common.add_code(zipper, policy, placement: :after)}
else
_ ->
policy =
quote do
policies do
policy unquote(condition) do
unquote(body)
end
end
end
|> Sourceror.to_string()
|> Sourceror.parse_string!()
{:ok, Igniter.Code.Common.add_code(zipper, policy)}
end
end)
end
@doc "Returns true if the given resource defines an attribute with the provided name"
@spec defines_attribute(Igniter.t(), Ash.Resource.t(), atom()) :: {Igniter.t(), true | false}
def defines_attribute(igniter, resource, name) do
{igniter, defines?} =
if name in [:inserted_at, :updated_at] do
has_timestamps_call(igniter, resource)
else
{igniter, false}
end
if defines? do
{igniter, true}
else
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :attributes),
{:ok, _zipper} <-
move_to_one_of_function_call_in_current_scope(
zipper,
[
:attribute,
:uuid_primary_key,
:integer_primary_key,
:uuid_v7_primary_key,
:create_timestamp,
:update_timestamp
],
[2, 3],
&Igniter.Code.Function.argument_equals?(&1, 0, name)
) do
{:ok, true}
else
_ ->
:error
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
end
@doc "Returns true if the given resource defines an identity with the provided name"
@spec defines_identity(Igniter.t(), Ash.Resource.t(), atom()) :: {Igniter.t(), true | false}
def defines_identity(igniter, resource, name) do
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :identities),
{:ok, _zipper} <-
move_to_one_of_function_call_in_current_scope(
zipper,
[:identity],
[2, 3],
&Igniter.Code.Function.argument_equals?(&1, 0, name)
) do
{:ok, true}
else
_ ->
:error
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
@doc "Returns true if the given resource defines an action with the provided name"
@spec defines_action(Igniter.t(), Ash.Resource.t(), atom()) :: {Igniter.t(), true | false}
def defines_action(igniter, resource, name) do
{igniter, defines?} =
if name in [:create, :read, :update, :destroy] do
has_default_action(igniter, resource, name)
else
{igniter, false}
end
if defines? do
{igniter, true}
else
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :actions),
{:ok, _zipper} <-
move_to_one_of_function_call_in_current_scope(
zipper,
[
:create,
:update,
:read,
:destroy,
:action
],
[2, 3, 4],
&Igniter.Code.Function.argument_equals?(&1, 0, name)
) do
{:ok, true}
else
_ ->
:error
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
end
@spec ensure_primary_action(
Igniter.t(),
Ash.Resource.t(),
:create | :read | :update | :destroy
) ::
Igniter.t()
def ensure_primary_action(igniter, resource, type) do
case has_default_action(igniter, resource, type) do
{igniter, true} ->
igniter
{igniter, false} ->
case has_action_with_primary(igniter, resource, type) do
{igniter, true} -> igniter
{igniter, false} -> add_default_action(igniter, resource, type)
end
end
end
@doc "Returns true if the given resource defines a relationship with the provided name"
@spec defines_relationship(Igniter.t(), Ash.Resource.t(), atom()) ::
{Igniter.t(), true | false}
def defines_relationship(igniter, resource, name) do
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :relationships),
{:ok, _zipper} <-
move_to_one_of_function_call_in_current_scope(
zipper,
[
:has_one,
:has_many,
:belongs_to,
:many_to_many
],
[2, 3],
&Igniter.Code.Function.argument_equals?(&1, 0, name)
) do
{:ok, true}
else
_ ->
:error
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
@doc "Adds the given code block to the resource's `attributes` block if there is no existing attribute with the given name"
def add_new_attribute(igniter, resource, name, attribute) do
{igniter, defines?} = defines_attribute(igniter, resource, name)
if defines? do
igniter
else
add_attribute(igniter, resource, attribute)
end
end
@doc "Adds the given code block to the resource's `identities` block if there is no existing identity with the given name"
def add_new_identity(igniter, resource, name, identity) do
{igniter, defines?} = defines_identity(igniter, resource, name)
if defines? do
igniter
else
add_identity(igniter, resource, identity)
end
end
@doc "Adds the given code block to the resource's `identities` block"
def add_identity(igniter, resource, identity) do
add_block(igniter, resource, :identities, identity)
end
@doc "Adds the given code block to the resource's `attributes` block"
def add_attribute(igniter, resource, attribute) do
add_block(igniter, resource, :attributes, attribute)
end
@doc "Adds an action if it doesn't already exist"
def add_new_action(igniter, resource, name, action) do
{igniter, defines?} = defines_action(igniter, resource, name)
if defines? do
igniter
else
add_action(igniter, resource, action)
end
end
@doc "Adds the given code block to the resource's `actions` block"
def add_action(igniter, resource, action) do
add_block(igniter, resource, :actions, action)
end
@doc "Adds the given code block to the resource's `relationships` block"
def add_new_relationship(igniter, resource, name, relationship) do
{igniter, defines?} = defines_relationship(igniter, resource, name)
if defines? do
igniter
else
add_block(igniter, resource, :relationships, relationship)
end
end
@doc "Adds the given code block to the resource's `relationships` block"
def add_relationship(igniter, resource, relationship) do
add_block(igniter, resource, :relationships, relationship)
end
@doc "Adds the given code block to the resource's `resource` block"
def add_resource_configuration(igniter, resource, resource_configuration) do
add_block(igniter, resource, :resource, resource_configuration)
end
@doc "Ensures that created_at and updated_at timestamps exist on the resource"
def ensure_timestamps(igniter, resource) do
{igniter, defines_inserted_at?} = defines_attribute(igniter, resource, :inserted_at)
{igniter, defines_created_at?} = defines_attribute(igniter, resource, :created_at)
defines_create_timestamp? = defines_inserted_at? || defines_created_at?
{igniter, defines_updated_at?} = defines_attribute(igniter, resource, :updated_at)
igniter
|> then(fn igniter ->
if defines_create_timestamp? do
igniter
else
add_create_timestamp_call(igniter, resource)
end
end)
|> then(fn igniter ->
if defines_updated_at? do
igniter
else
add_update_timestamp_call(igniter, resource)
end
end)
end
defp enter_section(zipper, name) do
with {:ok, zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
name,
1
) do
Igniter.Code.Common.move_to_do_block(zipper)
end
end
defp move_to_one_of_function_call_in_current_scope(zipper, [name], arities, pred) do
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
name,
arities,
pred
)
end
defp move_to_one_of_function_call_in_current_scope(zipper, [name | rest], arities, pred) do
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
name,
arities,
pred
) do
{:ok, zipper} -> {:ok, zipper}
:error -> move_to_one_of_function_call_in_current_scope(zipper, rest, arities, pred)
end
end
defp has_default_action(igniter, resource, type) do
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :actions),
{:ok, _zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:defaults,
1,
fn zipper ->
with {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 0),
{:ok, _zipper} <-
Igniter.Code.List.move_to_list_item(zipper, fn zipper ->
if Igniter.Code.Tuple.tuple?(zipper) do
case Igniter.Code.Tuple.tuple_elem(zipper, 0) do
{:ok, zipper} ->
Igniter.Code.Common.nodes_equal?(zipper, type)
_ ->
false
end
else
Igniter.Code.Common.nodes_equal?(zipper, type)
end
end) do
true
else
_ -> false
end
end
) do
{:ok, true}
else
_ -> :error
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
@spec has_action_with_primary(Igniter.t(), Ash.Resource.t(), atom()) ::
{Igniter.t(), true | false}
def has_action_with_primary(igniter, resource, type) do
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :actions),
{:ok, _zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(zipper, type, [1, 2]) do
case Igniter.Code.Common.move_to_do_block(zipper) do
{:ok, zipper} ->
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:primary?,
1,
&Igniter.Code.Function.argument_equals?(&1, 0, true)
)
:error ->
with {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1),
{:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :primary?),
true <- Igniter.Code.Common.nodes_equal?(zipper, true) do
{:ok, true}
end
end
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
defp add_default_action(igniter, resource, type) do
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :actions),
{:ok, _zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:defaults,
1
) do
{:ok, true}
else
_ -> :error
end
end)
|> case do
{:ok, igniter, module, _value} ->
Igniter.Project.Module.find_and_update_module!(igniter, module, fn zipper ->
with {:ok, zipper} <- enter_section(zipper, :actions),
{:ok, _zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:defaults,
1
),
{:ok, zipper} <- Igniter.Code.List.prepend_new_to_list(zipper, type) do
{:ok, zipper}
else
_ ->
{:error,
"Failed to add a default action of type #{inspect(type)} in #{inspect(module)}"}
end
end)
{:error, igniter} ->
igniter
end
end
defp add_create_timestamp_call(igniter, resource) do
add_block(igniter, resource, :attributes, "create_timestamp :created_at")
end
defp add_update_timestamp_call(igniter, resource) do
add_block(igniter, resource, :attributes, "update_timestamp :updated_at")
end
defp has_timestamps_call(igniter, resource) do
Spark.Igniter.find(igniter, resource, fn _, zipper ->
with {:ok, zipper} <- enter_section(zipper, :attributes),
{:ok, _zipper} <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:timestamps,
1
) do
{:ok, true}
else
_ -> :error
end
end)
|> case do
{:ok, igniter, _module, _value} ->
{igniter, true}
{:error, igniter} ->
{igniter, false}
end
end
end
end