Skip to content

Commit

Permalink
docs: reformat docs and revisit certain guides
Browse files Browse the repository at this point in the history
improvement: support `mix ash.rollback` with interactive rollback
  • Loading branch information
zachdaniel committed Apr 10, 2024
1 parent a02653e commit 38eec0b
Show file tree
Hide file tree
Showing 22 changed files with 277 additions and 131 deletions.
2 changes: 2 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ if Mix.env() == :test do
config :ash, :validate_domain_resource_inclusion?, false
config :ash, :validate_domain_config_inclusion?, false

config :ash_postgres, :ash_domains, [AshPostgres.Test.Domain]

config :ash_postgres, AshPostgres.TestRepo,
username: "postgres",
database: "ash_postgres_test",
Expand Down
2 changes: 1 addition & 1 deletion documentation/dsls/DSL:-AshPostgres.DataLayer.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ end
| [`migration_ignore_attributes`](#postgres-migration_ignore_attributes){: #postgres-migration_ignore_attributes } | `list(atom)` | `[]` | A list of attributes that will be ignored when generating migrations. |
| [`table`](#postgres-table){: #postgres-table } | `String.t` | | The table to store and read the resource from. If this is changed, the migration generator will not remove the old table. |
| [`schema`](#postgres-schema){: #postgres-schema } | `String.t` | | The schema that the table is located in. Schema-based multitenancy will supercede this option. If this is changed, the migration generator will not remove the old schema. |
| [`polymorphic?`](#postgres-polymorphic?){: #postgres-polymorphic? } | `boolean` | `false` | Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/polymorphic_resources.md) for more. |
| [`polymorphic?`](#postgres-polymorphic?){: #postgres-polymorphic? } | `boolean` | `false` | Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/resources/polymorphic-resources.md) for more. |


## postgres.custom_indexes
Expand Down
32 changes: 32 additions & 0 deletions documentation/home.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# AshPostgres Documentation

Welcome! This documentation is for `AshPostgres`, the PostgreSQL data layer for [Ash Framework](https://hexdocs.pm/ash). If you have not yet, please see the [Ash Framework documentation](https://hexdocs.pm/ash).

## Dive In

- [Get Started](documentation/tutorials/get-started-with-postgres.md)

---

## Tutorials

- [Get Started](documentation/tutorials/get-started-with-postgres.md)

## Topics

### Resources

- [References](documentation/topics/resources/references.md)
- [Polymorphic Resources](documentation/topics/resources/polymorphic-resources.md)

### Development

- [Migrations and tasks](documentation/topics/development/migrations-and-tasks.md)
- [Testing](documentation/topics/development/testing.md)
- [Upgrading to 2.0](documentation/topics/development/upgrading-to-2.0.md)

### Advanced

- [Expressions](documentation/topics/advanced/expressions.md)
- [Manual Relationships](documentation/topics/advanced/manual-relationships.md)
- [Schema Based Multitenancy](documentation/topics/advanced/schema-based-multitenancy.md)
43 changes: 0 additions & 43 deletions documentation/how_to/using-fragments.md

This file was deleted.

77 changes: 77 additions & 0 deletions documentation/topics/advanced/expressions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Expressions

In addition to the expressions listed in the [Ash expressions guide](https://hexdocs.pm/ash/expressions.html), AshPostgres provides the following expressions

# Fragments

Fragments allow you to use arbitrary postgres expressions in your queries. Fragments can often be an escape hatch to allow you to do things that don't have something officially supported with Ash.

### Examples

#### Simple expressions

```elixir
fragment("? / ?", points, count)
```

#### Calling functions

```elixir
fragment("repeat('hello', 4)")
```

#### Using entire queries

```elixir
fragment("points > (SELECT SUM(points) FROM games WHERE user_id = ? AND id != ?)", user_id, id)
```

> ### a last resport {: .warning}
>
> Using entire queries as shown above is a last resort, but can sometimes be the best way to accomplish a given task.
#### In calculations

```elixir
calculations do
calculate :lower_name, :string, expr(
fragment("LOWER(?)", name)
)
end
```

#### In migrations

```elixir
create table(:managers, primary_key: false) do
add :id, :uuid, null: false, default: fragment("UUID_GENERATE_V4()"), primary_key: true
end
```

## Like and ILike

These wrap the postgres builtin like and ilike operators.

Please be aware, these match *patterns* not raw text. Use `contains/1` if you want to match text without supporting patterns, i.e `%` and `_` have semantic meaning!

For example:

```elixir
Ash.Query.filter(User, like(name, "%obo%")) # name contains obo anywhere in the string, case sensitively
```

```elixir
Ash.Query.filter(User, ilike(name, "%ObO%")) # name contains ObO anywhere in the string, case insensitively
```

## Trigram similarity

To use this expression, you must have the `pg_trgm` extension in your repos `installed_extensions` list.

This calls the `similarity` function from that extension. See more https://www.postgresql.org/docs/current/pgtrgm.htmlhere: https://www.postgresql.org/docs/current/pgtrgm.html

For example:

```elixir
Ash.Query.filter(User, trigram_similarity(first_name, "fred") > 0.8)
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Join Manual Relationships
# Manual Relationships

See [Defining Manual Relationships](https://hexdocs.pm/ash/defining-manual-relationships.html) for an idea of manual relationships in general.
Manual relationships allow for expressing complex/non-typical relationships between resources in a standard way.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Testing With Postgres
# Testinging with AshPostgres

When using AshPostgres resources in tests, you will likely want to include use a test case similar to the following. This will ensure that your repo runs everything in a transaction.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Upgrade from 1.0 to 2.0
# Upgrading to 2.0

There are only three breaking changes in this release, one of them is very significant, the other two are minor.

Expand Down
40 changes: 0 additions & 40 deletions documentation/topics/postgres-expressions.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# References

To configure the foreign keys on a resource, we use the `references` block.
To configure the behavior of generated foreign keys on a resource, we use the `references` section.

For example:

Expand All @@ -10,14 +10,14 @@ references do
end
```

## Important

No resource logic is applied with these operations! No authorization rules or validations take place, and no notifications are issued. This operation happens *directly* in the database. That
> ### Actions are not used for this behavior {: .warning}
>
> No resource logic is applied with these operations! No authorization rules or validations take place, and no notifications are issued. This operation happens *directly* in the database.
## Nothing vs Restrict

The difference between `:nothing` and `:restrict` is subtle and, if you are unsure, choose `:nothing` (the default behavior). `:restrict` will prevent the deletion from happening *before* the end of the database transaction, whereas `:nothing` allows the transaction to complete before doing so. This allows for things like updating or deleting the destination row and *then* updating updating or deleting the reference(as long as you are in a transaction).

## On Delete

This option is called `on_delete`, instead of `on_destroy`, because it is hooking into the database level deletion, *not* a `destroy` action in your resource.
This option is called `on_delete`, instead of `on_destroy`, because it is hooking into the database level deletion, *not* a `destroy` action in your resource. See the warning above.
101 changes: 99 additions & 2 deletions lib/data_layer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ defmodule AshPostgres.DataLayer do
type: :boolean,
default: false,
doc: """
Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/polymorphic_resources.md) for more.
Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/resources/polymorphic-resources.md) for more.
"""
]
]
Expand All @@ -389,10 +389,107 @@ defmodule AshPostgres.DataLayer do
]

def migrate(args) do
# TODO: take args that we care about
Mix.Task.run("ash_postgres.migrate", args)
end

def rollback(args) do
repos = AshPostgres.Mix.Helpers.repos!([], args)

show_for_repo? = Enum.count_until(repos, 2) == 2

for repo <- repos do
for_repo =
if show_for_repo? do
" for repo #{inspect(repo)}"
else
""
end

migrations_path = AshPostgres.Mix.Helpers.migrations_path([], repo)
tenant_migrations_path = AshPostgres.Mix.Helpers.tenant_migrations_path([], repo)

files =
migrations_path
|> Path.join("**/*.exs")
|> Path.wildcard()
|> Enum.sort()
|> Enum.reverse()
|> Enum.take(20)
|> Enum.map(&String.trim_leading(&1, migrations_path))
|> Enum.with_index()
|> Enum.map(fn {file, index} -> "#{index + 1}: #{file}" end)

n =
Mix.shell().prompt("""
How many migrations should be rolled back#{for_repo}? (default: 0)
Last 20 migration names, with the input you must provide to
rollback up to *and including* that migration:
#{Enum.join(files, "\n")}
Rollback to:
""" |> String.trim_trailing())
|> String.trim()
|> case do
"" ->
0

n ->
try do
String.to_integer(n)
rescue
_ ->
raise "Required an integer value, got: #{n}"

Check warning on line 442 in lib/data_layer.ex

View workflow job for this annotation

GitHub Actions / ash-ci (15) / mix credo --strict

Use `reraise` inside a rescue block to preserve the original stacktrace.

Check warning on line 442 in lib/data_layer.ex

View workflow job for this annotation

GitHub Actions / ash-ci (16) / mix credo --strict

Use `reraise` inside a rescue block to preserve the original stacktrace.

Check warning on line 442 in lib/data_layer.ex

View workflow job for this annotation

GitHub Actions / ash-ci (14) / mix credo --strict

Use `reraise` inside a rescue block to preserve the original stacktrace.
end
end

Mix.Task.run("ash_postgres.rollback", args ++ ["-r", inspect(repo), "-n", to_string(n)])
Mix.Task.reenable("ash_postgres.rollback")

tenant_files =
tenant_migrations_path
|> Path.join("**/*.exs")
|> Path.wildcard()
|> Enum.sort()
|> Enum.reverse()
|> Enum.take(20)
|> Enum.map(&String.trim_leading(&1, tenant_migrations_path))
|> Enum.with_index()
|> Enum.map(fn {file, index} -> "#{index + 1}: #{file}" end)

if !Enum.empty?(tenant_files) do
n =
Mix.shell().prompt("""
How many _tenant_ migrations should be rolled back#{for_repo}? (default: 0)
Last 20 migration names, with the input you must provide to
rollback up to *and including* that migration:
#{Enum.join(tenant_files, "\n")}
Rollback to:
""")
|> String.trim()
|> case do
"" ->
0

n ->
try do
String.to_integer(n)
rescue
_ ->
raise "Required an integer value, got: #{n}"

Check warning on line 483 in lib/data_layer.ex

View workflow job for this annotation

GitHub Actions / ash-ci (15) / mix credo --strict

Use `reraise` inside a rescue block to preserve the original stacktrace.

Check warning on line 483 in lib/data_layer.ex

View workflow job for this annotation

GitHub Actions / ash-ci (16) / mix credo --strict

Use `reraise` inside a rescue block to preserve the original stacktrace.

Check warning on line 483 in lib/data_layer.ex

View workflow job for this annotation

GitHub Actions / ash-ci (14) / mix credo --strict

Use `reraise` inside a rescue block to preserve the original stacktrace.
end
end

Mix.Task.run("ash_postgres.rollback", args ++ ["--tenants", "-r", inspect(repo), "-n", to_string(n)])
Mix.Task.reenable("ash_postgres.rollback")
end
end
end

def codegen(args) do
{args, _, _} = OptionParser.parse(args, strict: [name: :string])

Expand Down
2 changes: 1 addition & 1 deletion lib/mix/helpers.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule AshPostgres.MixHelpers do
defmodule AshPostgres.Mix.Helpers do
@moduledoc false
def domains!(opts, args) do
apps =
Expand Down
Loading

0 comments on commit 38eec0b

Please sign in to comment.