Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions lib/mix/tasks/ash_postgres.gen.resources.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ if Code.ensure_loaded?(Igniter) do
- `public` - Mark all attributes and relationships as `public? true`. Defaults to `true`.
- `no-migrations` - Do not generate snapshots & migrations for the resources. Defaults to `false`.
- `skip-unknown` - Skip any attributes with types that we don't have a corresponding Elixir type for, and relationships that we can't assume the name of.
- `use-fragments`, `f` - Generate attributes and relationships in a separate fragment file. This allows the fragment to be regenerated without affecting user customizations in the main resource file. Defaults to `false`.

## Tables

Expand Down Expand Up @@ -61,20 +62,23 @@ if Code.ensure_loaded?(Igniter) do
skip_unknown: :boolean,
migrations: :boolean,
snapshots_only: :boolean,
domain: :keep
domain: :keep,
use_fragments: :boolean
],
aliases: [
t: :tables,
y: :boolean,
r: :repo,
e: :extend,
d: :domain,
s: :skip_tables
s: :skip_tables,
f: :use_fragments
],
defaults: [
default_actions: true,
migrations: true,
public: true
public: true,
use_fragments: false
]
}
end
Expand Down
147 changes: 146 additions & 1 deletion lib/resource_generator/resource_generator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ if Code.ensure_loaded?(Igniter) do
|> Spec.add_relationships(resources, opts)

Enum.reduce(specs, igniter, fn table_spec, igniter ->
table_to_resource(igniter, table_spec, domain, opts)
if opts[:use_fragments] do
table_to_resource_with_fragment(igniter, table_spec, domain, opts)
else
table_to_resource(igniter, table_spec, domain, opts)
end
end)
end

Expand Down Expand Up @@ -149,6 +153,147 @@ if Code.ensure_loaded?(Igniter) do
end)
end

defp table_to_resource_with_fragment(
igniter,
%AshPostgres.ResourceGenerator.Spec{} = table_spec,
domain,
opts
) do
fragment_module = Module.concat(table_spec.resource, Model)

fragment_content =
"""
use Spark.Dsl.Fragment,
of: Ash.Resource

#{attributes_block(table_spec, opts)}
#{identities_block(table_spec)}
#{relationships_block(table_spec, opts)}
"""

resource_path = Igniter.Project.Module.proper_location(igniter, table_spec.resource)

resource_exists? =
Rewrite.has_source?(igniter.rewrite, resource_path) ||
Igniter.exists?(igniter, resource_path)

if resource_exists? do
# Only create/update the fragment file
Igniter.Project.Module.create_module(igniter, fragment_module, fragment_content)
else
# Create both resource and fragment
no_migrate_flag =
if opts[:no_migrations] do
"migrate? false"
end

resource_content =
"""
use Ash.Resource,
domain: #{inspect(domain)},
data_layer: AshPostgres.DataLayer,
fragments: [#{inspect(fragment_module)}]

#{default_actions(opts)}

postgres do
table #{inspect(table_spec.table_name)}
repo #{inspect(table_spec.repo)}
#{schema_option(table_spec)}
#{no_migrate_flag}
#{references(table_spec, opts[:no_migrations])}
#{custom_indexes(table_spec, opts[:no_migrations])}
#{check_constraints(table_spec, opts[:no_migrations])}
#{skip_unique_indexes(table_spec)}
#{identity_index_names(table_spec)}
end
"""

igniter
|> Igniter.Project.Module.create_module(fragment_module, fragment_content)
|> Igniter.Project.Module.create_module(table_spec.resource, resource_content)
|> Ash.Domain.Igniter.add_resource_reference(domain, table_spec.resource)
|> then(fn igniter ->
if opts[:extend] && opts[:extend] != [] do
Igniter.compose_task(igniter, "ash.patch.extend", [
table_spec.resource | opts[:extend] || []
])
else
igniter
end
end)
end
end

defp attributes_block(table_spec, opts) do
"""
attributes do
#{attributes(table_spec, opts)}
end
"""
end

defp identities_block(%{indexes: indexes}) do
indexes
|> Enum.filter(fn %{unique?: unique?, columns: columns} ->
unique? && Enum.all?(columns, &Regex.match?(~r/^[0-9a-zA-Z_]+$/, &1))
end)
|> Enum.map(fn index ->
name = index.identity_name

fields = "[" <> Enum.map_join(index.columns, ", ", &":#{&1}") <> "]"

case identity_options(index) do
"" ->
"identity :#{name}, #{fields}"

options ->
"""
identity :#{name}, #{fields} do
#{options}
end
"""
end
end)
|> case do
[] ->
""

identities ->
"""
identities do
#{Enum.join(identities, "\n")}
end
"""
end
end

defp relationships_block(%{relationships: []}, _opts), do: ""

defp relationships_block(%{relationships: relationships} = spec, opts) do
relationships
|> Enum.map_join("\n", fn relationship ->
case relationship_options(spec, relationship, opts) do
"" ->
"#{relationship.type} :#{relationship.name}, #{inspect(relationship.destination)}"

options ->
"""
#{relationship.type} :#{relationship.name}, #{inspect(relationship.destination)} do
#{options}
end
"""
end
end)
|> then(fn rels ->
"""
relationships do
#{rels}
end
"""
end)
end

defp schema_option(%{schema: schema}) when schema != "public" do
"schema #{inspect(schema)}"
end
Expand Down
Loading