docs: greatly improved DSL documentation in hex

This commit is contained in:
Zach Daniel 2023-09-13 22:16:39 -04:00
parent c33f66a4ce
commit 6e2d1fd0ac
63 changed files with 4221 additions and 449 deletions

View file

@ -11,8 +11,8 @@
## ...or adjusted (e.g. use one-line formatter for more compact credo output)
# {:credo, "mix credo --format oneline"},
{:check_formatter, command: "mix spark.formatter --check"}
# {:doctor, false}
{:check_formatter, command: "mix spark.formatter --check"},
{:doctor, false}
## custom new tools may be added (mix tasks or arbitrary commands)
# {:my_mix_task, command: "mix release", env: %{"MIX_ENV" => "prod"}},

View file

@ -125,7 +125,7 @@
{Credo.Check.Refactor.MatchInCondition, false},
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
{Credo.Check.Refactor.Nesting, [max_nesting: 7]},
{Credo.Check.Refactor.Nesting, [max_nesting: 9]},
{Credo.Check.Refactor.UnlessWithElse, []},
{Credo.Check.Refactor.WithClauses, []},

View file

@ -0,0 +1,145 @@
# DSL: Ash.Api.Dsl
Apis are the entrypoints for working with your resources.
Apis may optionally include a list of resources, in which case they can be
used as an `Ash.Registry` in various places. This is for backwards compatibility,
but if at all possible you should define an `Ash.Registry` if you are using an extension
that requires a list of resources. For example, most extensions look for two application
environment variables called `:ash_apis` and `:ash_registries` to find any potential registries
## api
General Api configuration
---
### Examples
```
api do
description """
Resources related to the flux capacitor.
"""
end
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| description | `String.t` | A description for the api. |
## resources
List the resources present in this API
---
* [resource](#resources-resource)
### Examples
```
resources do
registry MyApp.Registry
en
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| allow | `mfa` | | Support a dynamic resource list by providing a callback that checks whether or not the resource should be allowed. |
| allow_unregistered? | `boolean` | false | Whether the Api will support only registered entries or not. |
| registry | `module` | | Configure the registry that contains the resources. It is recommended to use application config for this, to help with compile times. See the quick start guide for more. |
## resources.resource
Introspection Target: `Ash.Api.Dsl.ResourceReference`
A resource present in the API
---
### Examples
```
resource Foo
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| resource | `module` | |
## execution
Options for how requests are executed using this Api
---
### Examples
```
execution do
timeout :timer.seconds(30)
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| timeout | `timeout` | 30000 | The default timeout to use for requests using this API. See the [timeouts guide](/documentation/topics/timeouts.md) for more. |
| 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 |
## authorization
Options for how requests are authorized using this Api. See the [security guide](/documentation/topics/security.md) for more.
---
### Examples
```
authorization do
authorize :by_default
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| require_actor? | `boolean` | false | Requires that an actor has been supplied. |
| authorize | `:always \| :by_default \| :when_requested` | :when_requested | When to run authorization for a given request. |

View file

@ -0,0 +1,37 @@
# DSL: Ash.DataLayer.Ets
An ETS (Erlang Term Storage) backed Ash Datalayer, for testing and lightweight usage.
Remember, this does not have support for transactions! This is not recommended for production
use, especially in multi-user applications. It can, however, be great for prototyping.
## ets
A section for configuring the ets data layer
---
### Examples
```
ets do
# Used in testing
private? true
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| private? | `boolean` | false | Sets the ets table protection to private, and scopes it to only this process. The table name will not be used directly if this is true, to allow multiple processes to use this resource separately. |
| table | `atom` | | The name of the table. Defaults to the resource name. |

View file

