docs: more restructuring, reviewing of guides

This commit is contained in:
Zach Daniel 2024-04-08 13:39:45 -04:00
parent 5e1df439d7
commit 4cbf2d3e14
21 changed files with 121 additions and 168 deletions

View file

@ -206,7 +206,7 @@ end
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`timeout`](#execution-timeout){: #execution-timeout } | `timeout` | `30000` | The default timeout to use for requests using this domain. See the [timeouts guide](/documentation/topics/timeouts.md) for more. |
| [`timeout`](#execution-timeout){: #execution-timeout } | `timeout` | `30000` | The default timeout in milliseconds to use for requests using this domain. See the [timeouts guide](/documentation/topics/timeouts.md) for more. |
| [`trace_name`](#execution-trace_name){: #execution-trace_name } | `String.t` | | The name to use in traces. Defaults to the last part of the module. See the [monitoring guide](/documentation/topics/monitoring.md) for more |

View file

@ -3,20 +3,19 @@ This file was generated by Spark. Do not edit it by hand.
-->
# DSL: Ash.Notifier.PubSub
Ash includes a builtin notifier to help you publish events over any kind of pub-sub pattern.
This is plug and play with `Phoenix.PubSub`, but could be used with any pubsub pattern.
A builtin notifier to help you publish events over any kind of pub-sub tooling.
This is plug and play with `Phoenix.PubSub`, but could be used with any pubsub system.
You configure a module that defines a `broadcast/3` function, and then add some "publications"
which configure under what conditions an event should be sent and what the topic should be.
For the full DSL spec see `Ash.Notifier.PubSub`
## Debugging PubSub
It can be quite frustrating when setting up pub_sub when everything appears to be set up properly, but
you aren't receiving events. This usually means some kind of mismatch between the event names produced
by the resource/config of your publications, and you can use the following flag to display debug
information about pub sub events coming from `Ash.Notifier.PubSub`
information about all pub sub events.
```elixir
config :ash, :pub_sub, debug?: true
@ -107,8 +106,6 @@ Configured with `broadcast_type`.
## pub_sub
A section for configuring how resource actions are published over pubsub
See `Ash.Notifier.PubSub` and the [Notifiers](/documentation/topics/notifiers.md) guide for more.
### Nested DSLs
* [publish](#pub_sub-publish)
@ -151,9 +148,6 @@ publish action, topic
Configure a given action to publish its results over a given topic.
See `Ash.Notifier.PubSub` and the [Notifiers](/documentation/topics/notifiers.md) guides for more.
### Examples
@ -196,10 +190,7 @@ publish_all type, topic
```
Works just like `publish`, except that it takes a type
and publishes all actions of that type
See `Ash.Notifier.PubSub` and the [Notifiers](/documentation/topics/notifiers.md) guides for more.
Works the same as `publish`, except that it takes a type and publishes all actions of that type.

View file

@ -3432,7 +3432,7 @@ Options for configuring the multitenancy behavior of a resource.
To specify a tenant, use `Ash.Query.set_tenant/2` or
`Ash.Changeset.set_tenant/2` before passing it to an operation.
See the [multitenancy guide](/documentation/topics/multitenancy.md)
See the [multitenancy guide](/documentation/topics/advanced/multitenancy.md)

View file

@ -34,14 +34,15 @@ Welcome to the Ash Framework documentation! Here you will find everything you ne
### Resources
- [Attributes](documentation/topics/resources/attributes.md)
- [Relationships](documentation/topics/resources/relationships.md)
- [Calculations](documentation/topics/resources/calculations.md)
- [Aggregates](documentation/topics/resources/aggregates.md)
- [Code Interfaces](documentation/topics/resources/code-interfaces.md)
- [Relationships](documentation/topics/resources/relationships.md)
- [Identities](documentation/topics/resources/identities.md)
- [Validations](documentation/topics/resources/validations.md)
- [Changes](documentation/topics/resources/changes.md)
- [Embedded Resources](documentation/topics/resources/embedded-resources.md)
- [Notifiers](documentation/topics/resources/notifiers.md)
### Actions
@ -61,9 +62,17 @@ Welcome to the Ash Framework documentation! Here you will find everything you ne
### Development
- [Project Structure](documentation/topics/development/project-structure.md)
- [Testing](documentation/topics/development/testing.md)
- [Development Utilities](documentation/topics/development/development-utilities.md)
- [Upgrading to 3.0](documentation/topics/development/upgrading-to-3.0.md)
### Advanced
- [Monitoring](documentation/topics/advanced/monitoring.md)
- [Reactor](documentation/topics/advanced/reactor.md)
- [Timeouts](documentation/topics/advanced/timeouts.md)
### About Ash
- [What is Ash?](documentation/topics/about_ash/what-is-ash.md)
@ -79,6 +88,14 @@ Welcome to the Ash Framework documentation! Here you will find everything you ne
## Reference
- [Glossary](documentation/topics/reference/glossary.md)
- [Expressions](documentation/topics/reference/expressions.md)
- [Ash.Resource DSL](documentation/dsls/DSL:-Ash.Resource.md)
- [Ash.Domain DSL](documentation/dsls/DSL:-Ash.Domain.md)
- [Ash.Reactor DSL](documentation/dsls/DSL:-Ash.Reactor.md)
- [Ash.Notifier.PubSub DSL](documentation/dsls/DSL:-Ash.Notifier.PubSub.md)
- [Ash.Policy.Authorizer DSL](documentation/dsls/DSL:-Ash.Policy.Authorizer.md)
- [Ash.DataLayer.Ets DSL](documentation/dsls/DSL:-Ash.DataLayer.Ets.md)
- [Ash.DataLayer.Mnesia DSL](documentation/dsls/DSL:-Ash.DataLayer.Mnesia.md)
## Packages

View file

@ -1,35 +0,0 @@
## Validations Section
The validations section allows you to create validations based on the changeset.
The only information available is the changeset. If you want to adjust the behavior based
on other details of the request, like the current user, you are most likely looking for
authorization.
A validation is a module that implements the `Ash.Resource.Validation` behaviour. The built in validations
expose utility functions that are imported into the resource's scope, to make them easier to read. You
can do this with custom validations as well. See the documentation in `Ash.Resource.Validation` for more information.
Right now, there are not very many built in validations, but the idea is that eventually we will have a rich
library of built in validations to choose from.
Validations can be scoped to the `type` (`:create`, `:update`, `:destroy`) of action (but not to specific actions). If you would like to adjust the validations for a specific action, you can place that validation directly in the action, i.e
```elixir
create :create do
validate attribute_equals(:name, "fred")
end
```
### Important Note
By default, validations in the global `validations` block will run on create and update only. Many validations don't make sense in the context of destroys. To make them run on destroy, use `on: [:create, :update, :destroy]`
### Examples
```elixir
validations do
validate present([:foo, :bar]), on: :update
validate present([:foo, :bar, :baz], at_least: 2), on: :create
validate absent([:foo, :bar, :baz], exactly: 1), on: [:update, :destroy]
validate {MyCustomValidation, [foo: :bar]}, on: :create
end
```

View file

@ -210,7 +210,7 @@ The following steps are performed when you call `Ash.Query.for_read/4`.
These steps are trimmed down, and are aimed at helping users understand the general flow. Some steps are omitted.
- Run `Ash.Query.for_read/3` if it has not already been run
- [Apply tenant filters for attribute](/documentation/topics/multitenancy.md)
- [Apply tenant filters for attribute](/documentation/topics/advanced/multitenancy.md)
- Apply [pagination](#pagination) options
- Run before action hooks
- Multi-datalayer filter is synthesized. We run queries in other data layers to fetch ids and translate related filters to `(destination_field in ^ids)`

View file

@ -1,6 +1,10 @@
# Instrumentation
# Monitoring
Instrumentation Ash has two primary components, `Ash.Tracer` and `:telemetry`. Instrumentation is closely tied to observability and monitoring.
Monitoring in Ash has two primary components, `Ash.Tracer` and `:telemetry`. Monitoring might also be referred to as observability and instrumentation.
## Packages
If you want to integrate with [Appsignal](https://www.appsignal.com), use the [AshAppsignal](https://hexdocs.pm/ash_appsignal) package, which is maintained by the core team. We believe that Appsignal is a great way to get started quickly, is relatively cost effective, and provides a great user experience.
## Telemetry

View file

@ -1,8 +1,9 @@
# Multitenancy
Multitenancy is the idea of splitting up your data into discrete areas, typically by customer. One of the most common examples of this, is the idea of splitting up a postgres database into "schemas" one for each customer that you have. Then, when making any queries, you ensure to always specify the "schema" you are querying, and you never need to worry about data crossing over between customers. The biggest benefits of this kind of strategy are the simplification of authorization logic, and better performance. Instead of all queries from all customers needing to use the same large table, they are each instead all using their own smaller tables. Another benefit is that it is much easier to delete a single customer's data on request.
Multitenancy is the splitting up your data into discrete areas, typically by customer. One of the most common examples of this, is the idea of splitting up a postgres database into "schemas" one for each customer that you have. Then, when making any queries, you ensure to always specify the "schema" you are querying, and you never need to worry about data crossing over between customers. The biggest benefits of this kind of strategy are the simplification of authorization logic, and better performance. Instead of all queries from all customers needing to use the same large table, they are each instead all using their own smaller tables. Another benefit is that it is much easier to delete a single customer's data on request.
In Ash, there are a two primary strategies for implementing multitenancy. The first (and simplest) works for any data layer that supports filtering, and requires very little maintenance/mental overhead. It is done via expecting a given attribute to line up with the `tenant`, and is called `:attribute`. The second, is based on the data layer backing your resource, and is called `:context`. For information on
context based multitenancy, see the documentation of your datalayer. For example, `AshPostgres` uses postgres schemas. While the `:attribute` strategy is simple to implement, it also offers fewer advantages, primarily acting as another way to ensure your data is filtered to the correct tenant.
## Attribute Multitenancy

View file

@ -1,13 +1,5 @@
# Timeouts
Timeouts in Ash work a bit differently than other tools. The following considerations must be taken into account:
1. If you run a resource action in a transaction, then the timeout applies to the entire transaction.
2. If the resource action you are running, and any of its `touches_resources` is *already in a transaction* then the timeout is ignored, as the outer transaction is handling the timeout.
3. If the resource is not in a transaction, and supports async execution (ash_postgres does), then everything is run in a task and awaited with the provided timeout.
4. If the data layer of the resource does not support timeouts, or async execution then timeouts are **ignored**.
5. As of the writing of this guide, none of the web API extensions support specifying a timeout. If/when they do, they will run the action they are meant to run in a `Task`.
## Ways to Specify Timeouts
You have a few options.
@ -43,7 +35,7 @@ actions do
end
```
And you can specify a default timeout on the domain module that you call your resources with. Overriding a domain with a default timeout requires providing a timeout of `:infinity` in one of the other methods.
You can also specify a default timeout on your domain modules.
```elixir
execution do
@ -51,4 +43,13 @@ execution do
end
```
Keep in mind, you can't specify timeouts in a before_action or after_action hook, because at that point you are already "within" the code that should have a timeout applied.
Keep in mind, you can't specify timeouts in a before_action or after_action hook, because at that point you are already "within" the code that should have a timeout applied.
## How are timeouts handled?
Timeouts in Ash work a bit differently than other tools. The following considerations must be taken into account:
1. If you run a resource action in a transaction, then the timeout applies to the entire transaction.
2. If the resource action you are running, and any of its `touches_resources` is *already in a transaction* then the timeout is ignored, as the outer transaction is handling the timeout.
3. If the resource is not in a transaction, and supports async execution (ash_postgres does), then everything is run in a task and awaited with the provided timeout.
4. If the data layer of the resource does not support timeouts, or async execution then timeouts are **ignored**.

View file

@ -1,20 +1,21 @@
# Structure your project
In this guide we'll discuss some best practices for how to structure your project.
In this guide we'll discuss some best practices for how to structure your project. These recommendations align well with [Elixir conventions](https://hexdocs.pm/elixir/1.16.2/naming-conventions.html#casing) around file and module naming. These conventions allow for a logical coupling of module and file names, and help keep your project organized and easy to navigate.
## A few notes
- None of the things we show you here are _requirements_, only recommendations.
- We avoid any pattern that requires you to name a file or module in a specific way, or put them in a specific place. This ensures that all connections between one module and another module are explicit rather than implicit.
- We break a common Elixir pattern of having the module name match the file name in one specific way. If the resource has a folder, we suggest putting the `resource.ex` in the folder with the same name. See the example below for more.
> ### These are recommendations {: .info}
>
> None of the things we show you here are _requirements_, only recommendations.
> Feel free to plot your own course here. Ash avoids any pattern that requires
> you to name a file or module in a specific way, or put them in a specific
> place. This ensures that all connections between one module and another
> module are _explicit_ rather than _implicit_.
```
lib/ # top level lib folder for your whole project
├─ my_app/ # your app's main namespace
│ ├─ accounts/ # The Accounts context
│ │ ├─ user/ # resource w/ additional files
│ │ ├─ user.ex # The resource file
│ │ ├─ token.ex # A resource without additional files
│ │ ├─ password_helper.ex # A non-resource file
│ │ ├─ accounts.ex # The Accounts domain module
@ -25,9 +26,9 @@ lib/ # top level lib folder for your whole project
│ │ │ ├─ preparations/ # Components of the reosurce, grouped by type
│ │ │ ├─ changes/
│ │ │ ├─ checks/
│ │ ├─ ticket.ex # The resource file
│ │ ├─ ticket.ex # The resource file
```
Generally speaking, your Ash application lives in the standard place within your elixir application, i.e `lib/my_app`. Within that folder, you create one folder for each context that you have. Each context has an `Ash.Domain` module within it, and the resources that live within that context. All resource interaction ultimately goes through a domain module.
Alongside the domain module, you have your resources, as well as any other files used in the context. If a resource has any additional files that are used to implement it, they should be placed in a folder with the same name as the resource, in subfolders grouping the files type, and _the resource should be placed there too_. This is optional, as stated above, but we've found that with large contexts it keeps things very simple.
Alongside the domain module, you have your resources, as well as any other files used in the context. If a resource has any additional files that are used to implement it, they should be placed in a folder with the same name as the resource, in subfolders grouping the files by type. Feel free to choose another logical grouping, but we've found by-type to be effective.

View file

@ -8,7 +8,9 @@ Ash.Expr.expr(x + y)
Ash.Expr.expr(post.title <> " | " <> post.subtitle)
```
Ash expressions have some interesting properties in their evaluation, primarily because they are made to be portable, i.e executable in some data layer (like SQL) or executable in Elixir. In general, these expressions will behave the same way they do in Elixir. The primary difference is how `nil` values work. They behave the way that `NULL` values behave in SQL. This is primarily because this pattern is easier to replicate to various popular data layers, and is generally safer when using expressions for things like authentication. The practical implications of this are that `nil` values will "poison" many expressions, and cause them to return `nil`. For example, `x + nil` would always evaluate to `nil`. Additionally, `true and nil` will always result in `nil`, _this is also true with or and not_, i.e `true or nil` will return `nil`, and `not nil` will return `nil`.
> ### Ash Expressions are SQL-ish {: .info}
>
> Ash expressions have some interesting properties in their evaluation, primarily because they are made to be portable, i.e executable in some data layer (like SQL) or executable in Elixir. In general, these expressions will behave the same way they do in Elixir. The primary difference is how `nil` values work. They behave the way that `NULL` values behave in SQL. This is primarily because this pattern is easier to replicate to various popular data layers, and is generally safer when using expressions for things like authentication. The practical implications of this are that `nil` values will "poison" many expressions, and cause them to return `nil`. For example, `x + nil` would always evaluate to `nil`. Additionally, `true and nil` will always result in `nil`, _this is also true with or and not_, i.e `true or nil` will return `nil`, and `not nil` will return `nil`.
## Operators
@ -134,21 +136,9 @@ mix deps.compile ash --force
These expressions will be available across all usages of Ash expressions within your application.
## Use cases for expressions
## Filter semantics & joins
### Filters
The most obvious place we use expressions is when filtering data. For example:
```elixir
Ash.Query.filter(Ticket, status == :open and opened_at >= ago(10, :day))
```
These filters will be run in the data layer, i.e in the SQL query.
#### Filter semantics & joins
The semantics of Ash filters are probably slightly different than what you are used to, and they are important to understand. Every filter expression is always talking about a single row, potentially "joined" to single related rows. By referencing relationships, you are implicitly doing a join. For those familiar with SQL terminology, it is equivalent to a left join, although AshPostgres can detect when it is safe to do an inner join (for performance reason). Lets use an example of `posts` and `comments`.
The semantics of Ash filters are probably slightly different than what you are used to, and they are important to understand. Every filter expression is always talking about a single row, potentially "joined" to single related rows. By referencing relationships, you are implicitly doing a join. For those familiar with SQL terminology, it is equivalent to a left join, although AshPostgres can detect when it is safe to do an inner join (for performance reasons). Lets use an example of `posts` and `comments`.
Given a filter like the following:
@ -174,7 +164,7 @@ Post
That code _seems_ like it ought to produce a filter over `Post` that would give us any post with a comment having more than 10 points, _and_ with a comment tagged `elixir`. That is not the same thing as having a _single_ comment that meets both those criteria. So how do we make this better?
##### Exists
### Exists
Lets rewrite the above using exists:
@ -194,7 +184,7 @@ Post
Now, they will compose properly! Generally speaking, you should use exists when you are filtering across any relationships that are `to_many` relationships \*even if you don't expect your filter to be composed. Currently, the filter syntax does not minimize(combine) these `exists/2` statements, but doing so is not complex and can be added. While unlikely, please lodge an issue if you see any performance issues with `exists`.
##### Exists at path
### Exists at path
Sometimes, you want the ability to say that some given row must have an existing related entry matching a filter. For example:
@ -204,19 +194,6 @@ Ash.Query.filter(Post, author.exists(roles, name == :admin) and author.active)
While the above is not common, it can be useful in some specific circumstances, and is used under the hood by the policy authorizer when combining the filters of various resources to create a single filter.
## Relationship Filters
When filtering relationships, you can use the `parent/1` function to scope a part of the expression to "source" of the join. This allows for very expressive relationships! Keep in mind, however, that if you want to update and/or manage these relationships, you'll have to make sure that any attributes that make these things actually related are properly set.
```elixir
has_many :descendents, __MODULE__ do
description "All descendents in the same tree"
no_attributes? true # this says that there is no matching source_attribute and destination_attribute on this relationship
# This is an example using postgres' ltree extension.
filter expr(tree_id == parent(tree_id) and fragment("? @> ?", parent(path), path))
end
```
## Portability
Ash expressions being portable is more important than it sounds. For example, if you were using AshPostgres and had the following calculation, which is an expression capable of being run in elixir or translated to SQL:
@ -238,10 +215,10 @@ You would see that it ran a SQL query with the `full_name` calculation as SQL. T
```elixir
# data can be loaded in the query like above, or on demand later
Accounts.load!(user, :full_name)
Accounts.load!(user, :full_name, reuse_values?: true)
```
you would see that no SQL queries are run. The calculation is run directly in Elixir and the value is set.
you would see that no SQL queries are run. The calculation is run directly in Elixir without needing to visit the database.
## Parent
@ -253,6 +230,8 @@ Ash.Query.filter(exists(open_tickets, severity >= parent(severity_threshold)))
```elixir
has_many :relevant_tickets, Ticket do
no_attributes? true
# this says that there is no matching source_attribute and destination_attribute on this relationship
filter expr(status == :open and severity >= parent(severity_threshold))
end
```
@ -270,7 +249,7 @@ When referencing related values in filters, if the reference is a `has_one` or `
### Referencing aggregates and calculations
Aggregates are simple, as all aggregates can be referenced in filter expressions (if you are using a data layer that supports it).
Aggregates are simple, as all aggregates can be referenced in filter expressions (if you are using a data layer that supports aggregates).
For calculations, only those that define an expression can be referenced in other expressions.

View file

@ -86,7 +86,7 @@ See the [Identities guide](/documentation/topics/resources/identities.md) for mo
Notifiers are modules that are called for each action that occurs on a resource (except generic actions). They are called at the end of transactions, meaning that if a notifier is called, it is guaranteed that the action they pertain to has completed successfully.
See the [Notifiers guide](/documentation/topics/notifiers.md) for more.
See the [Notifiers guide](/documentation/topics/resources/notifiers.md) for more.
## Policy
@ -119,4 +119,4 @@ See the [Resource DSL docs](dsl-ash-resource.html) for DSL documentation.
Multitenancy is the siloing of your app's data into discrete non-overlapping groups, typically by customer or organization (the tenant). Ash supports multitenancy both at the code level and the data layer level (depending on the data layer; for example, AshPostgres uses schemas to fully separate data per tenant.)
See the [Multitenancy guide](/documentation/topics/multitenancy.md) for more.
See the [Multitenancy guide](/documentation/topics/advanced/multitenancy.md) for more.

View file

@ -82,4 +82,4 @@ calculate :grade, :decimal, expr(
)
```
See the [Expressions guide](/documentation/topics/expressions.md#inline-aggregates) for more.
See the [Expressions guide](/documentation/topics/reference/expressions.md#inline-aggregates) for more.

View file

@ -18,7 +18,7 @@ calculations do
end
```
See the [Expressions guide](/documentation/topics/expressions.md) for more.
See the [Expressions guide](/documentation/topics/reference/expressions.md) for more.
### Module Calculations

View file

@ -2,15 +2,18 @@
## Built-in Notifiers
- PubSub: `Ash.Notifier.PubSub`
Ash comes with a builtin pub_sub notifier: `Ash.Notifier.PubSub`. See the module documentation for more.
## Creating a notifier
## Creating your own notifier
A notifier is a simple extension that must implement a single callback `notify/1`. Notifiers do not have to implement an Ash DSL extension, but they may in order to configure how that notifier should behave. See `Ash.Notifier.Notification` for the currently available fields. Notifiers should not do anything intensive synchronously. If any heavy work needs to be done, they should delegate to something else to handle the notification, like sending it to a GenServer or GenStage.
Eventually, there may be built in notifiers that will make setting up a GenStage that reacts to your resource changes easy. Until then, you'll have to write your own.
A notifier is a simple extension that must implement a single callback `notify/1`. Notifiers do not have to implement an Ash DSL extension, but they may in order to configure how that notifier should behave. See `Ash.Notifier.Notification` for the currently available fields on a notification.
For more information on creating a DSL extension to configure your notifier, see the docs for `Spark.Dsl.Extension`.
> ### notifier performance {: .warning}
>
> Notifiers should not do intensive synchronous work. If any heavy work needs to be done, they should delegate to something else to handle the notification, like sending it to a GenServer or GenStage.
### Example notifier
```elixir

View file

@ -35,9 +35,9 @@ defmodule Ash.Domain.Dsl do
timeout: [
type: :timeout,
doc: """
The default timeout to use for requests using this domain. See the [timeouts guide](/documentation/topics/timeouts.md) for more.
The default timeout in milliseconds to use for requests using this domain. See the [timeouts guide](/documentation/topics/timeouts.md) for more.
""",
default: 30_000
default: :timer.seconds(30)
],
trace_name: [
type: :string,

View file

@ -4,11 +4,7 @@ defmodule Ash.Notifier.PubSub do
@publish %Spark.Dsl.Entity{
name: :publish,
target: Ash.Notifier.PubSub.Publication,
describe: """
Configure a given action to publish its results over a given topic.
See `Ash.Notifier.PubSub` and the [Notifiers](/documentation/topics/notifiers.md) guides for more.
""",
describe: "Configure a given action to publish its results over a given topic.",
examples: [
"publish :create, \"created\"",
"""
@ -23,10 +19,7 @@ defmodule Ash.Notifier.PubSub do
name: :publish_all,
target: Ash.Notifier.PubSub.Publication,
describe: """
Works just like `publish`, except that it takes a type
and publishes all actions of that type
See `Ash.Notifier.PubSub` and the [Notifiers](/documentation/topics/notifiers.md) guides for more.
Works the same as `publish`, except that it takes a type and publishes all actions of that type.
""",
examples: [
"publish_all :create, \"created\""
@ -39,8 +32,6 @@ defmodule Ash.Notifier.PubSub do
name: :pub_sub,
describe: """
A section for configuring how resource actions are published over pubsub
See `Ash.Notifier.PubSub` and the [Notifiers](/documentation/topics/notifiers.md) guide for more.
""",
examples: [
"""
@ -91,20 +82,19 @@ defmodule Ash.Notifier.PubSub do
@sections [@pub_sub]
@moduledoc """
Ash includes a builtin notifier to help you publish events over any kind of pub-sub pattern.
This is plug and play with `Phoenix.PubSub`, but could be used with any pubsub pattern.
A builtin notifier to help you publish events over any kind of pub-sub tooling.
This is plug and play with `Phoenix.PubSub`, but could be used with any pubsub system.
You configure a module that defines a `broadcast/3` function, and then add some "publications"
which configure under what conditions an event should be sent and what the topic should be.
For the full DSL spec see `Ash.Notifier.PubSub`
## Debugging PubSub
It can be quite frustrating when setting up pub_sub when everything appears to be set up properly, but
you aren't receiving events. This usually means some kind of mismatch between the event names produced
by the resource/config of your publications, and you can use the following flag to display debug
information about pub sub events coming from `Ash.Notifier.PubSub`
information about all pub sub events.
```elixir
config :ash, :pub_sub, debug?: true

View file

@ -1363,7 +1363,7 @@ defmodule Ash.Resource.Dsl do
To specify a tenant, use `Ash.Query.set_tenant/2` or
`Ash.Changeset.set_tenant/2` before passing it to an operation.
See the [multitenancy guide](/documentation/topics/multitenancy.md)
See the [multitenancy guide](/documentation/topics/advanced/multitenancy.md)
""",
examples: [
"""

65
mix.exs
View file

@ -47,12 +47,15 @@ defmodule Ash.MixProject do
"documentation/topics/about_ash/design-principles.md",
"documentation/topics/about_ash/contributing-to-ash.md",
"documentation/topics/resources/attributes.md",
"documentation/topics/resources/embedded-resources.md",
"documentation/topics/resources/code-interfaces.md",
"documentation/topics/resources/identities.md",
"documentation/topics/resources/relationships.md",
"documentation/topics/resources/calculations.md",
"documentation/topics/resources/aggregates.md",
"documentation/topics/resources/validations.md",
"documentation/topics/resources/changes.md",
"documentation/topics/resources/code-interfaces.md",
"documentation/topics/resources/embedded-resources.md",
"documentation/topics/resources/identities.md",
"documentation/topics/resources/notifiers.md",
"documentation/topics/actions/actions.md",
"documentation/topics/actions/read-actions.md",
"documentation/topics/actions/create-actions.md",
@ -60,27 +63,19 @@ defmodule Ash.MixProject do
"documentation/topics/actions/destroy-actions.md",
"documentation/topics/actions/generic-actions.md",
"documentation/topics/actions/manual-actions.md",
"documentation/topics/advanced/reactor.md",
"documentation/topics/advanced/monitoring.md",
"documentation/topics/advanced/timeouts.md",
"documentation/topics/advanced/multitenancy.md",
"documentation/topics/development/project-structure.md",
"documentation/topics/development/testing.md",
"documentation/topics/development/development-utilities.md",
"documentation/topics/development/upgrading-to-3.0.md",
"documentation/topics/security/actors-and-authorization.md",
"documentation/topics/security/sensitive-data.md",
"CHANGELOG.md",
# below this line under review
"documentation/how_to/handle-errors.md",
"documentation/how_to/structure-your-project.md",
"documentation/how_to/use-without-data-layers.md",
"documentation/topics/resources/aggregates.md",
"documentation/topics/resources/calculations.md",
"documentation/topics/extending-resources.md",
"documentation/topics/expressions.md",
"documentation/topics/reference/glossary.md",
"documentation/topics/monitoring.md",
"documentation/topics/multitenancy.md",
"documentation/topics/notifiers.md",
"documentation/topics/security/policies.md",
"documentation/topics/reactor.md",
"documentation/topics/testing.md",
"documentation/topics/timeouts.md",
"documentation/topics/reference/glossary.md",
"documentation/topics/reference/expressions.md",
"documentation/dsls/DSL:-Ash.Resource.md",
"documentation/dsls/DSL:-Ash.Domain.md",
"documentation/dsls/DSL:-Ash.Notifier.PubSub.md",
@ -88,7 +83,12 @@ defmodule Ash.MixProject do
"documentation/dsls/DSL:-Ash.DataLayer.Ets.md",
"documentation/dsls/DSL:-Ash.DataLayer.Mnesia.md",
"documentation/dsls/DSL:-Ash.Reactor.md",
"documentation/dsls/DSL:-Ash.DataLayer.Mnesia.md"
"documentation/dsls/DSL:-Ash.DataLayer.Mnesia.md",
"CHANGELOG.md",
# below this line under review
"documentation/how_to/handle-errors.md",
"documentation/how_to/use-without-data-layers.md",
"documentation/topics/extending-resources.md"
],
groups_for_extras: [
"Start Here": [
@ -105,7 +105,8 @@ defmodule Ash.MixProject do
"documentation/topics/resources/identities.md",
"documentation/topics/resources/relationships.md",
"documentation/topics/resources/validations.md",
"documentation/topics/resources/changes.md"
"documentation/topics/resources/changes.md",
"documentation/topics/resources/notifiers.md"
],
Actions: [
"documentation/topics/actions/actions.md",
@ -122,9 +123,17 @@ defmodule Ash.MixProject do
"documentation/topics/security/sensitive-data.md"
],
Development: [
"documentation/topics/development/project-structure.md",
"documentation/topics/development/testing.md",
"documentation/topics/development/development-utilities.md",
"documentation/topics/development/upgrading-to-3.0.md"
],
Advanced: [
"documentation/topics/advanced/reactor.md",
"documentation/topics/advanced/monitoring.md",
"documentation/topics/timeouts.md",
"documentation/topics/multitenancy.md"
],
"About Ash": [
"documentation/topics/about_ash/what-is-ash.md",
"documentation/topics/about_ash/design-principles.md",
@ -133,31 +142,23 @@ defmodule Ash.MixProject do
],
"How To": [],
Reference: [
"documentation/topics/reference/expressions.md",
"documentation/topics/reference/glossary.md",
"documentation/dsls/DSL:-Ash.Resource.md",
"documentation/dsls/DSL:-Ash.Domain.md",
"documentation/dsls/DSL:-Ash.Reactor.md",
"documentation/dsls/DSL:-Ash.Notifier.PubSub.md",
"documentation/dsls/DSL:-Ash.Policy.Authorizer.md",
"documentation/dsls/DSL:-Ash.DataLayer.Ets.md",
"documentation/dsls/DSL:-Ash.DataLayer.Mnesia.md",
"documentation/dsls/DSL:-Ash.Reactor.md",
"documentation/dsls/DSL:-Ash.DataLayer.Mnesia.md"
],
"Under Review": [
# Documentation below this line is pending review
"documentation/topics/domains.md",
"documentation/how_to/handle-errors.md",
"documentation/how_to/structure-your-project.md",
"documentation/how_to/use-without-data-layers.md",
"documentation/topics/code-interface.md",
"documentation/topics/extending-resources.md",
"documentation/topics/expressions.md",
"documentation/topics/monitoring.md",
"documentation/topics/multitenancy.md",
"documentation/topics/notifiers.md",
"documentation/topics/reactor.md",
"documentation/topics/testing.md",
"documentation/topics/timeouts.md"
"documentation/topics/extending-resources.md"
]
],
skip_undefined_reference_warnings_on: [