@ -0,0 +1,40 @@
# DSL: Ash.DataLayer.Mnesia
An Mnesia backed Ash Datalayer.
In your application initialization, you will need to call `Mnesia.create_schema([node()])`.
Additionally, you will want to create your mnesia tables there.
This data layer is *unoptimized*, fetching all records from a table and filtering them
in memory. For that reason, it is not recommended to use it with large amounts of data. It can be
great for prototyping or light usage, though.
## mnesia
A section for configuring the mnesia data layer
---
### Examples
```
mnesia do
table :custom_table
end
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| table | `atom` | The table name to use, defaults to the name of the resource |

View file

@ -0,0 +1,549 @@
# DSL: Ash.Flow.Dsl
The built in flow DSL.
## Halting
Steps can be halted, which will stop the flow from continuing and return a halted flow. To attach a specific reason, use a `halt_reason`.
If you need more complex halting logic, then you'd want to use a custom step, and return `{:error, Ash.Error.Flow.Halted.exception(...)}`
## flow
Details about the flow itself, like description and the successful return type.
---
* [argument](#flow-argument)
### Examples
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| api | `module` | An api to use by default when calling actions |
| description | `String.t` | A description of the flow |
| trace_name | `String.t` | The name to use when creating traces. Defaults to the short name. |
| short_name | `atom` | A short name to use for the flow. Defaults to the last to parts of the module name, underscored. |
| returns | ``any`` | The step or step that should constitute the return value. |
## flow.argument
Introspection Target: `Ash.Flow.Argument`
An argument to be passed into the flow
---
### Examples
```
argument :params, :map do
default %{}
end
```
```
argument :retries, :integer do
allow_nil? false
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| name | `atom` | | The name to use for the argument |
| type | `module` | | The type of the argument. See `Ash.Type` for more. |
| default | `(-> any) \| mfa \| any()` | | A default value to use for the argument if not provided |
| allow_nil? | `boolean` | true | Whether or not the argument value may be nil |
| constraints | `Keyword.t` | [] | Constraints to provide to the type when casting the value. See the type's documentation for more information. |
## steps
---
* [map](#steps-map)
* [branch](#steps-branch)
* [transaction](#steps-transaction)
* [create](#steps-create)
* [debug](#steps-debug)
* [update](#steps-update)
* [destroy](#steps-destroy)
* [validate](#steps-validate)
* [read](#steps-read)
* [run_flow](#steps-run_flow)
* [custom](#steps-custom)
### Examples
```
steps do
# invokes a create action
create :create_post, MyApp.Post, :create
end
```
## steps.map
Introspection Target: `Ash.Flow.Step.Map`
Runs a set of steps for each item in a provided list.
---
### Examples
```
map :create_users, range(1, arg(:count)) do
output :create_user
create :create_user, Org, :create do
input %{
first_name: {Faker.Person, :first_name, []},
last_name: {Faker.Person, :last_name, []}
}
end
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| over | ``any`` | | The value to be iterated over. Will be available inside the `map` step as `element(:map_step_name)` |
| output | `atom` | | Which step to use when constructing the output list. Defaults to the last step. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
## steps.branch
Introspection Target: `Ash.Flow.Step.Branch`
Runs a set of steps based on a given value.
---
### Examples
```
branch :create_users, result(:create_users?) do
output :create_user
create :create_user, Org, :create do
input %{
first_name: {Faker.Person, :first_name, []},
last_name: {Faker.Person, :last_name, []}
}
end
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| condition | ``any`` | | A template that must evaluate to `true` for the branch to be executed. |
| output | `atom` | | Which step to use as the output. Defaults to the last step. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
## steps.transaction
Introspection Target: `Ash.Flow.Step.Transaction`
Runs a set of steps in a transaction.
---
### Examples
```
transaction :create_users do
create :create_user, User, :create do
input %{
first_name: {Faker.Person, :first_name, []},
last_name: {Faker.Person, :last_name, []}
}
end
create :create_user, Org, :create do
input %{
first_name: {Faker.Person, :first_name, []},
last_name: {Faker.Person, :last_name, []}
}
end
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| output | ``any`` | | Which step or steps to use when constructing the output. Defaults to the last step. |
| timeout | `timeout` | | A timeout to apply to the transaction. |
| resource | `module \| list(module)` | | The Ash resource to use for the transaction. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
## steps.create
Introspection Target: `Ash.Flow.Step.Create`
Declares a step that will call a create action on a resource.
---
### Examples
```
create :create_post, MyApp.Post, :create
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| upsert? | `boolean` | false | Wether or not this action is always an upsert. |
| upsert_identity | `atom` | | The identity to use for the upsert. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
| resource | ``any`` | | The resource to call the action on. |
| action | ``any`` | | The action to call on the resource. |
| api | ``any`` | | The api to use when calling the action. Defaults to the api set in the `flow` section. |
| tenant | ``any`` | | A tenant to use for the operation. May be a template or a literal value. |
| input | ``any`` | | A template for the input. |
## steps.debug
Introspection Target: `Ash.Flow.Step.Debug`
Declares a step that will inspect its input and provide
additional debug information.
---
### Examples
```
debug :show_some_information do
input %{post: result(:create_post)}
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| input | ``any`` | | A template for the input. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
## steps.update
Introspection Target: `Ash.Flow.Step.Update`
Declares a step that will call a update action on a resource.
---
### Examples
```
update :update_post, MyApp.Post, :update do
record result(:get_post)
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| record | ``any`` | | The record to be updated, can use template helpers, e.g `result(:step_name)`. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
| resource | ``any`` | | The resource to call the action on. |
| action | ``any`` | | The action to call on the resource. |
| api | ``any`` | | The api to use when calling the action. Defaults to the api set in the `flow` section. |
| tenant | ``any`` | | A tenant to use for the operation. May be a template or a literal value. |
| input | ``any`` | | A template for the input. |
## steps.destroy
Introspection Target: `Ash.Flow.Step.Destroy`
Declares a step that will call a destroy action on a resource.
---
### Examples
```
destroy :destroy_post, MyApp.Post, :destroy
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| record | ``any`` | | The record to be updated, can use template helpers, e.g `result(:step_name)`. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
| resource | ``any`` | | The resource to call the action on. |
| action | ``any`` | | The action to call on the resource. |
| api | ``any`` | | The api to use when calling the action. Defaults to the api set in the `flow` section. |
| tenant | ``any`` | | A tenant to use for the operation. May be a template or a literal value. |
| input | ``any`` | | A template for the input. |
## steps.validate
Introspection Target: `Ash.Flow.Step.Update`
Validates some input against an action.
---
### Examples
```
validate :update_post, MyApp.Post, :update do
record result(:get_post)
only_keys [:name]
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| record | ``any`` | | The record to be created/updated/destroyed. If the value is `nil` and would be required by the action type, the step is skipped and `nil` is the result of the step. |
| only_keys | `list(atom \| list(atom))` | | A list of keys or paths to keys that should be validated. Others will be ignored, and errors generated for other fields will be ignored. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
| resource | ``any`` | | The resource to call the action on. |
| action | ``any`` | | The action to call on the resource. |
| api | ``any`` | | The api to use when calling the action. Defaults to the api set in the `flow` section. |
| tenant | ``any`` | | A tenant to use for the operation. May be a template or a literal value. |
| input | ``any`` | | A template for the input. |
## steps.read
Introspection Target: `Ash.Flow.Step.Read`
Declares a step that will call a read action on a resource.
---
### Examples
```
read :destroy_post, MyApp.Post, :destroy
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| get? | `boolean` | false | Whether or not read action is expected to return a single result or `nil`. Set to `true` automatically if `get? true`. |
| not_found_error? | `boolean` | true | Whether or not finding no record should result in a not found error |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
| resource | ``any`` | | The resource to call the action on. |
| action | ``any`` | | The action to call on the resource. |
| api | ``any`` | | The api to use when calling the action. Defaults to the api set in the `flow` section. |
| tenant | ``any`` | | A tenant to use for the operation. May be a template or a literal value. |
| input | ``any`` | | A template for the input. |
## steps.run_flow
Introspection Target: `Ash.Flow.Step.RunFlow`
Runs another flow as part of the current flow.
The return value of the step is the return value of the flow.
---
### Examples
```
run_flow :get_org, GetOrgByName do
input %{
name: arg(:org_name)
}
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| flow | `atom` | | The flow to run. |
| input | ``any`` | | A template for the input. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |
## steps.custom
Introspection Target: `Ash.Flow.Step.Custom`
Runs a custom step module.
See `Ash.Flow.Step` for the necessary callbacks and more information.
---
### Examples
```
custom :do_custom_thing, MyApp.DoCustomThing do
input %{...}
end
```
```
custom :do_custom_thing, {MyApp.DoCustomThing, opt1: :foo, opt2: :bar} do
input %{...}
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| input | ``any`` | | A template for the input. |
| custom | `(any, any -> any) \| module` | | The module that implements the step behaviour. Also accepts a 2 argument function that takes the input and the context. |
| async? | `boolean` | false | Whether or not this step can be run outside of the current process. |
| name | `atom` | | The name of the step. Will be used when expressing dependencies, and step inputs. |
| short_name | `String.t` | | Set a short name for the step. Will be used when building things like mermaid charts. |
| wait_for | ``any`` | | Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited. |
| touches_resources | `list(atom)` | | A list of resources touched by any custom logic in this step. This is used in the case that this step is run in a transaction. This is primarily only needed for `custom` steps. |
| halt_if | ``any`` | | Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more. |
| halt_reason | ``any`` | :halted | Configures the reason for the `halt_if` clause. |
| description | `String.t` | | A description for the step. |

View file

@ -0,0 +1,110 @@
# DSL: Ash.Notifier.PubSub
A pubsub notifier extension.
## pub_sub
A section for configuring how resource actions are published over pubsub
See the [PubSub](/documentation/topics/pub_sub.md) and [Notifiers](/documentation/topics/notifiers.md) guide for more.
---
* [publish](#pub_sub-publish)
* [publish_all](#pub_sub-publish_all)
### Examples
```
pub_sub do
module MyEndpoint
prefix "post"
broadcast_type :phoenix_broadcast
publish :destroy, ["foo", :id]
publish :update, ["bar", :name] event: "name_change"
publish_all :create, "created"
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| module | `atom` | | The module to call `broadcast/3` on e.g module.broadcast(topic, event, message). |
| prefix | `String.t` | | A prefix for all pubsub messages, e.g `users`. A message with `created` would be published as `users:created` |
| broadcast_type | `:notification \| :phoenix_broadcast \| :broadcast` | :notification | What shape the event payloads will be in. See |
| name | `atom` | | A named pub sub to pass as the first argument to broadcast. |
## pub_sub.publish
Introspection Target: `Ash.Notifier.PubSub.Publication`
Configure a given action to publish its results over a given topic.
See the [PubSub](/documentation/topics/pub_sub.md) and [Notifiers](/documentation/topics/notifiers.md) guides for more.
---
### Examples
```
publish :create, "created"
```
```
publish :assign, "assigned"
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| action | `atom` | The name of the action that should be published |
| topic | ``any`` | The topic to publish |
| event | `String.t` | The name of the event to publish. Defaults to the action name |
| dispatcher | `atom` | The module to use as a dispatcher. If none is set, the pubsub module provided is used. |
## pub_sub.publish_all
Introspection Target: `Ash.Notifier.PubSub.Publication`
Works just like `publish`, except that it takes a type
and publishes all actions of that type
See the [PubSub](/documentation/topics/pub_sub.md) and [Notifiers](/documentation/topics/notifiers.md) guides for more.
---
### Examples
```
publish_all :create, "created"
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| type | `:create \| :update \| :destroy` | Publish on all actions of a given type |
| action | `atom` | The name of the action that should be published |
| topic | ``any`` | The topic to publish |
| event | `String.t` | The name of the event to publish. Defaults to the action name |
| dispatcher | `atom` | The module to use as a dispatcher. If none is set, the pubsub module provided is used. |

View file

@ -0,0 +1,725 @@
# DSL: Ash.Policy.Authorizer
An authorization extension for ash resources.
To add this extension to a resource, add it to the list of `authorizers` like so:
```elixir
use Ash.Resource,
...,
authorizers: [
Ash.Policy.Authorizer
]
```
A resource can be given a set of policies, which are enforced on each call to a resource action.
For reads, policies can be configured to filter out data that the actor shouldn't see, as opposed to
resulting in a forbidden error.
See the [policies guide](/documentation/topics/policies.md) for practical examples.
Policies are solved/managed via a boolean satisfiability solver. To read more about boolean satisfiability,
see this page: https://en.wikipedia.org/wiki/Boolean_satisfiability_problem. At the end of
the day, however, it is not necessary to understand exactly how Ash takes your
authorization requirements and determines if a request is allowed. The
important thing to understand is that Ash may or may not run any/all of your
authorization rules as they may be deemed unnecessary. As such, authorization
checks should have no side effects. Ideally, the checks built-in to ash should
cover the bulk of your needs.
## policies
A section for declaring authorization policies.
Each policy that applies must pass independently in order for the
request to be authorized.
See the [policies guide](/documentation/topics/policies.md) for more.
---
* [policy](#policies-policy)
* authorize_if
* forbid_if
* authorize_unless
* forbid_unless
* [bypass](#policies-bypass)
* authorize_if
* forbid_if
* authorize_unless
* forbid_unless
### Examples
```
policies do
# Anything you can use in a condition, you can use in a check, and vice-versa
# This policy applies if the actor is a super_user
# Additionally, this policy is declared as a `bypass`. That means that this check is allowed to fail without
# failing the whole request, and that if this check *passes*, the entire request passes.
bypass actor_attribute_equals(:super_user, true) do
authorize_if always()
end
# This will likely be a common occurrence. Specifically, policies that apply to all read actions
policy action_type(:read) do
# unless the actor is an active user, forbid their request
forbid_unless actor_attribute_equals(:active, true)
# if the record is marked as public, authorize the request
authorize_if attribute(:public, true)
# if the actor is related to the data via that data's `owner` relationship, authorize the request
authorize_if relates_to_actor_via(:owner)
end
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| default_access_type | `:strict \| :filter \| :runtime` | :filter | The default access type of policies for this resource. |
## policies.policy
Introspection Target: `Ash.Policy.Policy`
A policy has a name, a condition, and a list of checks.
Checks apply logically in the order they are specified, from top to bottom.
If no check explicitly authorizes the request, then the request is forbidden.
This means that, if you want to "blacklist" instead of "whitelist", you likely
want to add an `authorize_if always()` at the bottom of your policy, like so:
```elixir
policy action_type(:read) do
forbid_if not_logged_in()
forbid_if user_is_denylisted()
forbid_if user_is_in_denylisted_group()
authorize_if always()
end
```
If the policy should always run, use the `always()` check, like so:
```elixir
policy always() do
...
end
```
See the [policies guide](/documentation/topics/policies.md) for more.
---
* [authorize_if](#policies-policy-authorize_if)
* [forbid_if](#policies-policy-forbid_if)
* [authorize_unless](#policies-policy-authorize_unless)
* [forbid_unless](#policies-policy-forbid_unless)
### Examples
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| description | `String.t` | A description for the policy, used when explaining authorization results |
| access_type | `:strict \| :filter \| :runtime` | What portion of the checks inside the policy are allowed to run. See the guide for more. |
| condition | ``any`` | A check or list of checks that must be true in order for this policy to apply. |
## policies.policy.authorize_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_if logged_in()
```
```
authorize_if actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.policy.forbid_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_if not_logged_in()
```
```
forbid_if actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.policy.authorize_unless
Introspection Target: `Ash.Policy.Check`
If the check is false, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_unless not_logged_in()
```
```
authorize_unless actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.policy.forbid_unless
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_unless logged_in()
```
```
forbid_unless actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.bypass
Introspection Target: `Ash.Policy.Policy`
A policy that, if passed, will skip all following policies. If failed, authorization moves on to the next policy
---
* [authorize_if](#policies-bypass-authorize_if)
* [forbid_if](#policies-bypass-forbid_if)
* [authorize_unless](#policies-bypass-authorize_unless)
* [forbid_unless](#policies-bypass-forbid_unless)
### Examples
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| description | `String.t` | A description for the policy, used when explaining authorization results |
| access_type | `:strict \| :filter \| :runtime` | What portion of the checks inside the policy are allowed to run. See the guide for more. |
| condition | ``any`` | A check or list of checks that must be true in order for this policy to apply. |
## policies.bypass.authorize_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_if logged_in()
```
```
authorize_if actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.bypass.forbid_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_if not_logged_in()
```
```
forbid_if actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.bypass.authorize_unless
Introspection Target: `Ash.Policy.Check`
If the check is false, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_unless not_logged_in()
```
```
authorize_unless actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## policies.bypass.forbid_unless
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_unless logged_in()
```
```
forbid_unless actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies
---
* [field_policy_bypass](#field_policies-field_policy_bypass)
* authorize_if
* forbid_if
* authorize_unless
* forbid_unless
* [field_policy](#field_policies-field_policy)
* authorize_if
* forbid_if
* authorize_unless
* forbid_unless
### Examples
```
field_policies do
field_policy :admin_only_field do
authorize_if actor_attribute_equals(:admin, true)
end
end
```
```
# Example of denylist style
field_policies do
field_policy [:sensitive, :fields] do
authorize_if actor_attribute_equals(:admin, true)
end
field_policy :* do
authorize_if always()
end
end
```
## field_policies.field_policy_bypass
Introspection Target: `Ash.Policy.FieldPolicy`
A field policy that, if passed, will skip all following field policies for that field or fields. If failed, field authorization moves on to the next policy
---
* [authorize_if](#field_policies-field_policy_bypass-authorize_if)
* [forbid_if](#field_policies-field_policy_bypass-forbid_if)
* [authorize_unless](#field_policies-field_policy_bypass-authorize_unless)
* [forbid_unless](#field_policies-field_policy_bypass-forbid_unless)
### Examples
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| description | `String.t` | A description for the policy, used when explaining authorization results |
| fields | `list(atom) \| atom` | The field or fields that the policy applies to. |
| condition | ``any`` | A check or list of checks that must be true in order for this field policy to apply. If not specified, it always applies. |
## field_policies.field_policy_bypass.authorize_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_if logged_in()
```
```
authorize_if actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy_bypass.forbid_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_if not_logged_in()
```
```
forbid_if actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy_bypass.authorize_unless
Introspection Target: `Ash.Policy.Check`
If the check is false, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_unless not_logged_in()
```
```
authorize_unless actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy_bypass.forbid_unless
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_unless logged_in()
```
```
forbid_unless actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy
Introspection Target: `Ash.Policy.FieldPolicy`
Field policies behave similarly to policies. See `d:Ash.Policy.Authorizer.field_policies`
for more.
---
* [authorize_if](#field_policies-field_policy-authorize_if)
* [forbid_if](#field_policies-field_policy-forbid_if)
* [authorize_unless](#field_policies-field_policy-authorize_unless)
* [forbid_unless](#field_policies-field_policy-forbid_unless)
### Examples
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| description | `String.t` | A description for the policy, used when explaining authorization results |
| fields | `list(atom) \| atom` | The field or fields that the policy applies to. |
| condition | ``any`` | A check or list of checks that must be true in order for this field policy to apply. If not specified, it always applies. |
## field_policies.field_policy.authorize_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_if logged_in()
```
```
authorize_if actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy.forbid_if
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_if not_logged_in()
```
```
forbid_if actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy.authorize_unless
Introspection Target: `Ash.Policy.Check`
If the check is false, the request is authorized, otherwise run remaining checks.
---
### Examples
```
authorize_unless not_logged_in()
```
```
authorize_unless actor_attribute_matches_record(:group, :blacklisted_groups)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |
## field_policies.field_policy.forbid_unless
Introspection Target: `Ash.Policy.Check`
If the check is true, the request is forbidden, otherwise run remaining checks.
---
### Examples
```
forbid_unless logged_in()
```
```
forbid_unless actor_attribute_matches_record(:group, :group)
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| check | ``any`` | The check to run. See `Ash.Policy.Check` for more. |
| name | `String.t` | A short name or description for the check, used when explaining authorization results |

View file

@ -0,0 +1,58 @@
# DSL: Ash.Registry.Dsl
A small DSL for declaring an `Ash.Registry`. Not generally necessary any longer.
`Ash.Registry` can be used generically, but the main way it is used in Ash is to provide a compile-time registry for an Ash Api.
## entries
List the entries present in this registry
---
* [entry](#entries-entry)
### Examples
```
entries do
entry MyApp.User
entry MyApp.Post
entry MyApp.Comment
end
```
### Reference
| Name | Type | Default | Docs |
| --- | --- | --- | --- |
| warn_on_empty? | `boolean` | true | Set to `false` to ignore warnings about an empty registry |
## entries.entry
Introspection Target: `Ash.Registry.Entry`
A reference to an ash module (typically a resource)
---
### Examples
```
entry MyApp.User
```
### Reference
| Name | Type | Docs |
| --- | --- | --- |
| entry | `atom` | The referenced module |

File diff suppressed because it is too large Load diff

View file

@ -73,7 +73,7 @@ calculations do
end
```
See the documentation for the calculations section in `Ash.Resource.Dsl` and the `Ash.Calculation` docs for more information.
See the documentation for the calculations section in [Resource DSL docs](dsl-ash-resource.html#calculations) and the `Ash.Calculation` docs for more information.
The calculations declared on a resource allow for declaring a set of named calculations that can be used by extensions.
They can also be loaded in the query using `Ash.Query.load/2`, or after the fact using `c:Ash.Api.load/3`. Calculations declared on the resource will be keys in the resource's struct.

View file

@ -89,3 +89,16 @@ User.full_name(user)
```
This allows for running calculations without an instance of a resource, i.e `Api.load(user, :full_name)`
By default, configured args will be provided for any matching named reference *or* argument. This is normally fine, but in the case that you have an argument and a reference with the same name, you can specify it by supplying `{:arg, :name}` and `{:ref, :name}`. For example:
```elixir
define_calculation :id_matches, args: [{:arg, :id}, {:ref, :id}]
```
To make arguments optional, wrap them in `{:optional, ..}`, for example:
```elixir
define_calculation :id_matches, args: [{:arg, :id}, {:optional, {:ref, :id}}]
```

View file

@ -118,7 +118,7 @@ The central concept in Ash, a resource is a domain model object in your system,
It is not a strict requirement that resources contain data - they can be used purely to create a standard interface for performing tasks - but in practice, most resources will be used to manage data.
See `Ash.Resource.Dsl` for more information.
See the [Resource DSL docs](dsl-ash-resource.html) for DSL documentation.
## Tenant

View file

@ -73,5 +73,18 @@ Would produce the following messages, given a `team_id` of 1, a `tenant` of `org
Phoenix expects a specific shape of data to be broadcasted, and since it is so often used with Ash, instead of making you define your own notifier that creates the `%Phoenix.Socket.Broadcast` struct and publishes it, Ash has an option to do that automatically, via
```elixir
broadcast_type: :phoenix_broadcast
broadcast_type :phoenix_broadcast
```
## Named Pubsub modules
If you are using a phoenix `Endpoint` module for pubsub then this is unnecessary. If you want to use a custom pub sub started with something like `{Phoenix.PubSub, name: MyName}`, then you can provide `MyName` to
here.
## Broadcast Types
Configured with `broadcast_type`.
- `:notification` just sends the notification
- `:phoenix_broadcast` sends a `%Phoenix.Socket.Broadcast{}` (see above)
- `:broadcast` sends `%{topic: (topic), event: (event), notification: (notification)}`

View file

@ -271,3 +271,20 @@ followers =
# Will load followers and followers of those followers
YourApi.load(users, followers: followers)
```
## no_attributes? true
This can be very useful when combined with multitenancy. Specifically, if you have a tenant resource like `Organization`,
you can use `no_attributes?` to do things like `has_many :employees, Employee, no_attributes?: true`, which lets you avoid having an
unnecessary `organization_id` field on `Employee`. The same works in reverse: `has_one :organization, Organization, no_attributes?: true`
allows relating the employee to their organization.
Some important caveats here:
1. You can still manage relationships from one to the other, but "relate" and "unrelate"
will have no effect, because there are no fields to change.
2. Loading the relationship on a list of resources will not behave as expected in all circumstances involving multitenancy. For example,
if you get a list of `Organization` and then try to load `employees`, you would need to set a single tenant on the load query, meaning
you'll get all organizations back with the set of employees from one tenant. This could eventually be solved, but for now it is considered an
edge case.

View file

@ -4,6 +4,10 @@
A great thing to do early on is to be explicit about your security configuration. To that end, once you've read this guide, we highly recommend that you place the configuration found at the bottom of your guide into your api modules, even if you are simply setting them to their default values. Especially the `authorize` option.
## Sensitive Attributes
Using `sensitive? true` will cause the argument to be `** Redacted **` from the resource when logging or inspecting. In filter statements, any value used in the same expression as a sensitive attribute will also be redacted. For example, you might see: `email == "** Redacted **"` in a filter statement if `email` is marked as sensitive.
## Authorization
Authorization in Ash is done via authorizers. Generally, you won't need to create your own authorizer, as the builtin policy authorizer `Ash.Policy.Authorizer` should work well for any use case. Authorization is performed with a given actor and a query or changeset.

View file

@ -26,6 +26,7 @@ In this guide we will:
- [Install Elixir](https://elixir-lang.org/install.html)
- [Philosophy Guide](/documentation/tutorials/philosophy.md)
- [Using Hexdocs](/documentation/tutorials/using-hexdocs.md)
## Requirements

View file

@ -0,0 +1,37 @@
# Using Hexdocs
Ash is split across various packages. Each package has its own documentation. However, there is a global documentation search available at https://ash-hq.org. Do use it, use `Ctrl-K` or `Cmd-K` on that site.
## Packages
- [Ash](https://hexdocs.pm/ash): The core framework, providing all the features and goodies that power and enable the rest of the ecosystem.
- [AshPostgres](https://hexdocs.pm/ash_postgres): A PostgreSQL data layer for Ash resources, allowing for rich query capabilities and seamless persistence.
- [AshPhoenix](https://hexdocs.pm/ash_phoenix): Utilities for using Ash resources with Phoenix Framework, from building forms to running queries in sockets & LiveViews.
- [AshGraphql](https://hexdocs.pm/ash_graphql): A GraphQL extension that allows you to build a rich and customizable GraphQL API with minimal configuration required.
- [AshJsonApi](https://hexdocs.pm/ash_json_api): A JSON:API extension that allows you to effortlessly create a JSON:API spec compliant API.
- [AshAuthentication](https://hexdocs.pm/ash_authentication): Provides drop-in support for user authentication with various strategies and tons of customizability.
- [AshAuthenticationPhoenix](https://hexdocs.pm/ash_authentication_phoenix): Phoenix helpers and UI components in support of AshAuthentication.
- [AshStateMachine](https://hexdocs.pm/ash_state_machine): An Ash.Resource extension for building finite state machines.
- [AshCsv](https://hexdocs.pm/ash_csv): A CSV data layer allowing resources to be queried from and persisted in a CSV file.
- [AshDoubleEntry](https://hexdocs.pm/ash_double_entry): A customizable double entry bookkeeping system backed by Ash resources.
- [AshArchival](https://hexdocs.pm/ash_archival): A light-weight resource extension that modifies resources to simulate deletion by setting an `archived_at` attribute.
- [Reactor](https://hexdocs.pm/reactor): Reactor is a dynamic, concurrent, dependency resolving saga orchestrator.
- [Spark](https://hexdocs.pm/spark): The core DSL and extension tooling, allowing for powerful, extensible DSLs with minimal effort.
## DSL documentations
Some helpful tips on using Hex Docs. DSLs are each documented in their own area. Find them in the bottom of the sidebar on the left.
### Searching
#### In the sidebar
When searching for a dsl, prefix your search with `DSL: `. If you know the path
to the DSL you are looking for, use it separated by dots. For example, `DSL: attributes.attribute`. Only five results will show up in the sidebar, so be as specific as possible. If you don't find it, press enter and you will be taken to the search page.
#### In the search page
In the search page, you filter by type. By default, search terms are considered
optional. You can prefix them with + to make them required. Something you would
do to find a specific DSL is to search for `+type:dsl +something`.

View file

@ -35,17 +35,14 @@ defmodule Ash.Api.Dsl do
timeout: [
type: :timeout,
doc: """
The default timeout to use for requests using this API.
See the [timeouts guide](/documentation/topics/timeouts.md) for more.
The default timeout to use for requests using this API. See the [timeouts guide](/documentation/topics/timeouts.md) for more.
""",
default: 30_000
],
trace_name: [
type: :string,
doc: """
The name to use in traces. Defaults to the last part of the module.
See the [monitoring guide](/documentation/topics/monitoring.md) for more
The name to use in traces. Defaults to the last part of the module. See the [monitoring guide](/documentation/topics/monitoring.md) for more
"""
]
]
@ -54,9 +51,7 @@ defmodule Ash.Api.Dsl do
@authorization %Spark.Dsl.Section{
name: :authorization,
describe: """
Options for how requests are authorized using this Api.
See the [security guide](/documentation/topics/security.md) for more.
Options for how requests are authorized using this Api. See the [security guide](/documentation/topics/security.md) for more.
""",
examples: [
"""
@ -81,7 +76,7 @@ defmodule Ash.Api.Dsl do
]
}
defmodule Ash.Api.ResourceReference do
defmodule ResourceReference do
@moduledoc "A resource reference in an api"
defstruct [:resource]
end
@ -92,7 +87,7 @@ defmodule Ash.Api.Dsl do
examples: [
"resource Foo"
],
target: Ash.Api.ResourceReference,
target: ResourceReference,
args: [:resource],
no_depend_modules: [:resource],
schema: [
@ -143,8 +138,6 @@ defmodule Ash.Api.Dsl do
@sections [@api, @resources, @execution, @authorization]
@moduledoc """
A small DSL for declaring APIs
Apis are the entrypoints for working with your resources.
Apis may optionally include a list of resources, in which case they can be
@ -152,19 +145,6 @@ defmodule Ash.Api.Dsl do
but if at all possible you should define an `Ash.Registry` if you are using an extension
that requires a list of resources. For example, most extensions look for two application
environment variables called `:ash_apis` and `:ash_registries` to find any potential registries
<!--- ash-hq-hide-start --> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index(@sections)}
### Docs
#{Spark.Dsl.Extension.doc(@sections)}
<!--- ash-hq-hide-stop --> <!--- -->
"""
use Spark.Dsl.Extension, sections: @sections

View file

@ -36,19 +36,6 @@ defmodule Ash.DataLayer.Ets do
Remember, this does not have support for transactions! This is not recommended for production
use, especially in multi-user applications. It can, however, be great for prototyping.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index([@ets])}
### Docs
#{Spark.Dsl.Extension.doc([@ets])}
<!--- ash-hq-hide-stop--> <!--- -->
"""
use Spark.Dsl.Extension,

View file

@ -31,19 +31,6 @@ defmodule Ash.DataLayer.Mnesia do
This data layer is *unoptimized*, fetching all records from a table and filtering them
in memory. For that reason, it is not recommended to use it with large amounts of data. It can be
great for prototyping or light usage, though.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index([@mnesia])}
### Docs
#{Spark.Dsl.Extension.doc([@mnesia])}
<!--- ash-hq-hide-stop--> <!--- -->
"""
use Spark.Dsl.Extension,

View file

@ -13,6 +13,7 @@ defmodule Ash.Flow.Dsl do
"""
],
target: Ash.Flow.Step.Debug,
recursive_as: :steps,
args: [:name],
schema: Ash.Flow.Step.Debug.schema()
}
@ -27,6 +28,7 @@ defmodule Ash.Flow.Dsl do
create :create_post, MyApp.Post, :create
"""
],
recursive_as: :steps,
no_depend_modules: [:resource, :touches_resources],
modules: [:manual],
target: Ash.Flow.Step.Create,
@ -49,6 +51,7 @@ defmodule Ash.Flow.Dsl do
no_depend_modules: [:resource, :touches_resources],
modules: [:manual],
target: Ash.Flow.Step.Update,
recursive_as: :steps,
args: [:name, :resource, :action],
schema: Ash.Flow.Step.Update.schema()
}
@ -66,6 +69,7 @@ defmodule Ash.Flow.Dsl do
end
"""
],
recursive_as: :steps,
no_depend_modules: [:resource, :touches_resources],
target: Ash.Flow.Step.Update,
args: [:name, :resource, :action],
@ -82,6 +86,7 @@ defmodule Ash.Flow.Dsl do
destroy :destroy_post, MyApp.Post, :destroy
"""
],
recursive_as: :steps,
modules: [:manual],
no_depend_modules: [:resource, :touches_resources],
target: Ash.Flow.Step.Destroy,
@ -99,6 +104,7 @@ defmodule Ash.Flow.Dsl do
read :destroy_post, MyApp.Post, :destroy
"""
],
recursive_as: :steps,
no_depend_modules: [:resource, :touches_resources],
modules: [:manual],
target: Ash.Flow.Step.Read,
@ -120,6 +126,7 @@ defmodule Ash.Flow.Dsl do
}
"""
],
recursive_as: :run_flow,
no_depend_modules: [:resource, :touches_resources],
target: Ash.Flow.Step.RunFlow,
args: [:name, :flow],
@ -145,6 +152,7 @@ defmodule Ash.Flow.Dsl do
end
"""
],
recursive_as: :steps,
no_depend_modules: [:custom, :touches_resources],
target: Ash.Flow.Step.Custom,
args: [:name, :custom],
@ -210,8 +218,6 @@ defmodule Ash.Flow.Dsl do
]
}
@leaf_steps [@create, @debug, @update, @destroy, @validate, @read, @run_flow, @custom]
@transaction %Spark.Dsl.Entity{
name: :transaction,
describe: """
@ -222,7 +228,7 @@ defmodule Ash.Flow.Dsl do
args: [:name, :resource],
recursive_as: :steps,
entities: [
steps: @leaf_steps
steps: []
],
no_depend_modules: [:touches_resources],
examples: [
@ -257,7 +263,7 @@ defmodule Ash.Flow.Dsl do
recursive_as: :steps,
no_depend_modules: [:touches_resources],
entities: [
steps: @leaf_steps
steps: []
],
examples: [
"""
@ -286,7 +292,7 @@ defmodule Ash.Flow.Dsl do
recursive_as: :steps,
no_depend_modules: [:touches_resources],
entities: [
steps: @leaf_steps
steps: []
],
examples: [
"""
@ -318,12 +324,19 @@ defmodule Ash.Flow.Dsl do
"""
],
imports: [Ash.Flow.StepHelpers],
entities:
[
entities: [
@map,
@branch,
@transaction
] ++ @leaf_steps
@transaction,
@create,
@debug,
@update,
@destroy,
@validate,
@read,
@run_flow,
@custom
]
}
@transformers [
@ -340,18 +353,11 @@ defmodule Ash.Flow.Dsl do
@moduledoc """
The built in flow DSL.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
## Halting
### Index
#{Spark.Dsl.Extension.doc_index(@sections)}
### Docs
#{Spark.Dsl.Extension.doc(@sections)}
<!--- ash-hq-hide-stop--> <!--- -->
Steps can be halted, which will stop the flow from continuing and return a halted flow. To attach a specific reason, use a `halt_reason`.
If you need more complex halting logic, then you'd want to use a custom step, and return `{:error, Ash.Error.Flow.Halted.exception(...)}`
"""
use Spark.Dsl.Extension,

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Branch do
@moduledoc false
@moduledoc "Represents a branching set of steps in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:output, :condition, steps: []]
@shared_opts Ash.Flow.Step.shared_opts()

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Create do
@moduledoc false
@moduledoc "Represents a create action step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [
:resource,
:action,

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Custom do
@moduledoc false
@moduledoc "Represents a custom step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:input, :custom, :async?]
@shared_opts Ash.Flow.Step.shared_opts()

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Debug do
@moduledoc false
@moduledoc "Represents a debug step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:input]
@shared_opts Ash.Flow.Step.shared_opts()

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Destroy do
@moduledoc false
@moduledoc "Represents a destroy action step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:resource, :action, :api, :input, :tenant, :record]
@shared_opts Ash.Flow.Step.shared_opts()
@shared_action_opts Ash.Flow.Step.shared_action_opts()
@ -12,9 +12,6 @@ defmodule Ash.Flow.Step.Destroy do
required: true,
doc: """
The record to be updated, can use template helpers, e.g `result(:step_name)`.
If the value is `nil`, the step is skipped and `nil` is the result of the step.
Any other value is used as an input record.
"""
]
]

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Map do
@moduledoc false
@moduledoc "Represents a map grouping of steps in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:over, :element, :output, steps: []]
@shared_opts Ash.Flow.Step.shared_opts()

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Read do
@moduledoc false
@moduledoc "Represents a read action step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [
:resource,
:action,
@ -19,8 +19,7 @@ defmodule Ash.Flow.Step.Read do
get?: [
type: :boolean,
doc: """
Whether or not read action is expected to return a single result or `nil`.
If the action is configured with `get? true` then this is automatically set to `true`.
Whether or not read action is expected to return a single result or `nil`. Set to `true` automatically if `get? true`.
""",
default: false
],

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.RunFlow do
@moduledoc false
@moduledoc "Represents a nested flow step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:input, :flow, :built]
@shared_opts Ash.Flow.Step.shared_opts()

View file

@ -34,10 +34,7 @@ defmodule Ash.Flow.Step do
wait_for: [
type: :any,
doc: """
Ensures that the step happens after the configured step or steps.
This value is just a template that isn't used, except to determine dependencies, so you can
use it like this `wait_for [result(:step_one), result(:step_two)]` or `wait_for result(:step)`.
Ensures that the step happens after the configured step or steps. This is a template who's results are not used, only awaited.
"""
],
touches_resources: [
@ -49,11 +46,7 @@ defmodule Ash.Flow.Step do
halt_if: [
type: :any,
doc: """
Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables.
To attach a specific reason, use a `halt_reason`.
If you need more complex halting logic, then you'd want to use a custom step, and return `{:error, Ash.Error.Flow.Halted.exception(...)}`
Halts the step by emitting an error (with an `Ash.Error.Flow.Halted`). Can use template variables. See the section on Halting for more.
"""
],
halt_reason: [

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Transaction do
@moduledoc false
@moduledoc "Represents steps grouped into a transaction in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:output, :resource, :timeout, steps: []]
@shared_opts Ash.Flow.Step.shared_opts()

View file

@ -1,5 +1,5 @@
defmodule Ash.Flow.Step.Update do
@moduledoc false
@moduledoc "Represents an update action step in an Ash.Flow"
use Ash.Flow.Step.BuiltinStep, [:resource, :action, :api, :input, :tenant, :record]
@shared_opts Ash.Flow.Step.shared_opts()
@shared_action_opts Ash.Flow.Step.shared_action_opts()
@ -12,9 +12,6 @@ defmodule Ash.Flow.Step.Update do
required: true,
doc: """
The record to be updated, can use template helpers, e.g `result(:step_name)`.
If the value is `nil`, the step is skipped and `nil` is the result of the step.
Any other value is used as an input record.
"""
]
]

View file

@ -10,18 +10,13 @@ defmodule Ash.Flow.Step.Validate do
record: [
type: :any,
doc: """
The record to be created/updated/destroyed, if relevant. can use template helpers, e.g `result(:step_name)`.
If the value is `nil` and would be required by the action type, the step is skipped and `nil` is the result of the step.
Any other value is used as an input record.
The record to be created/updated/destroyed. If the value is `nil` and would be required by the action type, the step is skipped and `nil` is the result of the step.
"""
],
only_keys: [
type: {:list, {:or, [:atom, {:list, :atom}]}},
doc: """
If the keys are set, the step will succeed as long as there are no errors for those specific fields.
Additionally, only errors for those keys will be returned.
Use a list for the key if you want to check for an error at a path, and use `:_` to allow anything at that path
A list of keys or paths to keys that should be validated. Others will be ignored, and errors generated for other fields will be ignored.
"""
]
]

View file

@ -75,19 +75,12 @@ defmodule Ash.Notifier.PubSub do
type: {:one_of, [:notification, :phoenix_broadcast, :broadcast]},
default: :notification,
doc: """
What shape the event payloads will be in. `:notification` just sends the notification, `phoenix_broadcast` sends a `%Phoenix.Socket.Broadcast{}`, and `:broadcast`
sends `%{topic: (topic), event: (event), notification: (notification)}`
What shape the event payloads will be in. See
"""
],
name: [
type: :atom,
doc: """
A named pub sub to pass as the first argument to broadcast.
If you are using a phoenix `Endpoint` module for pubsub then this is unnecessary. If you want to use
a custom pub sub started with something like `{Phoenix.PubSub, name: MyName}`, then you can provide `MyName` to
here.
"""
doc: "A named pub sub to pass as the first argument to broadcast."
]
]
}
@ -96,19 +89,6 @@ defmodule Ash.Notifier.PubSub do
@moduledoc """
A pubsub notifier extension.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index(@sections)}
### Docs
#{Spark.Dsl.Extension.doc(@sections)}
<!--- ash-hq-hide-stop--> <!--- -->
"""
use Spark.Dsl.Extension, sections: @sections

View file

@ -1,5 +1,5 @@
defmodule Ash.Notifier.PubSub.Publication do
@moduledoc false
@moduledoc "Represents a configured publication from the pubsub notifier on an Ash.Resource"
defstruct [
:action,

View file

@ -28,9 +28,7 @@ defmodule Ash.Policy.Authorizer do
type: {:custom, __MODULE__, :validate_check, []},
required: true,
doc: """
The check to run.
See `Ash.Policy.Check` for more.
The check to run. See `Ash.Policy.Check` for more.
"""
],
name: [
@ -243,10 +241,8 @@ defmodule Ash.Policy.Authorizer do
],
condition: [
type: {:custom, __MODULE__, :validate_condition, []},
doc: """
A check or list of checks that must be true in order for this field policy to apply.
If not specified, it always applies.
"""
doc:
"A check or list of checks that must be true in order for this field policy to apply. If not specified, it always applies."
]
],
args: [:fields, {:optional, :condition, {Ash.Policy.Check.Static, result: true}}],
@ -364,19 +360,6 @@ defmodule Ash.Policy.Authorizer do
authorization rules as they may be deemed unnecessary. As such, authorization
checks should have no side effects. Ideally, the checks built-in to ash should
cover the bulk of your needs.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index(@sections)}
### Docs
#{Spark.Dsl.Extension.doc(@sections)}
<!--- ash-hq-hide-stop--> <!--- -->
"""
require Logger

View file

@ -1,5 +1,5 @@
defmodule Ash.Policy.FieldPolicy do
@moduledoc false
@moduledoc "Represents a field policy in an Ash.Resource"
defstruct [
:fields,
:condition,

View file

@ -1,5 +1,5 @@
defmodule Ash.Policy.Policy do
@moduledoc false
@moduledoc "Represents a policy on an Ash.Resource"
# For now we just write to `checks` and move them to `policies`
# on build, when we support nested policies we can change that.

View file

@ -1846,7 +1846,7 @@ defmodule Ash.Query do
Aggregations are made available on the `aggregates` field of the records returned
The filter option accepts either a filter or a keyword list of options to supply to build a limiting query for that aggregate.
See the DSL docs for each aggregate type in `Ash.Resource.Dsl` for more information.
See the DSL docs for each aggregate type in the [Resource DSL docs](dsl-ash-resource.html#aggregates) for more information.
Options:
@ -1887,7 +1887,7 @@ defmodule Ash.Query do
Aggregations are made available on the `aggregates` field of the records returned
The filter option accepts either a filter or a keyword list of options to supply to build a limiting query for that aggregate.
See the DSL docs for each aggregate type in `Ash.Resource.Dsl` for more information.
See the DSL docs for each aggregate type in the [Resource DSL docs](dsl-ash-resource.html#aggregates) for more information.
"""
@spec aggregate(
t() | Ash.Resource.t(),

View file

@ -45,22 +45,9 @@ defmodule Ash.Registry.Dsl do
@transformers [Ash.Registry.Transformers.WarnOnEmpty]
@moduledoc """
A small DSL for declaring an `Ash.Registry`.
A small DSL for declaring an `Ash.Registry`. Not generally necessary any longer.
`Ash.Registry` can be used generically, but the main way it is used in Ash is to provide a compile-time registry for an Ash Api.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index(@sections)}
### Docs
#{Spark.Dsl.Extension.doc(@sections)}
<!--- ash-hq-hide-stop--> <!--- -->
"""
use Spark.Dsl.Extension, sections: @sections, transformers: @transformers

View file

@ -1,5 +1,5 @@
defmodule Ash.Registry.Entry do
@moduledoc false
@moduledoc "Represents an entry in an Ash.Registry"
defstruct [:entry]

View file

@ -2,7 +2,7 @@ defmodule Ash.Resource do
@moduledoc """
A resource is a static definition of an entity in your system.
Resource DSL documentation: `Ash.Resource.Dsl`
[Resource DSL documentation](dsl-ash-resource.html)
"""
@type t :: module

View file

@ -38,10 +38,7 @@ defmodule Ash.Resource.Actions.Action do
constraints: [
type: :keyword_list,
doc: """
Constraints for the return type.
For more information see the specific type's documentation,
for general type information see `Ash.Type` and
for practical example [see the constraints topic](/documentation/topics/constraints.md).
Constraints for the return type. See the [constriants topic](/documentation/topics/constraints.md) for more.
"""
],
run: [

View file

@ -33,18 +33,14 @@ defmodule Ash.Resource.Actions.Argument do
type: :keyword_list,
default: [],
doc: """
Constraints to provide to the type when casting the value.
For more information see the specific type's documentation,
for general type information see `Ash.Type` and
for practical example [see the constraints topic](/documentation/topics/constraints.md).
Constraints to provide to the type when casting the value. For more information, see [the constraints topic](/documentation/topics/constraints.md).
"""
],
allow_nil?: [
type: :boolean,
default: true,
doc: """
Whether or not the argument value may be nil (or may be not provided)
If nil value is given error is raised.
Whether or not the argument value may be nil (or may be not provided). If nil value is given error is raised.
"""
],
private?: [
@ -58,9 +54,7 @@ defmodule Ash.Resource.Actions.Argument do
type: :boolean,
default: false,
doc: """
Whether or not the argument value contains sensitive information, like PII.
Using this option will cause the argument to be `** Redacted **` from the resource when logging or inspecting.
See the [security guide](/documentation/topics/security.md) for more.
Whether or not the argument value contains sensitive information, like PII. See the [security guide](/documentation/topics/security.md) for more.
"""
],
default: [

View file

@ -50,8 +50,7 @@ defmodule Ash.Resource.Actions.Create do
allow_nil_input: [
type: {:list, :atom},
doc: """
A list of attributes that would normally be required, but should not be for this action. They will still be validated just before
the record is created.
A list of attributes that would normally be required, but should not be for this action. They will still be validated just before the record is created.
"""
],
manual: [
@ -59,19 +58,14 @@ defmodule Ash.Resource.Actions.Create do
{:spark_function_behaviour, Ash.Resource.ManualCreate,
{Ash.Resource.ManualCreate.Function, 2}},
doc: """
Override the creation behavior. See the manual action guides for more. Accepts a module or module and opts, or a function that takes the changeset and context.
See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
Override the creation behavior. Accepts a module or module and opts, or a function that takes the changeset and context. See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
"""
],
upsert?: [
type: :boolean,
default: false,
doc: """
Wether or not this action is always an upsert.
If this is false, the action can still be used as an upsert by passing `upsert?: true` when using it.
This option forces all uses of this action to be treated as an upsert
Forces all uses of this action to be treated as an upsert.
"""
],
upsert_identity: [
@ -84,8 +78,6 @@ defmodule Ash.Resource.Actions.Create do
type: {:list, :atom},
doc: """
The fields to overwrite in the case of an upsert. If not provided, all fields except for fields set by defaults will be overwritten.
For example `uuid_primary_key :id` gets a default value, and so we would not overwrite that.
"""
]
]

View file

@ -49,9 +49,7 @@ defmodule Ash.Resource.Actions.Destroy do
{:spark_function_behaviour, Ash.Resource.ManualDestroy,
{Ash.Resource.ManualDestroy.Function, 2}},
doc: """
Override the update behavior. See the manual action guides for more. Accepts a module or module and opts, or a function that takes the changeset and context.
See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
Override the update behavior. Accepts a module or module and opts, or a function that takes the changeset and context. See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
"""
]
]

View file

@ -50,42 +50,27 @@ defmodule Ash.Resource.Actions.Read do
{:spark_function_behaviour, Ash.Resource.ManualRead,
{Ash.Resource.ManualRead.Function, 3}},
doc: """
Delegates running of the query to the provided module.
Accepts a module or module and opts, or a function that takes the changeset and context.
See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
Delegates running of the query to the provided module. Accepts a module or module and opts, or a function that takes the changeset and context. See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
"""
],
get?: [
type: :boolean,
default: false,
doc: """
Expresses that this action innately only returns a single result. Can be used by extensions to validate that you have not hooked something up that expects a list
to an action that can only return one thing. Used by the code interface when defining functions for read actions.
See the [code interface guide](/documentation/topics/code-interface.md) for more.
Expresses that this action innately only returns a single result. Used by extensions to validate and/or modify behavior. Causes code interfaces to return a single value instead of a list. See the [code interface guide](/documentation/topics/code-interface.md) for more.
"""
],
modify_query: [
type: {:or, [:mfa, {:fun, 2}]},
doc: """
Allows direct manipulation of the data layer query via an MFA.
The ash query and the data layer query will be provided as additional arguments.
The result must be `{:ok, new_data_layer_query} | {:error, error}`.
Here be dragons.
Allows direct manipulation of the data layer query via an MFA. The ash query and the data layer query will be provided as additional arguments. The result must be `{:ok, new_data_layer_query} | {:error, error}`.
"""
],
get_by: [
type: {:or, [:atom, {:list, :atom}]},
default: nil,
doc: """
A helper to automatically generate a "get by X" action.
Using this option will set `get?` to true, add arguments
for each of the specified fields, and add a filter to the
underlying query for each of the arguments.
A helper to automatically generate a "get by X" action. Sets `get?` to true, add args for each of the specified fields, and adds a filter for each of the arguments.
"""
]
],

View file

@ -20,8 +20,6 @@ defmodule Ash.Resource.Actions.SharedOptions do
type: :boolean,
doc: """
Whether or not the action should be run in transactions. Reads default to false, while create/update/destroy actions default to `true`.
Has no effect if the data layer does not support transactions, or if that data layer is already in a transaction.
"""
],
touches_resources: [
@ -41,8 +39,7 @@ defmodule Ash.Resource.Actions.SharedOptions do
type: :boolean,
default: false,
doc: """
If true, global validations will be done in a `before_action` hook, regardless of their configuration
on the resource.
If true, global validations will be done in a `before_action` hook, regardless of their configuration on the resource.
"""
],
skip_global_validations?: [
@ -55,18 +52,13 @@ defmodule Ash.Resource.Actions.SharedOptions do
reject: [
type: {:or, [in: [:all], list: :atom]},
doc: """
A list of attributes not to accept. This is useful if you want to say 'accept all but x'
If this is specified along with `accept`, then everything in the `accept` list minus any matches in the
`reject` list will be accepted.
A list of attributes not to accept. If this is specified along with `accept`, these are removed from the `accept` list.
"""
],
require_attributes: [
type: {:list, :atom},
doc: """
A list of attributes that would normally `allow_nil?`, to require for this action.
No need to include attributes that already do not allow nil?
A list of attributes that would normally `allow_nil?`, to require for this action. No need to include attributes that already do not allow nil?
"""
],
error_handler: [
@ -76,9 +68,7 @@ defmodule Ash.Resource.Actions.SharedOptions do
manual?: [
type: :boolean,
doc: """
Instructs Ash to *skip* the actual update/create/destroy step at the data layer. See the manual action guides for more.
See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
Instructs Ash to *skip* the actual update/create/destroy step at the data layer. See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
"""
]
]

View file

@ -46,10 +46,7 @@ defmodule Ash.Resource.Actions.Update do
{:spark_function_behaviour, Ash.Resource.ManualUpdate,
{Ash.Resource.ManualUpdate.Function, 2}},
doc: """
Override the update behavior. See the manual action guides for more.
Accepts a module or module and opts, or a function that takes the changeset and context.
See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
Override the update behavior. Accepts a module or module and opts, or a function that takes the changeset and context. See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
"""
]
]

View file

@ -28,10 +28,7 @@ defmodule Ash.Resource.Aggregate do
read_action: [
type: :atom,
doc: """
The read action to use when building the aggregate
Defaults to the primary read action. Keep in mind this action must not
have any required arguments.
The read action to use when building the aggregate. Defaults to the primary read action. Keep in mind this action must not have any required arguments.
"""
],
relationship_path: [
@ -87,13 +84,7 @@ defmodule Ash.Resource.Aggregate do
type: :boolean,
default: true,
doc: """
Wether or not the aggregate query should authorize based on the target action.
If you are using filter checks or simple checks that don't depend on arguments on the destination resource, then
this should be set to `true`. Additionally, you can set `read_action` to a separate action that does have filter checks.
Otherwise, you can set `authorize? false` on the aggregate.
If the parent query is not being authorized, then the aggregate not be authorized either regardless of the setting.
Wether or not the aggregate query should authorize based on the target action, if the parent query is authorized. Requires filter checks on the target action.
"""
]
]

View file

@ -57,10 +57,7 @@ defmodule Ash.Resource.Attribute do
constraints: [
type: :keyword_list,
doc: """
Constraints to provide to the type when casting the value.
For more information see the specific type's documentation,
for general type information see `Ash.Type` and
for practical example [see the constraints topic](/documentation/topics/constraints.md).
Constraints to provide to the type when casting the value. For more, see the [constraints topic](/documentation/topics/constraints.md).
"""
],
description: [
@ -71,9 +68,7 @@ defmodule Ash.Resource.Attribute do
type: :boolean,
default: false,
doc: """
Whether or not the attribute value contains sensitive information, like PII.
Using this option will cause the attribute to be `** Redacted **` from the resource when logging or inspecting.
See the [Security guide](/documentation/topics/security.md) for more.
Whether or not the attribute value contains sensitive information, like PII. See the [Security guide](/documentation/topics/security.md) for more.
"""
],
source: [
@ -86,29 +81,21 @@ defmodule Ash.Resource.Attribute do
type: :boolean,
default: false,
doc: """
Whether or not to ensure this attribute is always selected when reading from the database.
When this option is true and performing a read action, the attribute will **always** be selected even if it was explicitly selected out of the query.
For example say there is a resource with two attributes `:foo` and `:bar`.
Say `:foo` has `always_select? true` set.
The query `Ash.Query.select(MyResource, [:bar])` would return both `:foo` and `:bar` even though `:foo` was not selected in the query.
Whether or not to ensure this attribute is always selected when reading from the database, regardless of applied select statements.
"""
],
primary_key?: [
type: :boolean,
default: false,
doc: """
Whether the attribute is the primary key.
Composite primary key is also possible by using `primary_key? true` in more than one attribute.
If primary_key? is true, allow_nil? must be false.
Whether the attribute is the primary key. Composite primary key is also possible by using `primary_key? true` in more than one attribute. If primary_key? is true, allow_nil? must be false.
"""
],
allow_nil?: [
type: :boolean,
default: true,
doc: """
Whether or not the attribute can be set to nil.
If nil value is given error is raised.
Whether or not the attribute can be set to nil. If nil value is given error is raised.
"""
],
generated?: [
@ -122,20 +109,14 @@ defmodule Ash.Resource.Attribute do
type: :boolean,
default: true,
doc: """
Whether or not the value can be written to.
If `writable? false` then attribute is read-only and cannot be written to even when creating a record.
This can be overridden with `Ash.Changeset.force_change_attribute/3`.
Whether or not the value can be written to. Non-writable attributes can still be written with `Ash.Changeset.force_change_attribute/3`.
"""
],
private?: [
type: :boolean,
default: false,
doc: """
If `private? true` then attribute is read-only and cannot be written to even when creating a record.
Additionally it tells other extensions (e.g. AshJsonApi or AshGraphql) not to expose these attributes through the API.
The value of the attribute can be overridden with `Ash.Changeset.force_change_attribute/3`.
See the [security guide](/documentation/topics/security.md) for more.
The attribute is not publically writable, and should not be exposed over any public interfaces. See the [security guide](/documentation/topics/security.md) for more.
"""
],
default: [
@ -151,17 +132,13 @@ defmodule Ash.Resource.Attribute do
default: true,
doc: """
Whether or not the attribute can be referenced in filters.
Can be used to prevent filtering on large text columns with no indexing.
"""
],
match_other_defaults?: [
type: :boolean,
default: false,
doc: """
Ensures that other attributes that use the same "lazy" default (a function or an mfa), use the same default value.
Has no effect unless `default` is a zero argument function.
For example, create and update timestamps use this option, and have the same lazy function `&DateTime.utc_now/0`, so they
get the same value, instead of having slightly different timestamps.
Ensures that other attributes that use the same "lazy" default (a function or an mfa), use the same default value. Has no effect unless `default` is a zero argument function.
"""
]
]

View file

@ -38,9 +38,7 @@ defmodule Ash.Resource.Calculation do
]},
required: true,
doc: """
The module or `{module, opts}` to use for the calculation.
Also accepts a function that takes a single record and produces the result.
IMPORTANT: This function *does not take and return lists* like the `calculate/3` callback does.
The `module`, `{module, opts}` or `expr(...)` to use for the calculation. Also accepts a function that takes *a single record* and produces the result.
"""
],
description: [
@ -51,9 +49,7 @@ defmodule Ash.Resource.Calculation do
type: :boolean,
default: false,
doc: """
Whether or not the calculation will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql
See the [security guide](/documentation/topics/security.md) for more.
Whether or not the calculation will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql See the [security guide](/documentation/topics/security.md) for more.
"""
],
select: [

View file

@ -21,21 +21,7 @@ defmodule Ash.Resource.CalculationInterface do
type: :any,
default: [],
doc: """
Supply field or argument values referenced by the calculation.
By default, the value will be provided for any matching named reference *and* argument.
This is normally fine, but in the case that you have an argument and a reference with the same name,
you can specify it by supplying `{:arg, :name}` and `{:ref, :name}`. For example:
```elixir
define_calculation :id_matches, args: [{:arg, :id}, {:ref, :id}]
```
To make arguments optional, wrap them in `{:optional, ..}`, for example:
```elixir
define_calculation :id_matches, args: [{:arg, :id}, {:optional, {:ref, :id}}]
```
Supply field or argument values referenced by the calculation, in the form of :name, `{:arg, :name}` and/or `{:ref, :name}`. See the [code interface guide](/documentation/topics/code-interface.md) for more.
"""
]
]

View file

@ -40,10 +40,7 @@ defmodule Ash.Resource.Change do
{:spark_function_behaviour, Ash.Resource.Change, Ash.Resource.Change.Builtins,
{Ash.Resource.Change.Function, 2}},
doc: """
The module and options for a change.
Also accepts a function that takes the changeset and the context.
See `Ash.Resource.Change.Builtins` for more.
The module and options for a change. Also accepts a function that takes the changeset and the context. See `Ash.Resource.Change.Builtins` for builtin changes.
""",
required: true
],
@ -55,9 +52,7 @@ defmodule Ash.Resource.Change do
required: false,
default: [],
doc: """
Validations that should pass in order for this validation to apply.
These validations failing will not invalidate the changes, but instead just result in this change being ignored.
Also accepts functions take the changeset.
Validations that should pass in order for this validation to apply. These validations failing will result in this validation being ignored.
"""
]
]

View file

@ -391,19 +391,6 @@ defmodule Ash.Resource.Dsl do
args: [:change]
}
defmodule Set do
@moduledoc false
defstruct [:description, :where, :attribute, :expr]
def transform(set) do
{:ok,
%Ash.Resource.Change{
change: {Ash.Resource.Change.Atomic, attribute: set.attribute, expr: set.expr}
}
|> Map.merge(Map.take(set, [:description, :where]))}
end
end
@validate %Spark.Dsl.Entity{
name: :validate,
describe: """
@ -661,12 +648,7 @@ defmodule Ash.Resource.Dsl do
defaults: [
type: {:list, {:in, [:create, :read, :update, :destroy]}},
doc: """
Creates a simple action of each specified type, with the same name as the type.
These actions will be the primary actions, unless you've declared a different action
of the same type that is explicitly set as primary.
By default, resources have no default actions. Embedded resources, however, have a default
of all resource types.
Creates a simple action of each specified type, with the same name as the type. These will be `primary?` unless one already exists for that type. Embedded resources, however, have a default of all resource types.
"""
],
default_accept: [
@ -780,17 +762,13 @@ defmodule Ash.Resource.Dsl do
trace_name: [
type: :string,
doc: """
The name to use in traces. Defaults to the short_name stringified.
See the [monitoring guide](/documentation/topics/monitoring.md) for more.
The name to use in traces. Defaults to the short_name stringified. See the [monitoring guide](/documentation/topics/monitoring.md) for more.
"""
],
short_name: [
type: :atom,
doc: """
A short identifier for the resource, which should be unique.
See the [monitoring guide](/documentation/topics/monitoring.md) for more.
A short identifier for the resource, which should be unique. See the [monitoring guide](/documentation/topics/monitoring.md) for more.
"""
],
plural_name: [
@ -809,15 +787,7 @@ defmodule Ash.Resource.Dsl do
required: false,
default: true,
doc: """
Allow the resource to be used without any primary key fields.
### Warning
Lots of Ash and Ash extensions assume that every resource has a primary key and this is likely to break a lot of features.
If you run into a breakage please [open an issue](https://github.com/ash-project/ash/issues/new) and we'll take a look at it.
Disable at your peril.
Allow the resource to be used without any primary key fields. Warning: this option is experimental, and should not be used unless you know what you're doing.
"""
]
]
@ -826,11 +796,7 @@ defmodule Ash.Resource.Dsl do
@define_calculation %Spark.Dsl.Entity{
name: :define_calculation,
describe: """
Defines a function with the corresponding name and arguments, that evaluates a calculation.
Use `:_record` to take an instance of a record.
See the [code interface guide](/documentation/topics/code-interface.md) for more.
Defines a function with the corresponding name and arguments, that evaluates a calculation. Use `:_record` to take an instance of a record. See the [code interface guide](/documentation/topics/code-interface.md) for more.
""",
examples: [
"define_calculation :referral_link, args: [:id]",
@ -845,9 +811,7 @@ defmodule Ash.Resource.Dsl do
@define %Spark.Dsl.Entity{
name: :define,
describe: """
Defines a function with the corresponding name and arguments.
See the [code interface guide](/documentation/topics/code-interface.md) for more.
Defines a function with the corresponding name and arguments. See the [code interface guide](/documentation/topics/code-interface.md) for more.
""",
examples: [
"define :get_user_by_id, action: :get_by_id, args: [:id], get?: true"
@ -861,9 +825,7 @@ defmodule Ash.Resource.Dsl do
@code_interface %Spark.Dsl.Section{
name: :code_interface,
describe: """
Functions that will be defined on the Api module to interact with this resource.
See the [code interface guide](/documentation/topics/code-interface.md) for more.
Functions that will be defined on the Api module to interact with this resource. See the [code interface guide](/documentation/topics/code-interface.md) for more.
""",
examples: [
"""
@ -1386,21 +1348,7 @@ defmodule Ash.Resource.Dsl do
Ash.Resource.Verifiers.EnsureAggregateFieldIsAttributeOrCalculation
]
@moduledoc """
The built in resource DSL.
<!--- ash-hq-hide-start--> <!--- -->
## DSL Documentation
### Index
#{Spark.Dsl.Extension.doc_index(@sections)}
### Docs
#{Spark.Dsl.Extension.doc(@sections)}
<!--- ash-hq-hide-stop--> <!--- -->
"""
@moduledoc false
use Spark.Dsl.Extension,
sections: @sections,

View file

@ -25,8 +25,7 @@ defmodule Ash.Resource.Preparation do
{:spark_function_behaviour, Ash.Resource.Preparation, Ash.Resource.Preparation.Builtins,
{Ash.Resource.Preparation.Function, 2}},
doc: """
The module and options for a preparation.
Also accepts functions take the query and the context.
The module and options for a preparation. Also accepts functions take the query and the context.
""",
required: true
]

View file

@ -70,8 +70,7 @@ defmodule Ash.Resource.Relationships.HasOne do
type: :boolean,
default: false,
doc: """
Set to `true` if this relationship is actually a `has_many` relationship with only a single match.
This will allow data layers that do joining under the hood to properly do deduplication when necessary.
Signal that this relationship is actually a `has_many` where the first record is given via the `sort`. This will allow data layers to properly deduplicate when necessary.
"""
]
],

View file

@ -41,9 +41,7 @@ defmodule Ash.Resource.Relationships.SharedOptions do
type: :boolean,
default: false,
doc: """
Whether or not the relationship will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql
See the [security guide](/documentation/topics/security.md) for more.
Whether or not the relationship will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql See the [security guide](/documentation/topics/security.md) for more.
"""
],
not_found_message: [
@ -99,7 +97,6 @@ defmodule Ash.Resource.Relationships.SharedOptions do
type: :string,
doc: """
A message to show if there is a conflict with this relationship in the database on destroy.
For example, if a record is deleted while related records still exist (and aren't configured to cascade deletes)
"""
]
]
@ -113,23 +110,7 @@ defmodule Ash.Resource.Relationships.SharedOptions do
[
type: :boolean,
doc: """
If true, all existing entities are considered related, i.e this relationship is not based on any fields, and `source_attribute` and
`destination_attribute` are ignored.
This can be very useful when combined with multitenancy. Specifically, if you have a tenant resource like `Organization`,
you can use `no_attributes?` to do things like `has_many :employees, Employee, no_attributes?: true`, which lets you avoid having an
unnecessary `organization_id` field on `Employee`. The same works in reverse: `has_one :organization, Organization, no_attributes?: true`
allows relating the employee to their organization.
Some important caveats here:
1. You can still manage relationships from one to the other, but "relate" and "unrelate"
will have no effect, because there are no fields to change.
2. Loading the relationship on a list of resources will not behave as expected in all circumstances involving multitenancy. For example,
if you get a list of `Organization` and then try to load `employees`, you would need to set a single tenant on the load query, meaning
you'll get all organizations back with the set of employees from one tenant. This could eventually be solved, but for now it is considered an
edge case.
All existing entities are considered related, i.e this relationship is not based on any fields, and `source_attribute` and `destination_attribute` are ignored. See the See the [relationships guide](/documentation/topics/relationships.md) for more.
"""
]}
end

View file

@ -62,18 +62,14 @@ defmodule Ash.Resource.Validation do
required: false,
default: [],
doc: """
Validations that should pass in order for this validation to apply.
These validations failing will not invalidate the changes, but will instead result in this validation being ignored.
Accepts a module, module and opts, or a 1 argument function that takes the changeset.
Validations that should pass in order for this validation to apply. Any of these validations failing will result in this validation being ignored.
"""
],
on: [
type: {:custom, __MODULE__, :on, []},
default: [:create, :update],
doc: """
The action types the validation should run on.
Many validations don't make sense in the context of deletion, so by default it is left out of the list.
The action types the validation should run on. Many validations don't make sense in the context of deletion, so by default it is not included.
"""
],
only_when_valid?: [

View file

@ -40,9 +40,13 @@ defmodule Mix.Tasks.Ash.ReplaceDocLinks do
">#{contents}<"
end)
|> String.replace(~r</documentation/.*/.*.md>, fn "/documentation/" <> type_and_name ->
[_, filename] = String.split(type_and_name, "/")
case String.split(type_and_name, "/") do
[_, filename] ->
filename |> String.trim_trailing(".md") |> Kernel.<>(".html")
_ ->
"/documentation/" <> type_and_name
end
end)
File.write!(file, new_contents)

49
mix.exs
View file

@ -35,14 +35,16 @@ defmodule Ash.MixProject do
end
defp extras do
"documentation/**/*.md"
"documentation/**/*.{md,livemd,cheatmd}"
|> Path.wildcard()
|> Enum.map(fn path ->
title =
path
|> Path.basename(".md")
|> Path.basename(".livemd")
|> Path.basename(".cheatmd")
|> String.split(~r/[-_]/)
|> Enum.map_join(" ", &String.capitalize/1)
|> Enum.map_join(" ", &capitalize/1)
|> case do
"F A Q" ->
"FAQ"
@ -59,6 +61,15 @@ defmodule Ash.MixProject do
end)
end
defp capitalize(string) do
string
|> String.split(" ")
|> Enum.map(fn string ->
[hd | tail] = String.graphemes(string)
String.capitalize(hd) <> Enum.join(tail)
end)
end
defp groups_for_extras do
[
Tutorials: [
@ -68,7 +79,8 @@ defmodule Ash.MixProject do
~r'documentation/tutorials'
],
"How To": ~r'documentation/how_to',
Topics: ~r'documentation/topics'
Topics: ~r'documentation/topics',
DSLs: ~r'documentation/dsls'
]
end
@ -149,18 +161,15 @@ defmodule Ash.MixProject do
]
],
groups_for_modules: [
"Extensions & DSLs": [
Ash.Api.Dsl,
Ash.Resource.Dsl,
Ash.Flow.Dsl,
Extensions: [
Ash.Api,
Ash.Resource,
Ash.DataLayer.Ets,
Ash.DataLayer.Mnesia,
Ash.DataLayer.Simple,
Ash.Notifier.PubSub,
Ash.Policy.Authorizer,
Ash.Registry,
Ash.Registry.Dsl,
Ash.Resource
Ash.Registry
],
Resources: [
Ash.Api,
@ -280,24 +289,23 @@ defmodule Ash.MixProject do
[
{:spark, "~> 1.1 and >= 1.1.20"},
{:ecto, "~> 3.7"},
{:ets, "~> 0.8.0"},
{:ets, "~> 0.8"},
{:decimal, "~> 2.0"},
{:picosat_elixir, "~> 0.2"},
{:comparable, "~> 1.0"},
{:jason, ">= 1.0.0"},
{:earmark, "~> 1.4", optional: true},
{:stream_data, "~> 0.5.0"},
{:stream_data, "~> 0.5"},
{:telemetry, "~> 1.1"},
{:plug, ">= 0.0.0", optional: true},
# Dev/Test dependencies
{:ex_doc, "~> 0.22", only: [:dev, :test], runtime: false},
{:ex_check, "~> 0.12.0", only: [:dev, :test]},
{:ex_check, "~> 0.12", only: [:dev, :test]},
{:credo, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:git_ops, "~> 2.5", only: [:dev, :test]},
{:mix_test_watch, "~> 1.0", only: [:dev, :test], runtime: false},
{:parse_trans, "3.3.0", only: [:dev, :test], override: true},
{:benchee, "~> 1.1", only: [:dev, :test]},
{:doctor, "~> 0.21", only: [:dev, :test]}
]
@ -307,9 +315,18 @@ defmodule Ash.MixProject do
[
sobelow: "sobelow --skip",
credo: "credo --strict",
docs: ["docs", "ash.replace_doc_links"],
docs: [
"spark.cheat_sheets",
"docs",
"ash.replace_doc_links",
"spark.cheat_sheets_in_search"
],
"spark.cheat_sheets_in_search":
"spark.cheat_sheets_in_search --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Flow.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub,Ash.Policy.Authorizer",
"spark.formatter":
"spark.formatter --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Flow.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub,Ash.Policy.Authorizer"
"spark.formatter --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Flow.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub,Ash.Policy.Authorizer",
"spark.cheat_sheets":
"spark.cheat_sheets --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Flow.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub,Ash.Policy.Authorizer"
]
end
end

View file

@ -1,40 +1,39 @@
%{
"benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
"credo": {:hex, :credo, "1.6.4", "ddd474afb6e8c240313f3a7b0d025cc3213f0d171879429bf8535d7021d9ad78", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c28f910b61e1ff829bffa056ef7293a8db50e87f2c57a9b5c3f57eee124536b7"},
"credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"dialyxir": {:hex, :dialyxir, "1.4.1", "a22ed1e7bd3a3e3f197b68d806ef66acb61ee8f57b3ac85fc5d57354c5482a93", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "84b795d6d7796297cca5a3118444b80c7d94f7ce247d49886e7c291e1ae49801"},
"doctor": {:hex, :doctor, "0.21.0", "20ef89355c67778e206225fe74913e96141c4d001cb04efdeba1a2a9704f1ab5", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "a227831daa79784eb24cdeedfa403c46a4cb7d0eab0e31232ec654314447e4e0"},
"earmark": {:hex, :earmark, "1.4.24", "1923e201c3742af421860b983560967cc3e3deacc59c12966bc991a5435565e6", [:mix], [{:earmark_parser, "~> 1.4.25", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "9724242f241f2ad634756d8f2bb57a3d0992cedd10c51842fa655703b4da7c67"},
"earmark_parser": {:hex, :earmark_parser, "1.4.25", "2024618731c55ebfcc5439d756852ec4e85978a39d0d58593763924d9a15916f", [:mix], [], "hexpm", "56749c5e1c59447f7b7a23ddb235e4b3defe276afc220a6227237f3efe83f51e"},
"ecto": {:hex, :ecto, "3.10.2", "6b887160281a61aa16843e47735b8a266caa437f80588c3ab80a8a960e6abe37", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6a895778f0d7648a4b34b486af59a1c8009041fbdf2b17f1ac215eb829c60235"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"earmark": {:hex, :earmark, "1.4.40", "ff1a0f8bf3b298113c2a257c4e7a8b29ba9db5d35f5ee6d29291cb8caa09a071", [:mix], [{:earmark_parser, "~> 1.4.35", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "5fb622d5e36046bc313a426211e8bf769ba50db7720744859a21932c6470d75c"},
"earmark_parser": {:hex, :earmark_parser, "1.4.35", "437773ca9384edf69830e26e9e7b2e0d22d2596c4a6b17094a3b29f01ea65bb8", [:mix], [], "hexpm", "8652ba3cb85608d0d7aa2d21b45c6fad4ddc9a1f9a1f1b30ca3a246f0acc33f6"},
"ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"},
"elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"},
"ex_check": {:hex, :ex_check, "0.12.0", "c0e2919ecc06afeaf62c52d64f3d91bd4bc7dd8deaac5f84becb6278888c967a", [:mix], [], "hexpm", "cfafa8ef97c2596d45a1f19b5794cb5c7f700f25d164d3c9f8d7ec17ee67cf42"},
"ex_doc": {:hex, :ex_doc, "0.28.3", "6eea2f69995f5fba94cd6dd398df369fe4e777a47cd887714a0976930615c9e6", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "05387a6a2655b5f9820f3f627450ed20b4325c25977b2ee69bed90af6688e718"},
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
"ex_check": {:hex, :ex_check, "0.15.0", "074b94c02de11c37bba1ca82ae5cc4926e6ccee862e57a485b6ba60fca2d8dc1", [:mix], [], "hexpm", "33848031a0c7e4209c3b4369ce154019788b5219956220c35ca5474299fb6a0e"},
"ex_doc": {:hex, :ex_doc, "0.30.6", "5f8b54854b240a2b55c9734c4b1d0dd7bdd41f71a095d42a70445c03cf05a281", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bd48f2ddacf4e482c727f9293d9498e0881597eae6ddc3d9562bd7923375109f"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.5.5", "4f8369f3c9347e06a7f289de98fadfc95194149156335c5292479a53eddbccd2", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "3b1e3b12968f9da6f79b5e2b2274477206949376e3579d05a5f3d439eda0b746"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"git_ops": {:hex, :git_ops, "2.6.0", "e0791ee1cf5db03f2c61b7ebd70e2e95cba2bb9b9793011f26609f22c0900087", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "b98fca849b18aaf490f4ac7d1dd8c6c469b0cc3e6632562d366cab095e666ffe"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mix_test_watch": {:hex, :mix_test_watch, "1.1.1", "eee6fc570d77ad6851c7bc08de420a47fd1e449ef5ccfa6a77ef68b72e7e51ad", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "f82262b54dee533467021723892e15c3267349849f1f737526523ecba4e6baae"},
"nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"picosat_elixir": {:hex, :picosat_elixir, "0.2.0", "1c9d2d65b32039c9e3eb600bff903579e5916f559dbf0013b3c4ca617c93ac64", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "7900c6d58d65a9d8a2899f53019fb70e9b9678161bbb53f646e28e146aca138c"},
"plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"},
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
"sourceror": {:hex, :sourceror, "0.12.3", "a2ad3a1a4554b486d8a113ae7adad5646f938cad99bf8bfcef26dc0c88e8fade", [:mix], [], "hexpm", "4d4e78010ca046524e8194ffc4683422f34a96f6b82901abbb45acc79ace0316"},
"spark": {:hex, :spark, "1.1.20", "48f4dd3bed1e16d43a5084a0277f2d8c3adac9bcfa07dfdac9931b025b75480b", [:mix], [{:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "b48d77cdcbc3f00a6e67f447d9860c390f790baf4e7d879d7c6bfd37109c4659"},
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
"picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"},
"plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"sourceror": {:hex, :sourceror, "0.13.0", "c6ecc96ee3ae0e042e9082a9550a1989ea40182492dc29024a8d9d2b136e5014", [:mix], [], "hexpm", "d0a819491061cd26bfa4450d1c84301a410c19c1782a6577ce15853fc0e7e4e1"},
"spark": {:hex, :spark, "1.1.28", "45be55a6d1cd2da611c4f5bb6fc0b345221800d1c4ab53b5bcfe32703e75facf", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "0689416ec1abd7189ea4073247ae6959bfad94c12da096c78447667a17613652"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
"stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
}