improvement: update docs to new links formats for ash_hq (#483)

This commit is contained in:
Zach Daniel 2023-01-18 00:34:20 -05:00 committed by GitHub
parent 5bddd175f3
commit 897308ba3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 447 additions and 1310 deletions

View file

@ -20,11 +20,11 @@ actions do
end
```
But that is just a simple way to get started, or to create resources that really don't do anything beyond those four operations. You can have _as many actions as you want_. The best designed Ash applications will have numerous actions, named after the intent behind how they are used. They won't have all reads going through a single read action, and the same goes for the other action types. The richer the actions on the resource, the better interface you can have. With that said, many resources may only have those four basic actions, especially those that are "managed" through some parent resource. See the guide on {{link:ash:guide:Managing Relationships}} for more.
But that is just a simple way to get started, or to create resources that really don't do anything beyond those four operations. You can have _as many actions as you want_. The best designed Ash applications will have numerous actions, named after the intent behind how they are used. They won't have all reads going through a single read action, and the same goes for the other action types. The richer the actions on the resource, the better interface you can have. With that said, many resources may only have those four basic actions, especially those that are "managed" through some parent resource. See the guide on [Managing Relationships](/documentation/topics/managing-relationships.md) for more.
### Primary Actions
Primary actions are a way to inform the framework which actions should be used in certain "automated" circumstances, or in cases where an action has not been specified. If a primary action is attempted to be used but does not exist, you will get an error about it at runtime. The place you typically need primary actions is, when {{link:ash:guide:Managing Relationships}}. However, some prefer to be as explicit as possible, and so will _always_ indicate an action name, and in that case will never use primary actions. When using the `defaults` option to add default actions, they are marked as primary.
Primary actions are a way to inform the framework which actions should be used in certain "automated" circumstances, or in cases where an action has not been specified. If a primary action is attempted to be used but does not exist, you will get an error about it at runtime. The place you typically need primary actions is, when [Managing Relationships](/documentation/topics/managing-relationships.md). However, some prefer to be as explicit as possible, and so will _always_ indicate an action name, and in that case will never use primary actions. When using the `defaults` option to add default actions, they are marked as primary.
A simple example where a primary action would be used:
@ -125,21 +125,20 @@ end
The following steps are performed when you call `Ash.Query.for_read/4`.
- Gather process context | {{link:ash:guide:Store Context In Process}}
- Cast input arguments | {{link:ash:dsl:resource/actions/read/argument}}
- Set default argument values | {{link:ash:option:resource/actions/read/argument/default}}
- Add errors for missing required arguments | {{link:ash:option:resource/actions/read/argument/allow_nil?}}
- Run query preparations | {{link:ash:dsl:resource/actions/read/prepare}}
- Add action filter | {{link:ash:option:resource/actions/read/filter}}
- [Gather Process Context](/documentation/topics/store-context-in-process.md)
- Cast input arguments - `d:Ash.Resource.actions.read.argument`
- Set default argument values - `d:Ash.Resource.actions.read.argument|default`
- Add errors for missing required arguments | `d:Ash.Resource.actions.read.argument|allow_nil?`
- Run query preparations | `d:Ash.Resource.actions.read.prepare`
- Add action filter | `d:Ash.Resource.actions.read|filter`
#### Running the Read Action
If the query has not yet been run through `Ash.Query.for_read/3` for the action in question, we do that first. Then we perform the following steps. These steps are trimmed down, and are aimed at helping users understand the general flow. Some steps are omitted.
- Gather process context | {{link:ash:guide:Store Context In Process}}
- Run `Ash.Query.for_read/3` if it has not already been run
- Apply tenant Filters for attribute {{link:ash:guide:Multitenancy}}
- Apply pagination options
- [Apply tenant filters for attribute](/documentation/topics/multitenancy.md)
- Apply [pagination](/documentation/topics/pagination.md) options
- Run before action hooks
- Multi-datalayer filter is synthesized. We run queries in other data layers to fetch ids and translate related filters to `(destination_field in ^ids)`
- Strict Check & Filter Authorization is run
@ -155,7 +154,7 @@ The following steps happen asynchronously during or after the main data layer qu
### Create/Update/Destroy Actions
These actions operate on an `Ash.Changeset`. While standard destroy actions don't care about the changes you add to a changeset, you may mark a destroy action as {{link:ash:option:/resource/actions/destroy/soft?}}, which means you will be performing an update that will in some way "hide" the resource. Generally this hiding is done by adding a {{link:ash:option:resource/base_filter}} i.e `base_filter [is_nil: :archived_at]`
These actions operate on an `Ash.Changeset`. While standard destroy actions don't care about the changes you add to a changeset, you may mark a destroy action as `d:Ash.Resource.actions.destroy|soft?`, which means you will be performing an update that will in some way "hide" the resource. Generally this hiding is done by adding a `d:Ash.Resource.resource|base_filter` i.e `base_filter [is_nil: :archived_at]`
Here is an example create action:
@ -185,7 +184,7 @@ end
The following steps are run when calling `Ash.Changeset.for_create/4`, `Ash.Changeset.for_update/4` or `Ash.Changeset.for_destroy/4`.
- Gather process context | {{link:ash:guide:Store Context In Process}}
- [Gather process context](/documentation/topics/store-context-in-process.md)
- Cast input params | This is any arguments in addition to any accepted attributes
- Set argument defaults
- Require any missing arguments
@ -193,7 +192,7 @@ The following steps are run when calling `Ash.Changeset.for_create/4`, `Ash.Chan
- Require any accepted attributes that are `allow_nil?` false
- Set any default values for attributes
- Run action changes & validations
- Run validations, or add them in `before_action` hooks if using {{link:ash:option:resource/actions/create/validate#before_action?}}
- Run validations, or add them in `before_action` hooks if using `d:Ash.Resource.actions.create.validate|before_action?`
#### Running the Create/Update/Destroy Action
@ -204,6 +203,6 @@ All of these actions are run in a transaction if the data layer supports it. You
- Before action hooks are performed in reverse order they were added. (unless `append?` option was used)
- For manual actions, a before action hook must have set
- After action hooks are performed in the order they were added (unless `prepend?` option was used)
- For {{link:ash:guide:Manual Actions}}, one of these after action hooks must have returned a result, otherwise an error is returned.
- For [Manual Actions](/documentation/topics/manual-actions.md), one of these after action hooks must have returned a result, otherwise an error is returned.
- Non-belongs-to relationships are managed, creating/updating/destroying related records.
- If an `after_action` option was passed when running the action, it is run with the changeset and the result. Only supported for create & update actions.

View file

@ -1,6 +1,6 @@
# Aggregates
Aggregates in Ash allow for retrieving summary information over groups of related data. A simple example might be to show the "count of published posts for a user". Aggregates allow us quick and performant access to this data, in a way that supports being filtered/sorted on automatically. More aggregate types can be added, but you will be restricted to only the supported types. In cases where aggregates don't suffice, use {{link:ash:guide:Calculations}}, which are intended to be much more flexible.
Aggregates in Ash allow for retrieving summary information over groups of related data. A simple example might be to show the "count of published posts for a user". Aggregates allow us quick and performant access to this data, in a way that supports being filtered/sorted on automatically. More aggregate types can be added, but you will be restricted to only the supported types. In cases where aggregates don't suffice, use [Calculations](/documentation/topics/calculations.md), which are intended to be much more flexible.
## Declaring aggregates on a resource
@ -21,7 +21,7 @@ The available aggregate types are:
- `sum` - sums the related items meeting the criteria. Must specify the `field` to sum.
- `list` - lists the related values. Must specify the `field` to list.
See the docs on {{link:ash:dsl:resource/aggregates}} for more information.
See the docs on `d:Ash.Resource.aggregates` for more information.
The aggregates declared on a resource allow for declaring a set of named aggregates that can be used by extensions.

View file

@ -14,7 +14,7 @@ calculations do
end
```
See the {{link:ash:guide:Expressions}} guide for more.
See the [Expressions guide](/documentation/topics/expressions.md) for more.
### Module Calculations

View file

@ -10,7 +10,7 @@ code_interface do
end
```
This simple setup now allows you to open a ticket with `Helpdesk.Support.Ticket.open(subject)`. You can cause it to raise errors instead of return them with `Helpdesk.Support.Ticket.open!(subject)`. For information on the options and additional inputs these defined functions take, look at the generated function documentation, which you can do in iex with `h Helpdesk.Support.Ticket.open`. For more information on the code interface, read the DSL documentation: {{link:ash:dsl:resource/code_interface}}.
This simple setup now allows you to open a ticket with `Helpdesk.Support.Ticket.open(subject)`. You can cause it to raise errors instead of return them with `Helpdesk.Support.Ticket.open!(subject)`. For information on the options and additional inputs these defined functions take, look at the generated function documentation, which you can do in iex with `h Helpdesk.Support.Ticket.open`. For more information on the code interface, read the DSL documentation: `d:Ash.Resource.code_interface`.
## define_for and define_interface

View file

@ -38,7 +38,7 @@ The following functions are built in:
- `if` | Works like elixir's `if`.
- `is_nil/1` | Works like elixir's `is_nil`
- `get_path/2` | i.e `get_path(value, ["foo", "bar"])`. This is what expressions like `value[:foo]["bar"]` are turned into under the hood.
- `ago/2` | i.e `deleted_at > ago(7, :day)`. The available time intervals are documented in {{link:ash:module:Ash.Type.DurationName}}
- `ago/2` | i.e `deleted_at > ago(7, :day)`. The available time intervals are documented in `Ash.Type.DurationName`
- `contains/2` | if one string contains another string, i.e `contains("fred", "red")`
- `length/1` | the length of a list, i.e. `length([:foo, :bar])`
- `exists/2` | `exists(foo.bar, name == "fred")` takes an expression scoped to the destination resource, and checks if any related entry matches. See the section on `exists` below.

View file

@ -1,6 +1,6 @@
# Instrumentation
Instrumentation Ash has two primary components, {{link:ash:module:Ash.Tracer}} and `:telemetry`. Instrumentation is closely tied to observability and monitoring.
Instrumentation Ash has two primary components, `Ash.Tracer` and `:telemetry`. Instrumentation is closely tied to observability and monitoring.
## Telemetry
@ -49,10 +49,9 @@ Resource
For customizing the names created for each span, see:
- {{link:ash:option:api/execution/trace_name}}
- {{link:ash:option:resource/resource/trace_name}}
- {{link:ash:option:flow/flow/trace_name}}
- `d:Ash.Api.execution|trace_name`
- `d:Ash.Resource.resource|trace_name`
- `d:Ash.Flow.flow|trace_name`
## After/Before Action Hooks

View file

@ -73,4 +73,4 @@ You can also provide the `parse_attribute?` option if the tenant being set doesn
Context multitenancy allows for the data layer to dictate how multitenancy works. For example, a csv data layer might implement multitenancy via saving the file with different suffixes, or an API wrapping data layer might use different subdomains for the tenant.
For `AshPostgres` context multitenancy, which uses postgres schemas and is referred to ash "Schema Based Multitenancy", see the {{link:ash_postgres:guide:Schema Based Multitenancy|guide}}.
For `AshPostgres` context multitenancy, which uses postgres schemas and is referred to ash "Schema Based Multitenancy", see the [guide](https://hexdocs.pm/ash_postgres/schema-based-multitenancy.html)

View file

@ -4,7 +4,7 @@ Pagination is configured at the action level. There are two kinds of pagination
pros and cons to each. An action can support both at the same time, or only one (or none). A full count of records can be
requested by passing `page: [count: true]`, but it should be kept in mind that doing this requires running the same query
twice, one of which is a count of all records. Ash does these in parallel, but it can still be quite expensive on large
datasets. For more information on the options for configuring actions to support pagination, see the {{link:ash:dsl:resource/actions/read/prepare}}
datasets. For more information on the options for configuring actions to support pagination, see `d:Ash.Resource.actions.read|prepare`
## Offset Pagination

View file

@ -4,7 +4,7 @@ Policies determine what actions on a resource are permitted for a given actor.
## Important!
Read and understand the {{link:ash:guide:Security}} guide before proceeding, which explains actors, how to set them, and other relevant configurations.
Read and understand the [Security](/documentation/topics/security.md) guide before proceeding, which explains actors, how to set them, and other relevant configurations.
## Guide

View file

@ -4,7 +4,7 @@ Ash includes a builtin notifier to help you publish events over any kind of pub-
You configure a module that defines a `broadcast/3` function, and then add some "publications" which configure under what conditions an event should be sent and what the topic should be.
For the full DSL spec see {{link:ash:dsl:PubSub/pub_sub}}
For the full DSL spec see `Ash.Notifier.PubSub`
## Topic Templates

View file

@ -75,7 +75,7 @@ YourApi.load(users, friends: friends)
## Managing related data
See {{link:ash:guide:Managing Relationships}} for more information.
See [Managing Relationships](/documentation/topics/managing-relationships.md) for more information.
## Relationships Basics
@ -97,9 +97,9 @@ A `belongs_to` relationship means that there is an attribute (`source_attribute`
The `destination_attribute` defaults to `:id`.
By default, a belongs_to relationship will define an attribute called `<relationship_name>_id` of type `:uuid` on the resource. To configure this, use options like:
- {{link:ash:option:relationships/belongs_to/define_attribute?}} to define it yourself
- {{link:ash:option:relationships/belongs_to/attribute_type}} to modify the default type
- {{link:ash:option:relationships/belongs_to/attribute_writable?}} to make the source attribute `private?: false, writable?: true` (both are not the default)
- `d:Ash.Resource.relationships.belongs_to|define_attribute?` to define it yourself
- `d:Ash.Resource.relationships.belongs_to|attribute_type` to modify the default type
- `d:Ash.Resource.relationships.belongs_to|attribute_writable?` to make the source attribute `private?: false, writable?: true` (both are not the default)
For example:
@ -126,7 +126,7 @@ relationships do
end
```
See the docs for more: {{link:ash:dsl:relationships/belongs_to}}
See the docs for more: `d:Ash.Resource.relationships.belongs_to`
### Has One
@ -142,7 +142,7 @@ the destination resource, instead of the source. In the example above, we'd expe
By default, the `source_attribute` is assumed to be `:id`, and `destination_attribute` defaults to `<snake_cased_last_part_of_module_name>_id`. In the above example, it would default `destination_attribute` to `user_id`.
See the docs for more: {{link:ash:dsl:relationships/has_one}}
See the docs for more: `d:Ash.Resource.relationships.has_one`
### Has Many
@ -157,7 +157,7 @@ A `has_many` relationship is similar to a `has_one` in that the reference attrib
By default, the `source_attribute` is assumed to be `:id`, and `destination_attribute` defaults to `<snake_cased_last_part_of_module_name>_id`. In the above example, it would default `destination_attribute` to `post_id`.
See the docs for more: {{link:ash:dsl:relationships/has_many}}
See the docs for more: `d:Ash.Resource.relationships.has_many`
## Many To Many Relationships
Lets say that individual todo items in our app can be added to multiple lists, and every list has multiple todo items. This is a great case for `many_to_many` relationships.
@ -199,4 +199,4 @@ end
Now that we have a resource with the proper attributes, Ash will use this automatically under the hood when
performing the relationship operations detailed above, like filtering and loading.
See the docs for more: {{link:ash:dsl:relationships/many_to_many}}
See the docs for more: `d:Ash.Resource.relationships.many_to_many`

View file

@ -6,7 +6,7 @@ A great thing to do early on is to be explicit about your security configuration
## Authorization
Authorization in Ash is done via authorizers. Generally, you won't need to create your own authorizer, as the builtin policy authorizer {{link:ash:extension:Policy Authorizer}} should work well for any use case. Authorization is performed with a given actor and a query or changeset.
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.
### Actors
@ -71,16 +71,16 @@ Api.read!(User)
### Authorization Configuration
The default behavior is illustrated above, but it can be customized with the options in the {{link:ash:dsl:api/authorization}} section of the Api module you are calling.
The default behavior is illustrated above, but it can be customized with the options in the `d:Ash.Api.authorization` section of the Api module you are calling.
#### {{link:ash:option:api/authorization/require_actor?}}
#### `d:Ash.Api.authorization|require_actor?`
Requires that an actor is set for all requests.
Important: `nil` is still a valid actor, so this won't prevent providing `actor: nil`.
#### {{link:ash:option:api/authorization/authorize}}
#### `d:Ash.Api.authorization|authorize`
##### Important!

View file

@ -195,11 +195,11 @@ defmodule MyApp.Tweet do
end
```
Your extension will be automatically supported by the `elixir_sense` extension, showing inline documentation and auto complete as you type. For more on that, see {{link:ash:guide:Development Utilities}}.
Your extension will be automatically supported by the `elixir_sense` extension, showing inline documentation and auto complete as you type. For more on that, see p[Development Utilities](/documentation/topics/development-utilities.md)
## Making a Base Resource
The "Base Resource" pattern has been adopted by some as a way to make it easy to ensure that your base extension is used everywhere. Instead of using `Ash.Resource` you use `MyApp.Resource`. Take a look at the {{link:ash:guide:Development Utilities}} guide if you do this, as you will need to update your formatter configuration, if you are using it.
The "Base Resource" pattern has been adopted by some as a way to make it easy to ensure that your base extension is used everywhere. Instead of using `Ash.Resource` you use `MyApp.Resource`. Take a look at the [Development Utilities](/documentation/topics/development-utilities.md) guide if you do this, as you will need to update your formatter configuration, if you are using it.
```elixir
defmodule MyApp.Resource do

View file

@ -18,7 +18,7 @@ In this guide we will:
## Things you may want to read first
- [Install Elixir](https://elixir-lang.org/install.html)
- {{link:ash:guide:Philosophy}}
- [Philosophy Guide](/documentation/tutorials/philosophy.md)
## Requirements
@ -314,7 +314,7 @@ If we didn't include a subject, or left off the arguments completely, we would s
Now let's add some logic to close a ticket. This time we'll add an `update` action.
Here we will use a `change`. Changes allow you to customize how an action executes with very fine-grained control. There are built-in changes that are automatically available as functions, but you can define your own and pass it in as shown below. You can add multiple, and they will be run in order. See the {{link:ash:guide:Actions}} guides for more.
Here we will use a `change`. Changes allow you to customize how an action executes with very fine-grained control. There are built-in changes that are automatically available as functions, but you can define your own and pass it in as shown below. You can add multiple, and they will be run in order. See the [Actions guide](/documentation/topics/actions.md) for more.
```elixir
# lib/helpdesk/support/resources/ticket.ex
@ -414,7 +414,7 @@ Helpdesk.Support.Ticket
|> Helpdesk.Support.read!()
```
The examples above could be easily implemented with `Enum.filter`, but the real power here is to allow you to use the same tools when working with any data layer. If you were using the {{link:ash_postgres:extension:AshPostgres}}, the above code would be exactly the same, except we wouldn't need the call to `set_data/2`.
The examples above could be easily implemented with `Enum.filter`, but the real power here is to allow you to use the same tools when working with any data layer. If you were using the `AshPostgres.DataLayer` data layer.
Even though it doesn't persist data in any way, `Ash.DataLayer.Simple` can be useful to model static data, or be used for resources where all the actions are manual and inject data from other sources.
@ -529,7 +529,7 @@ You may notice that if you don't add the resource to the registry, or if you don
## Working with relationships
There are a wide array of options when managing relationships, and we won't cover all of them here. See the guide on {{link:ash:guide:Managing Relationships}} for a full explanation.
There are a wide array of options when managing relationships, and we won't cover all of them here. See the guide on [Managing Relationships](/documentation/topics/managing-relationships.md) for a full explanation.
In this example we'll demonstrate the use of action arguments, the method by which you can accept additional input to an action.
@ -601,16 +601,16 @@ Where Ash shines however, is all of the tools that can operate on your resources
Creating and using changesets manually can be verbose, and they all look very similar. Luckily, Ash has your back and can generate these for you using Code Interfaces!
Check out the {{link:ash:guide:Code Interface}} to derive things like `Helpdesk.Support.Ticket.assign!(representative.id)`
Check out the [Code Interface Guide](/documentation/topics/code-interface.md) to derive things like `Helpdesk.Support.Ticket.assign!(representative.id)`
#### Persist your data
See {{link:ash_postgres:guide:Get Started With Postgres|AshPostgres}} to see how to back your resources with Postgres. This is highly recommended, as the Postgres data layer provides tons of advanced capabilities.
See [The AshPostgres getting started guide](https://hexdocs.pm/ash_postgres/get-started-with-postgres.html) to see how to back your resources with Postgres. This is highly recommended, as the Postgres data layer provides tons of advanced capabilities.
#### Add an API
Check out the {{link:ash_json_api:library|AshJsonApi}} and {{link:ash_graphql:library|AshGraphql}} extensions to effortlessly build APIs around your resources
Check out the `AshJsonApi` and `AshGraphql` extensions to effortlessly build APIs around your resources
#### Authorize access and work with users
See the {{link:ash:guide:Policies}} guide for information on how to authorize access to your resources using actors and policies.
See the [Policies guide](/documentation/topics/policies.md) for information on how to authorize access to your resources using actors and policies.

View file

@ -2,7 +2,6 @@ defmodule Ash.Api.Dsl do
@execution %Spark.Dsl.Section{
name: :execution,
describe: "Options for how requests are executed using this Api",
links: [],
examples: [
"""
execution do
@ -13,29 +12,30 @@ defmodule Ash.Api.Dsl do
schema: [
timeout: [
type: :timeout,
doc: "The default timeout to use for requests using this API.",
default: 30_000,
links: [
guides: [
"ash:guide:Timeouts"
]
]
doc: """
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",
links: [
guides: [
"ash:guide:Instrumentation"
]
]
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
"""
]
]
}
@authorization %Spark.Dsl.Section{
name: :authorization,
describe: "Options for how requests are authorized using this Api",
describe: """
Options for how requests are authorized using this Api.
See the [security guide](/documentation/topics/security.md) for more.
""",
examples: [
"""
authorization do
@ -44,29 +44,18 @@ defmodule Ash.Api.Dsl do
end
"""
],
links: [
guides: [
"ash:guide:Security"
]
],
schema: [
require_actor?: [
type: :boolean,
default: false,
doc: "Requires that an actor has been supplied.",
links: [
guides: ["ash:guide:Security"]
]
doc: "Requires that an actor has been supplied."
],
authorize: [
type: {:in, [:always, :by_default, :when_requested]},
default: :when_requested,
doc: """
When to run authorization for a given request.
""",
links: [
guides: ["ash:guide:Security"]
]
"""
]
]
}
@ -81,30 +70,22 @@ defmodule Ash.Api.Dsl do
en
"""
],
links: [],
schema: [
allow: [
type: :mfa,
doc: """
Support a dynamic resource list by providing a callback that checks whether or not the resource should be allowed.
""",
links: []
"""
],
allow_unregistered?: [
type: :boolean,
default: false,
links: [],
doc: """
Whether the Api will support only registered entries or not.
"""
],
registry: [
type: {:behaviour, Ash.Registry},
links: [
guides: [
"ash:guide:Quick Start"
]
],
doc: """
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.
"""

View file

@ -14,21 +14,18 @@ defmodule Ash.DataLayer.Ets do
end
"""
],
links: [],
schema: [
private?: [
type: :boolean,
default: false,
doc:
"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.",
links: []
"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: [
type: :atom,
doc: """
The name of the table. Defaults to the resource name.
""",
links: []
"""
]
]
}

View file

@ -13,12 +13,10 @@ defmodule Ash.DataLayer.Mnesia do
end
"""
],
links: [],
schema: [
table: [
type: :atom,
doc: "The table name to use, defaults to the name of the resource",
links: []
doc: "The table name to use, defaults to the name of the resource"
]
]
}

View file

@ -6,37 +6,28 @@ defmodule Ash.Flow.Argument do
name: [
type: :atom,
required: true,
doc: "The name to use for the argument",
links: []
doc: "The name to use for the argument"
],
type: [
type: Ash.OptionsHelpers.ash_type(),
required: true,
doc: "The type of the argument",
links: [
modules: [
"ash:module:Ash.Type"
]
]
doc: "The type of the argument. See `Ash.Type` for more."
],
default: [
type: {:or, [{:mfa_or_fun, 0}, :literal]},
required: false,
doc: "A default value to use for the argument if not provided",
links: []
doc: "A default value to use for the argument if not provided"
],
allow_nil?: [
type: :boolean,
default: true,
doc: "Whether or not the argument value may be nil",
links: []
doc: "Whether or not the argument value may be nil"
],
constraints: [
type: :keyword_list,
default: [],
doc:
"Constraints to provide to the type when casting the value. See the type's documentation for more information.",
links: []
"Constraints to provide to the type when casting the value. See the type's documentation for more information."
]
]

View file

@ -5,7 +5,6 @@ defmodule Ash.Flow.Dsl do
Declares a step that will inspect its input and provide
additional debug information.
""",
links: [],
examples: [
"""
debug :show_some_information do
@ -28,7 +27,6 @@ defmodule Ash.Flow.Dsl do
create :create_post, MyApp.Post, :create
"""
],
links: [],
no_depend_modules: [:resource, :touches_resources],
modules: [:manual],
target: Ash.Flow.Step.Create,
@ -41,7 +39,6 @@ defmodule Ash.Flow.Dsl do
describe: """
Declares a step that will call a update action on a resource.
""",
links: [],
examples: [
"""
update :update_post, MyApp.Post, :update do
@ -61,7 +58,6 @@ defmodule Ash.Flow.Dsl do
describe: """
Validates some input against an action.
""",
links: [],
examples: [
"""
validate :update_post, MyApp.Post, :update do
@ -81,7 +77,6 @@ defmodule Ash.Flow.Dsl do
describe: """
Declares a step that will call a destroy action on a resource.
""",
links: [],
examples: [
"""
destroy :destroy_post, MyApp.Post, :destroy
@ -104,7 +99,6 @@ defmodule Ash.Flow.Dsl do
read :destroy_post, MyApp.Post, :destroy
"""
],
links: [],
no_depend_modules: [:resource, :touches_resources],
modules: [:manual],
target: Ash.Flow.Step.Read,
@ -118,7 +112,6 @@ defmodule Ash.Flow.Dsl do
Runs another flow as part of the current flow.
The return value of the step is the return value of the flow.
""",
links: [],
examples: [
"""
run_flow :get_org, GetOrgByName do
@ -140,7 +133,6 @@ defmodule Ash.Flow.Dsl do
See `Ash.Flow.Step` for the necessary callbacks and more information.
""",
links: [],
examples: [
"""
custom :do_custom_thing, MyApp.DoCustomThing do
@ -176,7 +168,6 @@ defmodule Ash.Flow.Dsl do
end
"""
],
links: [],
no_depend_modules: [:type],
target: Ash.Flow.Argument,
args: [:name, :type],
@ -192,12 +183,10 @@ defmodule Ash.Flow.Dsl do
entities: [
@argument
],
links: [],
schema: [
api: [
type: {:behaviour, Ash.Api},
doc: "An api to use by default when calling actions",
links: []
doc: "An api to use by default when calling actions"
],
description: [
type: :string,
@ -216,8 +205,7 @@ defmodule Ash.Flow.Dsl do
type: :any,
doc: """
The step or step that should constitute the return value.
""",
links: []
"""
]
]
}
@ -233,7 +221,6 @@ defmodule Ash.Flow.Dsl do
target: Ash.Flow.Step.Transaction,
args: [:name, :resource],
recursive_as: :steps,
links: [],
entities: [
steps: @step_entities
],
@ -269,7 +256,6 @@ defmodule Ash.Flow.Dsl do
args: [:name, :over],
recursive_as: :steps,
no_depend_modules: [:touches_resources],
links: [],
entities: [
steps: @step_entities
],
@ -299,7 +285,6 @@ defmodule Ash.Flow.Dsl do
args: [:name, :condition],
recursive_as: :steps,
no_depend_modules: [:touches_resources],
links: [],
entities: [
steps: @step_entities
],
@ -340,7 +325,6 @@ defmodule Ash.Flow.Dsl do
end
"""
],
links: [],
imports: [Ash.Flow.StepHelpers],
entities: [@map, @transaction, @branch] ++ @step_entities
}

View file

@ -2,7 +2,7 @@ defmodule Ash.Flow do
@moduledoc """
A flow is a static definition of a set of steps to be .
See the {{link:ash:guide:Flows}} guide for more.
See the [guide](/documentation/topics/flows.md) for more.
"""
@type t :: module

View file

@ -8,13 +8,11 @@ defmodule Ash.Flow.Step.Branch do
[
condition: [
type: :any,
doc: "A template that must evaluate to `true` for the branch to be executed.",
links: []
doc: "A template that must evaluate to `true` for the branch to be executed."
],
output: [
type: :atom,
doc: "Which step to use as the output. Defaults to the last step.",
links: []
doc: "Which step to use as the output. Defaults to the last step."
]
]
|> Spark.OptionsHelpers.merge_schemas(@shared_opts, "Global Options")

View file

@ -9,15 +9,13 @@ defmodule Ash.Flow.Step.Custom do
custom: [
type: {:spark_function_behaviour, Ash.Flow.Step, {Ash.Flow.Step.CustomFunction, 2}},
doc:
"The module that implements the step behaviour. Also accepts a 2 argument function that takes the input and the context.",
links: []
"The module that implements the step behaviour. Also accepts a 2 argument function that takes the input and the context."
],
async?: [
type: :boolean,
doc: """
Whether or not this step can be run outside of the current process. Defaults to true.
""",
links: [],
default: false
]
]

View file

@ -15,8 +15,7 @@ defmodule Ash.Flow.Step.Destroy do
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.
""",
links: []
"""
]
]
|> Spark.OptionsHelpers.merge_schemas(@shared_opts, "Global Options")

View file

@ -9,13 +9,11 @@ defmodule Ash.Flow.Step.Map do
over: [
type: :any,
doc:
"The value to be iterated over. Will be available inside the `map` step as `element(:map_step_name)`",
links: []
"The value to be iterated over. Will be available inside the `map` step as `element(:map_step_name)`"
],
output: [
type: :atom,
doc: "Which step to use when constructing the output list. Defaults to the last step.",
links: []
doc: "Which step to use when constructing the output list. Defaults to the last step."
]
]
|> Spark.OptionsHelpers.merge_schemas(@shared_opts, "Global Options")

View file

@ -22,7 +22,6 @@ defmodule Ash.Flow.Step.Read do
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`.
""",
links: [],
default: false
],
not_found_error?: [

View file

@ -9,8 +9,7 @@ defmodule Ash.Flow.Step.RunFlow do
flow: [
type: :atom,
doc: "The flow to run.",
required: true,
links: []
required: true
],
input: Ash.Flow.Step.input()
]

View file

@ -24,14 +24,12 @@ defmodule Ash.Flow.Step do
name: [
type: :atom,
required: true,
doc: "The name of the step. Will be used when expressing dependencies, and step inputs.",
links: []
doc: "The name of the step. Will be used when expressing dependencies, and step inputs."
],
short_name: [
type: :string,
doc:
"Set a short name for the step. Will be used when building things like mermaid charts.",
links: []
"Set a short name for the step. Will be used when building things like mermaid charts."
],
wait_for: [
type: :any,
@ -40,15 +38,13 @@ defmodule Ash.Flow.Step do
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)`.
""",
links: []
"""
],
touches_resources: [
type: {:list, :atom},
doc: """
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.
""",
links: []
"""
],
halt_if: [
type: :any,
@ -71,8 +67,7 @@ defmodule Ash.Flow.Step do
type: :string,
doc: """
A description for the step.
""",
links: []
"""
]
]
end
@ -83,25 +78,21 @@ defmodule Ash.Flow.Step do
resource: [
type: :any,
required: true,
doc: "The resource to call the action on.",
links: []
doc: "The resource to call the action on."
],
action: [
type: :any,
required: true,
doc: "The action to call on the resource.",
links: []
doc: "The action to call on the resource."
],
api: [
type: :any,
doc:
"The api to use when calling the action. Defaults to the api set in the `flow` section.",
links: []
"The api to use when calling the action. Defaults to the api set in the `flow` section."
],
tenant: [
type: :any,
doc: "A tenant to use for the operation. May be a template or a literal value.",
links: []
doc: "A tenant to use for the operation. May be a template or a literal value."
],
input: input()
]
@ -113,8 +104,7 @@ defmodule Ash.Flow.Step do
type: :any,
doc: """
A template for the input.
""",
links: []
"""
]
end
end

View file

@ -9,21 +9,18 @@ defmodule Ash.Flow.Step.Transaction do
output: [
type: :any,
doc:
"Which step or steps to use when constructing the output. Defaults to the last step.",
links: []
"Which step or steps to use when constructing the output. Defaults to the last step."
],
timeout: [
type: :timeout,
doc: "A timeout to apply to the transaction.",
links: []
doc: "A timeout to apply to the transaction."
],
resource: [
type:
{:or, [Ash.OptionsHelpers.ash_resource(), {:list, Ash.OptionsHelpers.ash_resource()}]},
doc: """
The Ash resource to use for the transaction.
""",
links: []
"""
]
]
|> Spark.OptionsHelpers.merge_schemas(@shared_opts, "Global Options")

View file

@ -15,8 +15,7 @@ defmodule Ash.Flow.Step.Update do
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.
""",
links: []
"""
]
]
|> Spark.OptionsHelpers.merge_schemas(@shared_opts, "Global Options")

View file

@ -14,8 +14,7 @@ defmodule Ash.Flow.Step.Validate do
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.
""",
links: []
"""
],
only_keys: [
type: {:list, {:or, [:atom, {:list, :atom}]}},
@ -23,8 +22,7 @@ defmodule Ash.Flow.Step.Validate do
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
""",
links: []
"""
]
]
|> Spark.OptionsHelpers.merge_schemas(@shared_opts, "Global Options")

View file

@ -6,13 +6,9 @@ defmodule Ash.Notifier.PubSub do
target: Ash.Notifier.PubSub.Publication,
describe: """
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.
""",
links: [
guides: [
"ash:guide:Pub Sub",
"ash:guide:Notifiers"
]
],
examples: [
"publish :create, \"created\"",
"""
@ -29,13 +25,9 @@ defmodule Ash.Notifier.PubSub do
describe: """
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.
""",
links: [
guides: [
"ash:guide:Pub Sub",
"ash:guide:Notifiers"
]
],
examples: [
"publish_all :create, \"created\""
],
@ -47,6 +39,8 @@ defmodule Ash.Notifier.PubSub do
name: :pub_sub,
describe: """
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.
""",
examples: [
"""
@ -66,24 +60,16 @@ defmodule Ash.Notifier.PubSub do
@publish_all
],
no_depend_modules: [:module],
links: [
guides: [
"ash:guide:Pub Sub",
"ash:guide:Notifiers"
]
],
schema: [
module: [
type: :atom,
doc: "The module to call `broadcast/3` on e.g module.broadcast(topic, event, message).",
required: true,
links: []
required: true
],
prefix: [
type: :string,
doc:
"A prefix for all pubsub messages, e.g `users`. A message with `created` would be published as `users:created`",
links: []
"A prefix for all pubsub messages, e.g `users`. A message with `created` would be published as `users:created`"
],
broadcast_type: [
type: {:one_of, [:notification, :phoenix_broadcast, :broadcast]},
@ -91,8 +77,7 @@ defmodule Ash.Notifier.PubSub do
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>}`
""",
links: []
"""
],
name: [
type: :atom,
@ -102,8 +87,7 @@ defmodule Ash.Notifier.PubSub do
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.
""",
links: []
"""
]
]
}

View file

@ -13,32 +13,27 @@ defmodule Ash.Notifier.PubSub.Publication do
action: [
type: :atom,
doc: "The name of the action that should be published",
required: true,
links: []
required: true
],
topic: [
type: {:custom, __MODULE__, :topic, []},
doc: "The topic to publish",
required: true,
links: []
required: true
],
event: [
type: :string,
doc: "The name of the event to publish. Defaults to the action name",
links: []
doc: "The name of the event to publish. Defaults to the action name"
],
dispatcher: [
type: :atom,
doc:
"The module to use as a dispatcher. If none is set, the pubsub module provided is used.",
links: []
"The module to use as a dispatcher. If none is set, the pubsub module provided is used."
]
]
@publish_all_schema @schema
|> Keyword.update!(:action, &Keyword.delete(&1, :required))
|> Keyword.put(:type,
links: [],
type: {:in, [:create, :update, :destroy]},
doc: "Publish on all actions of a given type"
)

View file

@ -26,19 +26,14 @@ defmodule Ash.Policy.Authorizer do
required: true,
doc: """
The check to run.
""",
links: [
modules: [
"ash:module:Ash.Policy.Check"
]
]
See `Ash.Policy.Check` for more.
"""
],
name: [
type: :string,
required: false,
doc:
"A short name or description for the check, used when explaining authorization results",
links: []
doc: "A short name or description for the check, used when explaining authorization results"
]
]
@ -51,7 +46,6 @@ defmodule Ash.Policy.Authorizer do
"authorize_if logged_in()",
"authorize_if actor_attribute_matches_record(:group, :group)"
],
links: [],
target: Ash.Policy.Check,
transform: {Ash.Policy.Check, :transform, []},
auto_set_fields: [
@ -66,7 +60,6 @@ defmodule Ash.Policy.Authorizer do
schema: @check_schema,
target: Ash.Policy.Check,
transform: {Ash.Policy.Check, :transform, []},
links: [],
examples: [
"forbid_if not_logged_in()",
"forbid_if actor_attribute_matches_record(:group, :blacklisted_groups)"
@ -83,7 +76,6 @@ defmodule Ash.Policy.Authorizer do
schema: @check_schema,
target: Ash.Policy.Check,
transform: {Ash.Policy.Check, :transform, []},
links: [],
examples: [
"authorize_unless not_logged_in()",
"authorize_unless actor_attribute_matches_record(:group, :blacklisted_groups)"
@ -97,7 +89,6 @@ defmodule Ash.Policy.Authorizer do
name: :forbid_unless,
describe: "If the check is true, the request is forbidden, otherwise run remaining checks.",
args: [:check],
links: [],
schema: @check_schema,
target: Ash.Policy.Check,
transform: {Ash.Policy.Check, :transform, []},
@ -112,11 +103,6 @@ defmodule Ash.Policy.Authorizer do
@policy %Spark.Dsl.Entity{
name: :policy,
links: [
guides: [
"ash:guide:Policies"
]
],
describe: """
A policy has a name, a condition, and a list of checks.
@ -141,24 +127,23 @@ defmodule Ash.Policy.Authorizer do
policy always() do
...
end
See the [policies guide](/documentation/topics/policies.md) for more
```
""",
schema: [
description: [
type: :string,
doc: "A description for the policy, used when explaining authorization results",
links: []
doc: "A description for the policy, used when explaining authorization results"
],
access_type: [
type: {:one_of, [:strict, :filter, :runtime]},
links: [],
doc: """
What portion of the checks inside the policy are allowed to run. See the guide for more.
"""
],
condition: [
type: {:custom, __MODULE__, :validate_condition, []},
links: [],
doc: """
A check or list of checks that must be true in order for this policy to apply.
"""
@ -191,12 +176,9 @@ defmodule Ash.Policy.Authorizer do
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.
""",
links: [
guides: [
"ash:guide:Policies"
]
],
examples: [
"""
policies do
@ -231,7 +213,6 @@ defmodule Ash.Policy.Authorizer do
schema: [
default_access_type: [
type: {:one_of, [:strict, :filter, :runtime]},
links: [],
default: :filter,
doc: """
The default access type of policies for this resource.
@ -260,7 +241,7 @@ defmodule Ash.Policy.Authorizer do
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 {{link:ash:guide:Policies}} for practical examples.
See the [policies guide](/documentation/topics/flows.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

View file

@ -4,7 +4,6 @@ defmodule Ash.Registry.Dsl do
describe: "A reference to an ash module (typically a resource)",
target: Ash.Registry.Entry,
args: [:entry],
links: [],
examples: [
"entry MyApp.User"
],
@ -12,8 +11,7 @@ defmodule Ash.Registry.Dsl do
entry: [
type: :atom,
required: true,
doc: "The referenced module",
links: []
doc: "The referenced module"
]
]
}
@ -21,7 +19,6 @@ defmodule Ash.Registry.Dsl do
@entries %Spark.Dsl.Section{
name: :entries,
describe: "List the entries present in this registry",
links: [],
examples: [
"""
entries do
@ -38,8 +35,7 @@ defmodule Ash.Registry.Dsl do
warn_on_empty?: [
type: :boolean,
doc: "Set to `false` to ignore warnings about an empty registry",
default: true,
links: []
default: true
]
]
}

View file

@ -18,55 +18,45 @@ defmodule Ash.Resource.Actions.Argument do
name: [
type: :atom,
required: true,
doc: "The name of the argument",
links: []
doc: "The name of the argument"
],
type: [
type: Ash.OptionsHelpers.ash_type(),
required: true,
doc: "The type of the argument",
links: [
modules: [
"ash:module:Ash.Type"
]
]
doc: "The type of the argument. See `Ash.Type` for more."
],
description: [
type: :string,
doc: "An optional description for the argument.",
links: []
doc: "An optional description for the argument."
],
constraints: [
type: :keyword_list,
default: [],
doc: "Type constraints on the argument",
links: []
doc: "Type constraints on the argument"
],
allow_nil?: [
type: :boolean,
default: true,
doc: "Whether or not the argument value may be nil (or may be not provided)",
links: []
doc: "Whether or not the argument value may be nil (or may be not provided)"
],
private?: [
type: :boolean,
default: false,
doc: "Whether or not the argument should be suppliable by the client.",
links: []
doc: "Whether or not the argument should be suppliable by the client."
],
sensitive?: [
type: :boolean,
default: false,
doc: "Whether or not the attribute value contains sensitive information, like PII.",
links: [
guides: ["ash:guide:Security"]
]
doc: """
Whether or not the attribute value contains sensitive information, like PII.
See the [security guide](/documentation/topics/security.md) for more.
"""
],
default: [
type: :any,
doc:
"The default value for the argument to take. It can be a zero argument function e.g `&MyMod.my_fun/0` or a value",
links: []
"The default value for the argument to take. It can be a zero argument function e.g `&MyMod.my_fun/0` or a value"
]
]
end

View file

@ -46,24 +46,16 @@ defmodule Ash.Resource.Actions.Create do
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.
""",
links: [
guides: [
"ash:guide:Actions"
]
]
"""
],
manual: [
type:
{:spark_function_behaviour, Ash.Resource.ManualCreate,
{Ash.Resource.ManualCreate.Function, 2}},
links: [
guides: [
"ash:guide:Manual Actions"
]
],
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.
"""
],
upsert?: [

View file

@ -38,20 +38,16 @@ defmodule Ash.Resource.Actions.Destroy do
soft?: [
type: :atom,
doc: "If specified, the destroy action behaves as an update internally",
default: false,
links: []
default: false
],
manual: [
type:
{:spark_function_behaviour, Ash.Resource.ManualDestroy,
{Ash.Resource.ManualDestroy.Function, 2}},
links: [
guides: [
"ash:guide:Manual Actions"
]
],
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.
"""
]
]

View file

@ -16,41 +16,31 @@ defmodule Ash.Resource.Actions.Metadata do
name: [
type: :atom,
required: true,
doc: "The name of the metadata",
links: []
doc: "The name of the metadata"
],
type: [
type: :any,
required: true,
doc: "The type of the metadata",
links: [
modules: [
"ash:module:Ash.Type"
]
]
doc: "The type of the metadata. See `Ash.Type` for more."
],
constraints: [
type: :keyword_list,
default: [],
doc: "Type constraints on the metadata",
links: []
doc: "Type constraints on the metadata"
],
description: [
type: :string,
doc: "An optional description for the metadata.",
links: []
doc: "An optional description for the metadata."
],
allow_nil?: [
type: :boolean,
default: true,
doc: "Whether or not the metadata may return `nil`",
links: []
doc: "Whether or not the metadata may return `nil`"
],
default: [
type: :any,
doc:
"The default value for the metadata to take. It can be a zero argument function e.g `&MyMod.my_fun/0` or a value",
links: []
"The default value for the metadata to take. It can be a zero argument function e.g `&MyMod.my_fun/0` or a value"
]
]
end

View file

@ -35,8 +35,7 @@ defmodule Ash.Resource.Actions.Read do
filter: [
type: :any,
doc:
"A filter template that will be applied whenever the action is used. See `Ash.Filter` for more on templates",
links: []
"A filter template that will be applied whenever the action is used. See `Ash.Filter` for more on templates"
],
manual: [
type:
@ -45,12 +44,9 @@ defmodule Ash.Resource.Actions.Read do
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.
""",
links: [
guides: [
"ash:guide:Manual Action"
]
]
See the [manual actions guide](/documentation/topics/manual-actions.md) for more.
"""
],
get?: [
type: :boolean,
@ -58,12 +54,9 @@ defmodule Ash.Resource.Actions.Read do
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.
""",
links: [
guides: [
"ash:guide:Code Interface"
]
]
See the [code interface guide](/documentation/topics/code-interface.md) for more.
"""
],
modify_query: [
type: :mfa,
@ -74,8 +67,7 @@ defmodule Ash.Resource.Actions.Read do
The result must be `{:ok, new_data_layer_query} | {:error, error}`.
Here be dragons.
""",
links: []
"""
]
],
@global_opts,
@ -86,39 +78,33 @@ defmodule Ash.Resource.Actions.Read do
keyset?: [
type: :boolean,
doc: "Whether or not keyset based pagination is supported",
default: false,
links: []
default: false
],
offset?: [
type: :boolean,
doc: "Whether or not offset based pagination is supported",
default: false,
links: []
default: false
],
default_limit: [
type: :pos_integer,
doc: "The default page size to apply, if one is not supplied",
links: []
doc: "The default page size to apply, if one is not supplied"
],
countable: [
type: {:in, [true, false, :by_default]},
doc:
"Whether not a returned page will have a full count of all records. Use `:by_default` to do it automatically.",
default: false,
links: []
default: false
],
max_page_size: [
type: :pos_integer,
doc: "The maximum amount of records that can be requested in a single page",
default: 250,
links: []
default: 250
],
required?: [
type: :boolean,
doc:
"Whether or not pagination can be disabled. Only relevant if some pagination configuration is supplied.",
default: true,
links: []
default: true
]
]

View file

@ -5,19 +5,16 @@ defmodule Ash.Resource.Actions.SharedOptions do
name: [
type: :atom,
required: true,
doc: "The name of the action",
links: []
doc: "The name of the action"
],
primary?: [
type: :boolean,
default: false,
doc: "Whether or not this action should be used when no action is specified by the caller.",
links: []
doc: "Whether or not this action should be used when no action is specified by the caller."
],
description: [
type: :string,
doc: "An optional description for the action",
links: []
doc: "An optional description for the action"
],
transaction?: [
type: :boolean,
@ -25,23 +22,20 @@ defmodule Ash.Resource.Actions.SharedOptions do
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.
""",
links: []
"""
],
touches_resources: [
type: {:list, :atom},
doc: """
A list of resources that the action may touch, used when building transactions.
""",
links: []
"""
]
]
@create_update_opts [
accept: [
type: {:or, [in: [:all], list: :atom]},
doc: "The list of attributes to accept. Defaults to all attributes on the resource",
links: []
doc: "The list of attributes to accept. Defaults to all attributes on the resource"
],
reject: [
type: {:or, [in: [:all], list: :atom]},
@ -50,12 +44,10 @@ defmodule Ash.Resource.Actions.SharedOptions do
If this is specified along with `accept`, then everything in the `accept` list minus any matches in the
`reject` list will be accepted.
""",
links: []
"""
],
require_attributes: [
type: {:list, :atom},
links: [],
doc: """
A list of attributes that would normally `allow_nil?`, to require for this action.
@ -64,18 +56,14 @@ defmodule Ash.Resource.Actions.SharedOptions do
],
error_handler: [
type: :mfa,
links: [],
doc: "Sets the error handler on the changeset. See `Ash.Changeset.handle_errors/2` for more"
],
manual?: [
type: :boolean,
links: [
guides: [
"ash:guide:Manual Actions"
]
],
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.
"""
]
]

View file

@ -40,14 +40,11 @@ defmodule Ash.Resource.Actions.Update do
type:
{:spark_function_behaviour, Ash.Resource.ManualUpdate,
{Ash.Resource.ManualUpdate.Function, 2}},
links: [
guides: [
"ash:guide:Manual Actions"
]
],
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.
"""
]
]

View file

@ -20,14 +20,12 @@ defmodule Ash.Resource.Aggregate do
name: [
type: :atom,
doc: "The field to place the aggregate in",
required: true,
links: []
required: true
],
relationship_path: [
type: {:custom, __MODULE__, :relationship_path, []},
doc: "The relationship or relationship path to use for the aggregate",
required: true,
links: []
required: true
],
kind: [
type:
@ -37,49 +35,41 @@ defmodule Ash.Resource.Aggregate do
{:tuple, [{:in, [:custom]}, Ash.OptionsHelpers.ash_type()]}
]},
doc: "The kind of the aggregate",
required: true,
links: []
required: true
],
field: [
type: :atom,
doc:
"The field to aggregate. Defaults to the first field in the primary key of the resource",
required: false,
links: []
required: false
],
filter: [
type: :any,
doc: "A filter to apply to the aggregate",
default: [],
links: []
default: []
],
sort: [
type: :any,
doc: "A sort to be applied to the aggregate",
links: []
doc: "A sort to be applied to the aggregate"
],
description: [
type: :string,
doc: "An optional description for the aggregate",
links: []
doc: "An optional description for the aggregate"
],
default: [
type: :any,
doc: "A default value to use in cases where nil would be used. Count defaults to `0`.",
links: []
doc: "A default value to use in cases where nil would be used. Count defaults to `0`."
],
private?: [
type: :boolean,
default: false,
doc:
"Whether or not the aggregate will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql",
links: []
"Whether or not the aggregate will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql"
],
filterable?: [
type: {:or, [:boolean, {:in, [:simple_equality]}]},
default: true,
doc: "Whether or not the aggregate should be usable in filters.",
links: []
doc: "Whether or not the aggregate should be usable in filters."
]
]

View file

@ -48,51 +48,41 @@ defmodule Ash.Resource.Attribute do
@schema [
name: [
type: :atom,
doc: "The name of the attribute.",
links: []
doc: "The name of the attribute."
],
type: [
type: Ash.OptionsHelpers.ash_type(),
doc: "The type of the attribute.",
links: [
modules: ["ash:module:Ash.Type"]
]
doc: "The type of the attribute. See `Ash.Type` for more."
],
constraints: [
type: :keyword_list,
doc:
"Constraints to provide to the type when casting the value. See the type's documentation for more information.",
links: [
modules: ["ash:module:Ash.Type"]
]
"Constraints to provide to the type when casting the value. See the type's documentation for more information. See `Ash.Type` for more."
],
description: [
type: :string,
doc: "An optional description for the attribute.",
links: []
doc: "An optional description for the attribute."
],
sensitive?: [
type: :boolean,
default: false,
doc: "Whether or not the attribute value contains sensitive information, like PII.",
links: [
guides: ["ash:guide:Security"]
]
doc: """
Whether or not the attribute value contains sensitive information, like PII.
See the [Security guide](/documentation/topics/security.md) for more.
"""
],
source: [
type: :atom,
doc: """
If the field should be mapped to a different name in the data layer. Support varies by data layer.
""",
links: []
"""
],
always_select?: [
type: :boolean,
default: false,
doc: """
Whether or not to ensure this attribute is always selected when reading from the database.
""",
links: []
"""
],
primary_key?: [
type: :boolean,
@ -100,57 +90,46 @@ defmodule Ash.Resource.Attribute do
doc: """
Whether or not the attribute is part of the primary key (one or more fields that uniquely identify a resource)."
If primary_key? is true, allow_nil? must be false.
""",
links: []
"""
],
allow_nil?: [
type: :boolean,
default: true,
doc: "Whether or not the attribute can be set to nil.",
links: []
doc: "Whether or not the attribute can be set to nil."
],
generated?: [
type: :boolean,
default: false,
doc: "Whether or not the value may be generated by the data layer.",
links: [
guides: ["ash:guide:Actions"]
]
doc: """
Whether or not the value may be generated by the data layer.
"""
],
writable?: [
type: :boolean,
default: true,
doc: "Whether or not the value can be written to.",
links: []
doc: "Whether or not the value can be written to."
],
private?: [
type: :boolean,
default: false,
doc:
"Whether or not the attribute can be provided as input, or will be shown when extensions work with the resource (i.e won't appear in a web api).",
links: [
guides: ["ash:guide:Security"]
]
doc: """
Whether or not the attribute can be provided as input, or will be shown when extensions work with the resource (i.e won't appear in a web api).
See the [security guide](/documentation/topics/security.md) for more.
"""
],
default: [
type: {:or, [{:mfa_or_fun, 0}, :literal]},
doc: "A value to be set on all creates, unless a value is being provided already.",
links: [
guides: ["ash:guide:Actions"]
]
doc: "A value to be set on all creates, unless a value is being provided already."
],
update_default: [
type: {:or, [{:mfa_or_fun, 0}, :literal]},
doc: "A value to be set on all updates, unless a value is being provided already.",
links: [
guides: ["ash:guide:Actions"]
]
doc: "A value to be set on all updates, unless a value is being provided already."
],
filterable?: [
type: {:or, [:boolean, {:in, [:simple_equality]}]},
default: true,
doc: "Whether or not the attribute can be referenced in filters.",
links: []
doc: "Whether or not the attribute can be referenced in filters."
],
match_other_defaults?: [
type: :boolean,
@ -160,10 +139,7 @@ defmodule Ash.Resource.Attribute do
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.
""",
links: [
guides: ["ash:guide:Actions"]
]
"""
]
]

View file

@ -20,37 +20,21 @@ defmodule Ash.Resource.Calculation do
name: [
type: :atom,
required: true,
links: [],
doc: "The field name to use for the calculation value"
],
type: [
type: :any,
links: [
modules: [
"ash:module:Ash.Type"
]
],
doc: "The type of the calculation",
doc: "The type of the calculation. See `Ash.Type` for more.",
required: true
],
constraints: [
type: :keyword_list,
default: [],
links: [
modules: [
"ash:module:Ash.Type"
]
],
doc: "Constraints to provide to the type."
doc: "Constraints to provide to the type. See `Ash.Type` for more."
],
allow_async?: [
type: :boolean,
default: false,
links: [
guides: [
"ash:guide:Actions"
]
],
doc: """
If set to `true`, then the calculation may be run after the main query.
"""
@ -63,7 +47,6 @@ defmodule Ash.Resource.Calculation do
{:custom, __MODULE__, :expr_calc, []}
]},
required: true,
links: [],
doc: """
The module or `{module, opts}` to use for the calculation
Also accepts a function that takes the list of records and the context.
@ -71,42 +54,35 @@ defmodule Ash.Resource.Calculation do
],
description: [
type: :string,
links: [],
doc: "An optional description for the calculation"
],
private?: [
type: :boolean,
default: false,
links: [
guides: [
"ash:guide:Security"
]
],
doc:
"Whether or not the calculation will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql"
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.
"""
],
select: [
type: {:list, :atom},
default: [],
links: [],
doc: "A list of fields to ensure selected if the calculation is used."
],
load: [
type: :any,
default: [],
links: [],
doc: "A load statement to be applied if the calculation is used."
],
allow_nil?: [
type: :boolean,
default: true,
links: [],
doc: "Whether or not the calculation can return nil."
],
filterable?: [
type: {:or, [:boolean, {:in, [:simple_equality]}]},
default: true,
links: [],
doc: "Whether or not the calculation should be usable in filters."
]
]
@ -130,47 +106,33 @@ defmodule Ash.Resource.Calculation do
name: [
type: :atom,
required: true,
doc: "The name of the argument",
links: []
doc: "The name of the argument"
],
type: [
type: Ash.OptionsHelpers.ash_type(),
required: true,
links: [
modules: [
"ash:module:Ash.Type"
]
],
doc: "The type of the argument"
doc: "The type of the argument. See `Ash.Type` for more."
],
default: [
type: {:or, [{:mfa_or_fun, 0}, :literal]},
required: false,
links: [],
doc: "A default value to use for the argument if not provided"
],
allow_nil?: [
type: :boolean,
default: true,
links: [],
doc: "Whether or not the argument value may be nil (or may be not provided)"
],
allow_expr?: [
type: :boolean,
default: false,
links: [],
doc: "Allow passing expressions as argument values. Expressions cannot be type validated."
],
constraints: [
type: :keyword_list,
default: [],
links: [
modules: [
"ash:module:Ash.Type"
]
],
doc:
"Constraints to provide to the type when casting the value. See the type's documentation for more information."
"Constraints to provide to the type when casting the value. See the type's documentation and `Ash.Type` for more."
]
]

View file

@ -10,14 +10,12 @@ defmodule Ash.Resource.CalculationInterface do
name: [
type: :atom,
doc: "The name of the function that will be defined",
required: true,
links: []
required: true
],
calculation: [
type: :atom,
doc:
"The name of the calculation that will be evaluated. Defaults to the same name as the function.",
links: []
"The name of the calculation that will be evaluated. Defaults to the same name as the function."
],
args: [
type: :any,
@ -38,8 +36,7 @@ defmodule Ash.Resource.CalculationInterface do
```elixir
define_calculation :id_matches, args: [{:arg, :id}, {:optional, {:ref, :id}}]
```
""",
links: []
"""
]
]

View file

@ -22,34 +22,28 @@ defmodule Ash.Resource.Change do
default: [:create, :update],
doc: """
The action types the validation should run on. Destroy actions are omitted by default as most changes don't make sense for a destroy.
""",
links: []
"""
],
only_when_valid?: [
type: :boolean,
default: false,
links: [],
doc: """
If the change should only be run on valid changes. By default, all changes are run unless stated otherwise here.
"""
],
description: [
type: :string,
doc: "An optional description for the change",
links: []
doc: "An optional description for the change"
],
change: [
type:
{:spark_function_behaviour, Ash.Resource.Change, Ash.Resource.Change.Builtins,
{Ash.Resource.Change.Function, 2}},
links: [
modules: [
"ash:module:Ash.Resource.Change.Builtins"
]
],
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.
""",
required: true
],
@ -59,11 +53,6 @@ defmodule Ash.Resource.Change do
{:spark_function_behaviour, Ash.Resource.Validation, Ash.Resource.Validation.Builtins,
{Ash.Resource.Validation.Function, 1}}},
required: false,
links: [
modules: [
"ash:module:Ash.Resource.Validation.Builtins"
]
],
default: [],
doc: """
Validations that should pass in order for this validation to apply.

View file

@ -4,7 +4,6 @@ defmodule Ash.Resource.Dsl do
describe: """
Declares an attribute on the resource.
""",
links: [],
examples: [
"""
attribute :name, :string do
@ -27,7 +26,6 @@ defmodule Ash.Resource.Dsl do
examples: [
"create_timestamp :inserted_at"
],
links: [],
target: Ash.Resource.Attribute,
args: [:name],
schema: Ash.Resource.Attribute.create_timestamp_schema(),
@ -39,7 +37,6 @@ defmodule Ash.Resource.Dsl do
describe: """
Declares a non-writable attribute with a create and update default of `&DateTime.utc_now/0`
""",
links: [],
examples: [
"update_timestamp :inserted_at"
],
@ -59,7 +56,6 @@ defmodule Ash.Resource.Dsl do
examples: [
"integer_primary_key :id"
],
links: [],
args: [:name],
target: Ash.Resource.Attribute,
schema: Ash.Resource.Attribute.integer_primary_key_schema(),
@ -75,7 +71,6 @@ defmodule Ash.Resource.Dsl do
examples: [
"uuid_primary_key :id"
],
links: [],
args: [:name],
target: Ash.Resource.Attribute,
schema: Ash.Resource.Attribute.uuid_primary_key_schema(),
@ -89,7 +84,6 @@ defmodule Ash.Resource.Dsl do
A section for declaring attributes on the resource.
""",
imports: [Ash.Resource.Attribute.Helpers],
links: [],
examples: [
"""
attributes do
@ -137,6 +131,8 @@ defmodule Ash.Resource.Dsl do
Declares a has_one relationship. In a relational database, the foreign key would be on the *other* table.
Generally speaking, a `has_one` also implies that the destination table is unique on that foreign key.
See the [relationships guide](/documentation/topics/relationships.md) for more.
""",
examples: [
"""
@ -147,11 +143,6 @@ defmodule Ash.Resource.Dsl do
end
"""
],
links: [
guides: [
"ash:guide:Relationships"
]
],
modules: [:destination],
target: Ash.Resource.Relationships.HasOne,
schema: Ash.Resource.Relationships.HasOne.opt_schema(),
@ -162,6 +153,8 @@ defmodule Ash.Resource.Dsl do
name: :has_many,
describe: """
Declares a has_many relationship. There can be any number of related entities.
See the [relationships guide](/documentation/topics/relationships.md) for more.
""",
examples: [
"""
@ -172,11 +165,6 @@ defmodule Ash.Resource.Dsl do
end
"""
],
links: [
guides: [
"ash:guide:Relationships"
]
],
target: Ash.Resource.Relationships.HasMany,
modules: [:destination],
schema: Ash.Resource.Relationships.HasMany.opt_schema(),
@ -189,12 +177,9 @@ defmodule Ash.Resource.Dsl do
Declares a many_to_many relationship. Many to many relationships require a join resource.
A join resource is a resource that consists of a relationship to the source and destination of the many to many.
See the [relationships guide](/documentation/topics/relationships.md) for more.
""",
links: [
guides: [
"ash:guide:Relationships"
]
],
examples: [
"""
# In a resource called `Word`
@ -224,12 +209,9 @@ defmodule Ash.Resource.Dsl do
Declares a belongs_to relationship. In a relational database, the foreign key would be on the *source* table.
This creates a field on the resource with the corresponding name and type, unless `define_attribute?: false` is provided.
See the [relationships guide](/documentation/topics/relationships.md) for more.
""",
links: [
guides: [
"ash:guide:Relationships"
]
],
examples: [
"""
# In a resource called `Word`
@ -252,12 +234,9 @@ defmodule Ash.Resource.Dsl do
Relationships are a core component of resource oriented design. Many components of Ash
will use these relationships. A simple use case is loading relationships (done via the `Ash.Query.load/2`).
See the [relationships guide](/documentation/topics/relationships.md) for more.
""",
links: [
guides: [
"ash:guide:Relationships"
]
],
examples: [
"""
relationships do
@ -308,15 +287,9 @@ defmodule Ash.Resource.Dsl do
name: :change,
describe: """
A change to be applied to the changeset.
See `Ash.Resource.Change` for more.
""",
links: [
guides: [
"ash:guide:Actions"
],
modules: [
"ash:module:Ash.Resource.Change"
]
],
examples: [
"change relate_actor(:reporter)",
"change {MyCustomChange, :foo}"
@ -332,7 +305,6 @@ defmodule Ash.Resource.Dsl do
describe: """
Declares an argument on the action
""",
links: [],
examples: [
"argument :password_confirmation, :string"
],
@ -349,7 +321,6 @@ defmodule Ash.Resource.Dsl do
A special kind of attribute that is only added to specific actions. Nothing sets this value, it must be set in a custom
change via `Ash.Resource.Info.put_metadata/3`.
""",
links: [],
examples: [
"""
metadata :api_token, :string, allow_nil?: false
@ -366,16 +337,10 @@ defmodule Ash.Resource.Dsl do
@change %Spark.Dsl.Entity{
name: :change,
links: [
guides: [
"ash:guide:Actions"
],
modules: [
"ash:module:Ash.Resource.Change"
]
],
describe: """
A change to be applied to the changeset.
See `Ash.Resource.Change` for more.
""",
examples: [
"change relate_actor(:reporter)",
@ -391,19 +356,13 @@ defmodule Ash.Resource.Dsl do
name: :validate,
describe: """
Declares a validation for creates and updates.
See `Ash.Resource.Change` for more.
""",
examples: [
"validate {Mod, [foo: :bar]}",
"validate at_least_one_of_present([:first_name, :last_name])"
],
links: [
guides: [
"ash:guide:Actions"
],
modules: [
"ash:module:Ash.Resource.Change"
]
],
target: Ash.Resource.Validation,
schema: Ash.Resource.Validation.opt_schema(),
no_depend_modules: [:validation],
@ -415,18 +374,12 @@ defmodule Ash.Resource.Dsl do
name: :validate,
describe: """
Declares a validation to be applied to the changeset.
See `Ash.Resource.Validation` for more.
""",
examples: [
"validate changing(:email)"
],
links: [
guides: [
"ash:guide:Actions"
],
modules: [
"ash:module:Ash.Resource.Validation"
]
],
target: Ash.Resource.Validation,
schema: Ash.Resource.Validation.action_schema(),
no_depend_modules: [:validation],
@ -451,11 +404,6 @@ defmodule Ash.Resource.Dsl do
Ash.Resource.Validation.Builtins,
Ash.Filter.TemplateHelpers
],
links: [
guides: [
"ash:guide:Actions"
]
],
target: Ash.Resource.Actions.Create,
schema: Ash.Resource.Actions.Create.opt_schema(),
modules: [:manual],
@ -485,11 +433,6 @@ defmodule Ash.Resource.Dsl do
prepare build(sort: [:foo, :bar])
"""
],
links: [
guides: [
"ash:guide:Actions"
]
],
target: Ash.Resource.Preparation,
schema: Ash.Resource.Preparation.schema(),
no_depend_modules: [:preparation],
@ -503,12 +446,7 @@ defmodule Ash.Resource.Dsl do
""",
target: Ash.Resource.Actions.Read.Pagination,
schema: Ash.Resource.Actions.Read.pagination_schema(),
transform: {Ash.Resource.Actions.Read.Pagination, :transform, []},
links: [
guides: [
"ash:guide:Pagination"
]
]
transform: {Ash.Resource.Actions.Read.Pagination, :transform, []}
}
@read %Spark.Dsl.Entity{
@ -532,11 +470,6 @@ defmodule Ash.Resource.Dsl do
transform: {Ash.Resource.Actions.Read, :transform, []},
no_depend_modules: [:touches_resources],
modules: [:manual],
links: [
guides: [
"ash:guide:Actions"
]
],
entities: [
arguments: [
@action_argument
@ -564,11 +497,6 @@ defmodule Ash.Resource.Dsl do
examples: [
"update :flag_for_review, primary?: true"
],
links: [
guides: [
"ash:guide:Actions"
]
],
modules: [:manual],
entities: [
changes: [
@ -606,11 +534,6 @@ defmodule Ash.Resource.Dsl do
Ash.Resource.Validation.Builtins,
Ash.Filter.TemplateHelpers
],
links: [
guides: [
"ash:guide:Actions"
]
],
no_depend_modules: [:touches_resources],
entities: [
changes: [
@ -641,11 +564,6 @@ defmodule Ash.Resource.Dsl do
your resources to conform to your business logic. It is normal and expected to have
multiple actions of each type in a large application.
""",
links: [
guides: [
"ash:guide:Actions"
]
],
schema: [
defaults: [
type: {:list, {:in, [:create, :read, :update, :destroy]}},
@ -656,14 +574,12 @@ defmodule Ash.Resource.Dsl do
By default, resources have no default actions. Embedded resources, however, have a default
of all resource types.
""",
links: []
"""
],
default_accept: [
type: {:list, :atom},
doc:
"A default value for the `accept` option for each action. Defaults to all public attributes.",
links: []
"A default value for the `accept` option for each action. Defaults to all public attributes."
]
],
examples: [
@ -708,16 +624,13 @@ defmodule Ash.Resource.Dsl do
name: :identity,
describe: """
Represents a unique constraint on the resource.
See the [identities guide](/documentation/topics/identities.md) for more.
""",
examples: [
"identity :name, [:name]",
"identity :full_name, [:first_name, :last_name]"
],
links: [
guides: [
"ash:guide:Identities"
]
],
target: Ash.Resource.Identity,
schema: Ash.Resource.Identity.schema(),
no_depend_modules: [:pre_check_with, :eager_check_with],
@ -726,7 +639,6 @@ defmodule Ash.Resource.Dsl do
@identities %Spark.Dsl.Section{
name: :identities,
links: [],
describe: """
Unique identifiers for the resource
""",
@ -756,43 +668,36 @@ defmodule Ash.Resource.Dsl do
end
"""
],
links: [],
imports: [Ash.Filter.TemplateHelpers],
modules: [:simple_notifiers],
schema: [
description: [
type: :string,
doc:
"A human readable description of the resource, to be used in generated documentation",
links: []
doc: "A human readable description of the resource, to be used in generated documentation"
],
base_filter: [
type: :any,
doc: "A filter statement to be applied to any queries on the resource",
links: []
doc: "A filter statement to be applied to any queries on the resource"
],
default_context: [
type: :any,
doc: "Default context to apply to any queries/changesets generated for this resource.",
links: []
doc: "Default context to apply to any queries/changesets generated for this resource."
],
trace_name: [
type: :string,
doc: "The name to use in traces. Defaults to the short_name stringified",
links: [
guides: [
"ash:guide:Instrumentation"
]
]
doc: """
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.",
links: [
guides: [
"ash:guide:Instrumentation"
]
]
doc: """
A short identifier for the resource, which should be unique.
See the [monitoring guide](/documentation/topics/monitoring.md) for more.
"""
],
simple_notifiers: [
type: {:list, :module},
@ -806,6 +711,8 @@ defmodule Ash.Resource.Dsl do
name: :define_calculation,
describe: """
Defines a function with the corresponding name and arguments, that evaluates a calculation.
See the [code interface guide](/documentation/topics/code-interface.md) for more.
""",
examples: [
"define_calculation :referral_link, args: [:id]",
@ -814,11 +721,6 @@ defmodule Ash.Resource.Dsl do
target: Ash.Resource.CalculationInterface,
schema: Ash.Resource.CalculationInterface.schema(),
transform: {Ash.Resource.CalculationInterface, :transform, []},
links: [
guides: [
"ash:guide:Code Interface"
]
],
args: [:name]
}
@ -826,17 +728,14 @@ defmodule Ash.Resource.Dsl do
name: :define,
describe: """
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"
],
target: Ash.Resource.Interface,
schema: Ash.Resource.Interface.schema(),
links: [
guides: [
"ash:guide:Code Interface"
]
],
transform: {Ash.Resource.Interface, :transform, []},
args: [:name]
}
@ -845,6 +744,8 @@ defmodule Ash.Resource.Dsl do
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.
""",
examples: [
"""
@ -861,13 +762,7 @@ defmodule Ash.Resource.Dsl do
type: {:spark, Ash.Api},
doc:
"Defines the code interface on the resource module directly, using the provided Api.",
default: false,
links: []
]
],
links: [
guides: [
"ash:guide:Code Interface"
default: false
]
],
entities: [
@ -881,9 +776,6 @@ defmodule Ash.Resource.Dsl do
describe: """
Declare validations prior to performing actions against the resource
""",
links: [
guides: ["ash:guide:Actions"]
],
imports: [Ash.Resource.Validation.Builtins],
examples: [
"""
@ -902,20 +794,14 @@ defmodule Ash.Resource.Dsl do
name: :changes,
describe: """
Declare changes that occur on create/update/destroy actions against the resource
See `Ash.Resource.Change` for more.
""",
imports: [
Ash.Resource.Validation.Builtins,
Ash.Resource.Change.Builtins,
Ash.Filter.TemplateHelpers
],
links: [
guides: [
"ash:guide:Actions"
],
modules: [
"ash:module:Ash.Resource.Change"
]
],
examples: [
"""
changes do
@ -934,11 +820,6 @@ defmodule Ash.Resource.Dsl do
describe: """
Declare preparations that occur on all read actions for a given resource
""",
links: [
guides: [
"ash:guide:Actions"
]
],
imports: [Ash.Resource.Preparation.Builtins, Ash.Resource.Validation.Builtins],
examples: [
"""
@ -955,15 +836,12 @@ defmodule Ash.Resource.Dsl do
@count %Spark.Dsl.Entity{
name: :count,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named count aggregate on the resource
Supports `filter`, but not `sort` (because that wouldn't affect the count)
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -980,16 +858,13 @@ defmodule Ash.Resource.Dsl do
@first %Spark.Dsl.Entity{
name: :first,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `first` aggregate on the resource
First aggregates return the first value of the related record
that matches. Supports both `filter` and `sort`.
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1007,15 +882,12 @@ defmodule Ash.Resource.Dsl do
@max %Spark.Dsl.Entity{
name: :max,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `max` aggregate on the resource
Supports `filter`, but not `sort` (because that wouldn't affect the max)
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1032,15 +904,12 @@ defmodule Ash.Resource.Dsl do
@min %Spark.Dsl.Entity{
name: :min,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `min` aggregate on the resource
Supports `filter`, but not `sort` (because that wouldn't affect the min)
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1057,15 +926,12 @@ defmodule Ash.Resource.Dsl do
@sum %Spark.Dsl.Entity{
name: :sum,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `sum` aggregate on the resource
Supports `filter`, but not `sort` (because that wouldn't affect the sum)
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1082,15 +948,12 @@ defmodule Ash.Resource.Dsl do
@avg %Spark.Dsl.Entity{
name: :avg,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `avg` aggregate on the resource
Supports `filter`, but not `sort` (because that wouldn't affect the avg)
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1107,11 +970,6 @@ defmodule Ash.Resource.Dsl do
@custom %Spark.Dsl.Entity{
name: :custom,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `custom` aggregate on the resource
@ -1119,7 +977,7 @@ defmodule Ash.Resource.Dsl do
Custom aggregates provide an `implementation` which must implement data layer specific callbacks.
See the relevant data layer documentation for more.
See the relevant data layer documentation and the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1147,16 +1005,13 @@ defmodule Ash.Resource.Dsl do
@list %Spark.Dsl.Entity{
name: :list,
links: [
guides: [
"ash:guide:Aggregates"
]
],
describe: """
Declares a named `list` aggregate on the resource.
A list aggregate selects the list of all values for the given field
and relationship combination.
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1178,6 +1033,8 @@ defmodule Ash.Resource.Dsl do
These are aggregates that can be loaded only by name using `Ash.Query.load/2`.
They are also available as top level fields on the resource.
See the [aggregates guide](/documentation/topics/aggregates.md) for more.
""",
examples: [
"""
@ -1188,11 +1045,6 @@ defmodule Ash.Resource.Dsl do
end
"""
],
links: [
guides: [
"ash:guide:Aggregates"
]
],
imports: [Ash.Filter.TemplateHelpers],
entities: [
@count,
@ -1210,6 +1062,8 @@ defmodule Ash.Resource.Dsl do
name: :argument,
describe: """
An argument to be passed into the calculation's arguments map
See the [calculations guide](/documentation/topics/calculations.md) for more.
""",
examples: [
"""
@ -1223,11 +1077,6 @@ defmodule Ash.Resource.Dsl do
end
"""
],
links: [
guides: [
"ash:guide:Calculations"
]
],
target: Ash.Resource.Calculation.Argument,
args: [:name, :type],
schema: Ash.Resource.Calculation.Argument.schema(),
@ -1247,6 +1096,8 @@ defmodule Ash.Resource.Dsl do
1.) Specifying the `select` option on a calculation in the resource.
2.) Define a `select/2` callback in the calculation module
3.) Set `always_select?` on the attribute in question
See the [calculations guide](/documentation/topics/calculations.md) for more.
""",
examples: [
{
@ -1258,11 +1109,6 @@ defmodule Ash.Resource.Dsl do
"calculate :full_name, :string, expr(first_name <> \" \" <> last_name "
}
],
links: [
guides: [
"ash:guide:Calculation"
]
],
target: Ash.Resource.Calculation,
no_depend_modules: [:calculation],
args: [:name, :type, :calculation],
@ -1275,16 +1121,13 @@ defmodule Ash.Resource.Dsl do
@calculations %Spark.Dsl.Section{
name: :calculations,
links: [
guides: [
"ash:guide:Calculations"
]
],
describe: """
Declare named calculations on the resource.
These are calculations that can be loaded only by name using `Ash.Query.load/2`.
They are also available as top level fields on the resource.
See the [calculations guide](/documentation/topics/calculations.md) for more.
""",
examples: [
"""
@ -1306,6 +1149,8 @@ defmodule Ash.Resource.Dsl do
To specify a tenant, use `Ash.Query.set_tenant/2` or
`Ash.Changeset.set_tenant/2` before passing it to an operation.
See the [multitenancy guide](/documentation/topics/multitenancy.md)
""",
examples: [
"""
@ -1316,41 +1161,32 @@ defmodule Ash.Resource.Dsl do
end
"""
],
links: [
guides: [
"ash:guide:Multitenancy"
]
],
schema: [
strategy: [
type: {:in, [:context, :attribute]},
default: :context,
doc: """
Determine if multitenancy is performed with attribute filters or using data layer features.
""",
links: []
"""
],
attribute: [
type: :atom,
doc: """
If using the `attribute` strategy, the attribute to use, e.g `org_id`
""",
links: []
"""
],
global?: [
type: :boolean,
doc: """
Whether or not the data also exists outside of each tenant.
""",
default: false,
links: []
default: false
],
parse_attribute: [
type: :mfa,
doc:
"An mfa ({module, function, args}) pointing to a function that takes a tenant and returns the attribute value",
default: {__MODULE__, :identity, []},
links: []
default: {__MODULE__, :identity, []}
]
]
}

View file

@ -15,42 +15,32 @@ defmodule Ash.Resource.Identity do
name: [
type: :atom,
required: true,
doc: "The name of the identity.",
links: []
doc: "The name of the identity."
],
keys: [
type: {:custom, __MODULE__, :keys, []},
required: true,
doc: "The names of the attributes that uniquely identify this resource.",
links: []
doc: "The names of the attributes that uniquely identify this resource."
],
eager_check_with: [
type: {:behaviour, Ash.Api},
doc: """
Validates that the unique identity provided is unique at validation time, outside of any transactions, using the api module provided.
""",
links: []
"""
],
pre_check_with: [
type: {:behaviour, Ash.Api},
doc: """
Validates that the unique identity provided is unique in a before_action hook.
""",
links: []
"""
],
description: [
type: :string,
doc: "An optional description for the identity",
links: []
doc: "An optional description for the identity"
],
message: [
type: :string,
doc: "An error message to use when the unique identity would be violated",
links: [
guides: [
"ash:guide:Errors"
]
]
doc: "An error message to use when the unique identity would be violated"
]
]

View file

@ -95,21 +95,18 @@ defmodule Ash.Resource.Interface do
name: [
type: :atom,
doc: "The name of the function that will be defined",
required: true,
links: []
required: true
],
action: [
type: :atom,
doc:
"The name of the action that will be called. Defaults to the same name as the function.",
links: []
"The name of the action that will be called. Defaults to the same name as the function."
],
args: [
type: {:list, {:or, [:atom, {:tagged_tuple, :optional, :atom}]}},
doc: """
Map specific arguments to named inputs. Can provide any argument/attributes that the action allows.
""",
links: []
"""
],
not_found_error?: [
type: :boolean,
@ -121,22 +118,19 @@ defmodule Ash.Resource.Interface do
type: :boolean,
doc: """
Expects to only receive a single result from a read action, and returns a single result instead of a list. Ignored for other action types.
""",
links: []
"""
],
get_by: [
type: {:list, :atom},
doc: """
Takes a list of fields and adds those fields as arguments, which will then be used to filter. Sets `get?` to true automatically. Ignored for non-read actions.
""",
links: []
"""
],
get_by_identity: [
type: :atom,
doc: """
Only relevant for read actions. Takes an identity, and gets its field list, performing the same logic as `get_by` once it has the list of fields.
""",
links: []
"""
]
]

View file

@ -28,13 +28,7 @@ defmodule Ash.Resource.Preparation do
The module and options for a preparation.
Also accepts functions take the query and the context.
""",
required: true,
links: [
modules: [
"ash:module:Ash.Resource.Preparation",
"ash:module:Ash.Resource.Preparation.Builtins"
]
]
required: true
]
]
end

View file

@ -63,7 +63,6 @@ defmodule Ash.Resource.Relationships.BelongsTo do
[
primary_key?: [
type: :boolean,
links: [],
default: false,
doc:
"Whether the generated attribute is, or is part of, the primary key of a resource."
@ -71,14 +70,12 @@ defmodule Ash.Resource.Relationships.BelongsTo do
allow_nil?: [
type: :boolean,
default: true,
links: [],
doc:
"Whether this relationship must always be present, e.g: must be included on creation, and never removed (it may be modified). The generated attribute will not allow nil values."
],
attribute_writable?: [
type: :boolean,
default: false,
links: [],
doc: """
Whether the generated attribute will be marked as public & writable.
"""
@ -86,19 +83,13 @@ defmodule Ash.Resource.Relationships.BelongsTo do
define_attribute?: [
type: :boolean,
default: true,
links: [],
doc:
"If set to `false` an attribute is not created on the resource for this relationship, and one must be manually added in `attributes`, invalidating many other options."
],
attribute_type: [
type: :any,
links: [
modules: [
"ash:module:Ash.Type"
]
],
default: Application.compile_env(:ash, :default_belongs_to_type, :uuid),
doc: "The type of the generated created attribute."
doc: "The type of the generated created attribute. See `Ash.Type` for more."
]
],
@global_opts,

View file

@ -59,7 +59,6 @@ defmodule Ash.Resource.Relationships.HasOne do
[
allow_nil?: [
type: :boolean,
links: [],
default: true,
doc: """
Marks the relationship as required. Has no effect on validations, but can inform extensions that there will always be a related entity.

View file

@ -59,34 +59,22 @@ defmodule Ash.Resource.Relationships.ManyToMany do
source_attribute_on_join_resource: [
type: :atom,
required: true,
links: [
dsls: [
"ash:dsl:resource/attributes/attribute"
]
],
doc:
"The attribute on the join resource that should line up with `source_attribute` on this resource."
],
destination_attribute_on_join_resource: [
type: :atom,
required: true,
links: [
dsls: [
"ash:dsl:resource/attributes/attribute"
]
],
doc:
"The attribute on the join resource that should line up with `destination_attribute` on the related resource."
],
through: [
type: Ash.OptionsHelpers.ash_resource(),
required: true,
links: [],
doc: "The resource to use as the join resource."
],
join_relationship: [
type: :atom,
links: [],
doc:
"The has_many relationship to the join resource. Defaults to <relationship_name>_join_assoc"
]

View file

@ -4,26 +4,18 @@ defmodule Ash.Resource.Relationships.SharedOptions do
@shared_options [
name: [
type: :atom,
doc: "The name of the relationship",
links: []
doc: "The name of the relationship"
],
destination: [
type: Ash.OptionsHelpers.ash_resource(),
doc: "The destination resource",
links: []
doc: "The destination resource"
],
description: [
type: :string,
doc: "An optional description for the relationship",
links: []
doc: "An optional description for the relationship"
],
destination_attribute: [
type: :atom,
links: [
dsls: [
"ash:dsl:resource/attributes/attribute"
]
],
doc:
"The attribute on the related resource that should match the `source_attribute` configured on this resource."
],
@ -31,23 +23,16 @@ defmodule Ash.Resource.Relationships.SharedOptions do
type: :boolean,
default: true,
doc:
"Whether or not to validate that the destination field exists on the destination resource",
links: []
"Whether or not to validate that the destination field exists on the destination resource"
],
source_attribute: [
type: :atom,
links: [
dsls: [
"ash:dsl:resource/attributes/attribute"
]
],
doc:
"The field on this resource that should match the `destination_attribute` on the related resource."
],
relationship_context: [
type: :any,
as: :context,
links: [],
doc: """
Context to be set on any queries or changesets generated for managing or querying this relationship.
"""
@ -55,49 +40,42 @@ defmodule Ash.Resource.Relationships.SharedOptions do
private?: [
type: :boolean,
default: false,
links: [
guides: ["ash:guide:Security"]
],
doc:
"Whether or not the relationship will appear in any interfaces created off of this resource, e.g AshJsonApi and AshGraphql"
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.
"""
],
not_found_message: [
type: :string,
doc: """
A message to show if there is a conflict with this relationship in the database on update or create, or when managing relationships.
""",
links: [
guides: ["ash:guide:Managing Relationships"]
]
"""
],
writable?: [
type: :boolean,
default: true,
doc: """
Whether or not the relationship may be managed.
""",
links: []
"""
],
read_action: [
type: :atom,
doc: """
The read action on the destination resource to use when loading data and filtering.
""",
links: []
"""
],
api: [
type: :atom,
doc: """
The API module to use when working with the related entity.
""",
links: []
"""
],
filter: [
type: :any,
doc: """
A filter to be applied when reading the relationship.
""",
links: []
"""
],
filterable?: [
type: :boolean,
@ -108,20 +86,17 @@ defmodule Ash.Resource.Relationships.SharedOptions do
type: :any,
doc: """
A sort statement to be applied when loading the relationship.
""",
links: []
"""
],
could_be_related_at_creation?: [
type: :boolean,
default: false,
links: [],
doc: """
Whether or not related values may exist for this relationship at creation.
"""
],
violation_message: [
type: :string,
links: [],
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)
@ -137,7 +112,6 @@ defmodule Ash.Resource.Relationships.SharedOptions do
{:no_attributes?,
[
type: :boolean,
links: [],
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.
@ -165,7 +139,6 @@ defmodule Ash.Resource.Relationships.SharedOptions do
type:
{:spark_function_behaviour, Ash.Resource.ManualRelationship,
{Ash.Resource.ManualRelationship.Function, 2}},
links: [],
doc: """
A module that implements `Ash.Resource.ManualRelationship`. Also accepts a 2 argument function that takes the source records and the context.
"""}

View file

@ -51,18 +51,12 @@ defmodule Ash.Resource.Validation do
type: @validation_type,
required: true,
doc:
"The module (or module and opts) that implements the `Ash.Resource.Validation` behaviour. Also accepts a one argument function that takes the changeset.",
links: []
"The module (or module and opts) that implements the `Ash.Resource.Validation` behaviour. Also accepts a one argument function that takes the changeset."
],
where: [
type: {:or, [@validation_type, {:list, @validation_type}]},
required: false,
default: [],
links: [
modules: [
"ash:module:Ash.Resource.Validation.Builtins"
]
],
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.
@ -72,7 +66,6 @@ defmodule Ash.Resource.Validation do
on: [
type: {:custom, __MODULE__, :on, []},
default: [:create, :update],
links: [],
doc: """
The action types the validation should run on.
@ -82,24 +75,20 @@ defmodule Ash.Resource.Validation do
only_when_valid?: [
type: :boolean,
default: false,
links: [],
doc:
"If the validation should only run on valid changes. Useful for expensive validations or validations that depend on valid data."
],
message: [
type: :string,
doc: "If provided, overrides any message set by the validation error",
links: []
doc: "If provided, overrides any message set by the validation error"
],
description: [
type: :string,
doc: "An optional description for the validation",
links: []
doc: "An optional description for the validation"
],
before_action?: [
type: :boolean,
default: false,
links: [],
doc: "If set to `true`, the validation will be run in a before_action hook"
]
]

View file

@ -1,207 +0,0 @@
defmodule Ash.DocIndex do
@moduledoc false
use Spark.DocIndex,
guides_from: [
"documentation/**/*.md"
],
guide_order: %{
"Tutorials" => [
"get-started.md",
"philosophy.md",
"why-ash.md"
]
}
@impl true
@spec for_library() :: String.t()
def for_library, do: "ash"
@impl true
@spec extensions() :: list(Spark.DocIndex.extension())
def extensions do
[
%{
module: Ash.Resource.Dsl,
name: "Resource",
target: "Ash.Resource",
type: "Resource",
default_for_target?: true
},
%{
module: Ash.Api.Dsl,
name: "Api",
target: "Ash.Api",
type: "Api",
default_for_target?: true
},
%{
module: Ash.DataLayer.Ets,
name: "Ets",
target: "Ash.Resource",
type: "DataLayer"
},
%{
module: Ash.DataLayer.Mnesia,
name: "Mnesia",
target: "Ash.Resource",
type: "DataLayer"
},
%{
module: Ash.Policy.Authorizer,
name: "Policy Authorizer",
target: "Ash.Resource",
type: "Authorizer"
},
%{
module: Ash.Flow.Dsl,
name: "Flow",
target: "Ash.Flow",
type: "Flow",
default_for_target?: true
},
%{
module: Ash.Notifier.PubSub,
name: "PubSub",
target: "Ash.Resource",
type: "Notifier"
},
%{
module: Ash.Registry.Dsl,
name: "Registry",
target: "Ash.Registry",
type: "Registry",
default_for_target?: true
},
%{
module: Ash.Registry.ResourceValidations,
name: "Resource Validations",
type: "Extension",
target: "Ash.Registry"
}
]
end
@impl true
@spec mix_tasks :: [{String.t(), list(module)}]
def mix_tasks do
[
{
"Charts",
[
Mix.Tasks.Ash.GenerateFlowCharts
]
}
]
end
@impl true
def code_modules do
[
Resources: [
Ash.Api,
Ash.Filter.TemplateHelpers,
Ash.Calculation,
Ash.Resource.Calculation.Builtins,
Ash.CodeInterface,
Ash.DataLayer,
Ash.Notifier,
Ash.Notifier.Notification,
Ash.Resource.ManualRead,
Ash.Resource.ManualRelationship
],
Expressions: [
Ash.Expr
],
Queries: [
Ash.Query,
Ash.Resource.Preparation,
Ash.Resource.Preparation.Builtins,
Ash.Query.Calculation,
Ash.Query.Aggregate
],
Changesets: [
Ash.Changeset,
Ash.Resource.Change,
Ash.Resource.Change.Builtins,
Ash.Resource.Validation,
Ash.Resource.Validation.Builtins
],
Authorization: [
Ash.Authorizer,
Ash.Policy.Check,
Ash.Policy.Check.Builtins,
Ash.Policy.FilterCheck,
Ash.Policy.FilterCheckWithContext,
Ash.Policy.SimpleCheck
],
Introspection: [
Ash.Api.Info,
Ash.Registry.Info,
Ash.Resource.Info,
Ash.Flow.Info,
Ash.Policy.Info,
Ash.DataLayer.Ets.Info,
Ash.DataLayer.Mnesia.Info,
Ash.Notifier.PubSub.Info
],
Utilities: [
Ash,
Ash.Page.Keyset,
Ash.Page.Offset,
Ash.Filter,
Ash.Filter.Runtime,
Ash.Sort,
Ash.CiString,
Ash.UUID,
Ash.NotLoaded,
Ash.Changeset.ManagedRelationshipHelpers,
Ash.DataLayer.Simple,
Ash.Filter.Simple,
Ash.Filter.Simple.Not,
Ash.OptionsHelpers,
Ash.Resource.Builder,
Ash.Tracer
],
Testing: [
Ash.Generator,
Ash.Seed
],
Flow: [
Ash.Flow,
Ash.Flow.Step,
Ash.Flow.Executor,
Ash.Flow.Chart.Mermaid,
Ash.Flow.StepHelpers
],
Errors: [
Ash.Error,
Ash.Error.Exception,
Ash.Error.Stacktrace
],
Types: [
Ash.Type,
Ash.Type.Enum,
Ash.Type.Atom,
Ash.Type.Binary,
Ash.Type.Boolean,
Ash.Type.CiString,
Ash.Type.Date,
Ash.Type.Decimal,
Ash.Type.DurationName,
Ash.Type.Float,
Ash.Type.Function,
Ash.Type.Integer,
Ash.Type.Map,
Ash.Type.NaiveDatetime,
Ash.Type.String,
Ash.Type.Term,
Ash.Type.Time,
Ash.Type.UUID,
Ash.Type.UrlEncodedBinary,
Ash.Type.UtcDatetime,
Ash.Type.UtcDatetimeUsec
]
]
end
end

View file

@ -4,247 +4,57 @@ defmodule Mix.Tasks.Ash.ReplaceDocLinks do
"""
use Mix.Task
@shortdoc "Replaces any Spark doc links with text appropriate for hex docs."
@shortdoc "Replaces any ash specific doc links with text appropriate for hex docs."
def run(_argv) do
mix_project = Mix.Project.get!()
module_prefix = mix_project |> Module.split() |> Enum.at(0)
"doc/**/*.html"
|> Path.wildcard()
|> Enum.each(fn file ->
current_project = to_string(Mix.Project.config()[:app])
new_contents =
file
|> File.read!()
|> Spark.DocIndex.render_replacements(fn
:mix_dep, %{library: ^current_project}, _context ->
case Version.parse(Mix.Project.config()[:version]) do
{:ok, %Version{pre: pre, build: build}} when pre != [] or not is_nil(build) ->
~s({:#{current_project}, "~> #{Mix.Project.config()[:version]}"})
|> String.replace(~r/\>d\:.*\</, fn ">d:" <> contents ->
contents =
contents
|> String.trim_trailing("<")
|> String.replace("|", ".")
{:ok, %Version{major: major, minor: minor}} ->
~s({:#{current_project}, "~> #{major}.#{minor}"})
module_name =
contents
|> String.split(".")
|> Enum.take_while(&capitalized?/1)
|> Enum.join(".")
_ ->
~s({:#{current_project}, "~> x.y.z"})
end
:mix_dep, %{library: library}, _context ->
~s({:#{library}, "~> x.y.z"})
:link, %{type: "option", item: item, name_override: name, library: library}, _ ->
# TODO: validate options
path =
item
|> String.trim_leading("/")
|> String.split(~r/[\/\.]/)
|> Enum.drop(1)
name = name || join_path(path)
dsl_path = path |> :lists.droplast() |> Enum.map_join("/", &sanitize_name/1)
anchor = path |> List.last() |> sanitize_name()
~s(<a href="https://ash-hq.org/docs/dsl/#{library}/latest/#{dsl_path}##{anchor}">#{name}</a>)
:link, %{type: "dsl", item: item, name_override: name, library: library}, _ ->
# TODO: validate dsls
path =
item
|> String.trim_leading("/")
|> String.split(~r/[\/\.]/)
|> Enum.drop(1)
dsl_path = Enum.map_join(path, "/", &sanitize_name/1)
name = name || join_path(path)
~s(<a href="https://ash-hq.org/docs/dsl/#{library}/latest/#{dsl_path}">#{name}</a>)
:link,
%{
type: "extension",
item: item,
name_override: name,
library: ^current_project
},
_ ->
module = Module.concat([item])
case Code.ensure_compiled(module) do
{:module, module} ->
~s(<a href="#{inspect(module)}.html" class="no-underline">#{name || inspect(module)}</a>)
_ ->
~s(<a href="https://ash-hq.org/docs/dsl/#{current_project}/latest/#{sanitize_name(item)}">#{name || item}</a>)
end
:link, %{type: "extension", item: item, name_override: name, library: library}, _ ->
if String.contains?(item, " ") || !String.contains?(item, ".") do
~s(<a href="https://ash-hq.org/docs/dsl/#{current_project}/latest/#{sanitize_name(item)}">#{name || item}</a>)
else
~s(<a href="https://hexdocs.pm/#{library}/#{item}.html" class="no-underline">#{name || item}</a>)
end
:link,
%{type: "guide", item: item, name_override: name, library: ^current_project},
_ ->
Enum.find_value(doc_indexes(), fn doc_index ->
Enum.find(doc_index.guides(), fn guide ->
sanitize_name(item) == sanitize_name(guide.name) ||
sanitize_name_under(item) == sanitize_name_under(guide.name)
end)
end)
|> case do
nil ->
raise "No such guide matching name #{item}"
_ ->
if String.starts_with?(module_name, module_prefix) do
case Code.ensure_compiled(Module.concat([module_name])) do
{:module, _} ->
:ok
end
~s(<a href="#{sanitize_name_under(item)}.html">#{name || item}</a>)
:link, %{type: "guide", item: item, name_override: name, library: library}, _ ->
~s(<a href="https://hexdocs.pm/#{library}/#{sanitize_name_under(item)}.html">#{name || item}</a>)
:link,
%{
type: "module",
item: item,
name_override: name,
library: ^current_project,
text: text
},
_ ->
module = Module.concat([item])
case Code.ensure_compiled(module) do
{:module, module} ->
in_docs_modules? =
Mix.Project.config()[:docs][:groups_for_modules]
|> Kernel.||([])
|> Enum.any?(fn
{_group, %Regex{} = regex} ->
Regex.match?(regex, inspect(module))
{_group, mods} when is_list(mods) ->
module in mods
end)
unless in_docs_modules? do
raise """
Module #{inspect(module)} referenced in #{text} does not exist in the mix project's `groups_for_modules`.
Also be sure to add it to your doc index as well.
"""
end
in_a_spark_index? =
Enum.any?(doc_indexes(), fn doc_index ->
doc_index.code_modules
|> Enum.any?(fn {_key, mods} ->
module in mods
end)
end)
unless in_a_spark_index? do
raise """
Module #{inspect(module)} referenced in #{text} does not exist in a `Spark.DocIndex`.
"""
end
~s(<a href="#{inspect(module)}.html" class="no-underline"><code class="inline">#{name || item}</code></a>)
{:error, error} ->
raise """
Module #{inspect(module)} referenced in #{text} does not exist or could not be compiled: #{inspect(error)}.
"""
raise "Expected #{module_name} to be compiled, but it was not: #{inspect(error)}"
end
end
:link, %{type: "module", item: item, name_override: name, library: library}, _ ->
~s(<a href="https://hexdocs.pm/#{library}/#{item}.html" class="no-underline">#{name || item}</a>)
:link, %{type: "function", item: item, name_override: name, library: library}, _ ->
parts = String.split(item, ".")
function = List.last(parts)
module = parts |> :lists.droplast() |> Enum.join(".")
~s(<a href="https://hexdocs.pm/#{library}/#{module}.html#{function}" class="no-underline">#{name || item}</a>)
:link, %{type: "library", name_override: name, library: library}, _ ->
~s(<a href="https://hexdocs.pm/#{library}">#{name || library}</a>)
_, %{text: text}, _ ->
raise "No link handler for: `#{text}`"
">#{contents}<"
end)
|> String.replace(~r</documentation/.*/.*.md>, fn "/documentation/" <> type_and_name ->
[_, filename] = String.split(type_and_name, "/")
File.write!(file, new_contents)
end)
"doc/dist/*.js"
|> Path.wildcard()
|> Enum.filter(&String.contains?(&1, "sidebar"))
|> Enum.each(fn file ->
current_project = to_string(Mix.Project.config()[:app])
new_contents =
file
|> File.read!()
|> Spark.DocIndex.render_replacements(fn
:mix_dep, %{library: ^current_project}, _context ->
case Version.parse(Mix.Project.config()[:version]) do
{:ok, %Version{pre: pre, build: build}} when pre != [] or not is_nil(build) ->
~s({:#{current_project}, "~> #{Mix.Project.config()[:version]}"})
{:ok, %Version{major: major, minor: minor}} ->
~s({:#{current_project}, "~> #{major}.#{minor}"})
_ ->
~s({:#{current_project}, "~> x.y.z"})
end
:mix_dep, %{library: library}, _context ->
library
:link, %{name_override: name, item: item}, _context ->
name || item
_, %{text: text}, _ ->
text
filename |> String.trim_trailing(".md") |> Kernel.<>(".html")
end)
File.write!(file, new_contents)
end)
end
def doc_indexes do
for module <- modules(),
{:module, module} = Code.ensure_compiled(module),
Spark.implements_behaviour?(module, Spark.DocIndex) do
module
end
end
defp capitalized?(string) do
first =
string
|> String.graphemes()
|> Enum.at(0)
defp modules do
Mix.Project.config()[:app]
|> :application.get_key(:modules)
|> case do
{:ok, mods} when is_list(mods) ->
mods
_ ->
[]
end
end
defp join_path(path) do
Enum.join(path, " > ")
end
defp sanitize_name(name) do
String.downcase(String.replace(name, ~r/[^A-Za-z0-9_]/, "-"))
end
defp sanitize_name_under(name) do
String.downcase(String.replace(name, ~r/[^A-Za-z0-9-]/, "_"))
String.downcase(first) != first
end
end

121
mix.exs
View file

@ -48,23 +48,23 @@ defmodule Ash.MixProject do
{String.to_atom(path),
[
title: title
title: title,
default: title == "Get Started"
]}
end)
end
defp groups_for_extras do
"documentation/*"
|> Path.wildcard()
|> Enum.map(fn folder ->
name =
folder
|> Path.basename()
|> String.split(~r/[-_]/)
|> Enum.map_join(" ", &String.capitalize/1)
{name, folder |> Path.join("**") |> Path.wildcard()}
end)
[
Tutorials: [
"documentation/tutorials/get-started.md",
"documentation/tutorials/philosophy.md",
"documentation/tutorials/why-ash.md",
~r'documentation/tutorials'
],
"How To": ~r'documentation/how_to',
Topics: ~r'documentation/topics'
]
end
defp docs do
@ -76,6 +76,73 @@ defmodule Ash.MixProject do
extra_section: "GUIDES",
extras: extras(),
groups_for_extras: groups_for_extras(),
spark: [
extensions: [
%{
module: Ash.Resource.Dsl,
name: "Resource",
target: "Ash.Resource",
type: "Resource",
default_for_target?: true
},
%{
module: Ash.Api.Dsl,
name: "Api",
target: "Ash.Api",
type: "Api",
default_for_target?: true
},
%{
module: Ash.DataLayer.Ets,
name: "Ets",
target: "Ash.Resource",
type: "DataLayer"
},
%{
module: Ash.DataLayer.Mnesia,
name: "Mnesia",
target: "Ash.Resource",
type: "DataLayer"
},
%{
module: Ash.Policy.Authorizer,
name: "Policy Authorizer",
target: "Ash.Resource",
type: "Authorizer"
},
%{
module: Ash.Flow.Dsl,
name: "Flow",
target: "Ash.Flow",
type: "Flow",
default_for_target?: true
},
%{
module: Ash.Notifier.PubSub,
name: "PubSub",
target: "Ash.Resource",
type: "Notifier"
},
%{
module: Ash.Registry.Dsl,
name: "Registry",
target: "Ash.Registry",
type: "Registry",
default_for_target?: true
},
%{
module: Ash.Registry.ResourceValidations,
name: "Resource Validations",
type: "Extension",
target: "Ash.Registry"
}
],
mix_tasks: [
Charts: [
Mix.Tasks.Ash.GenerateFlowCharts
]
]
],
groups_for_modules: [
"Extensions & DSLs": [
Ash.Api.Dsl,
@ -168,33 +235,13 @@ defmodule Ash.MixProject do
Ash.Flow.Chart.Mermaid,
Ash.Flow.StepHelpers
],
Types: [
"Ash.Type",
~r/Ash.Type\./
],
Errors: [
Ash.Error,
Ash.Error.Exception,
Ash.Error.Stacktrace
],
Types: [
Ash.Type,
Ash.Type.Enum,
Ash.Type.Atom,
Ash.Type.Binary,
Ash.Type.Boolean,
Ash.Type.CiString,
Ash.Type.Date,
Ash.Type.Decimal,
Ash.Type.DurationName,
Ash.Type.Float,
Ash.Type.Function,
Ash.Type.Integer,
Ash.Type.Map,
Ash.Type.NaiveDatetime,
Ash.Type.String,
Ash.Type.Term,
Ash.Type.Time,
Ash.Type.UUID,
Ash.Type.UrlEncodedBinary,
Ash.Type.UtcDatetime,
Ash.Type.UtcDatetimeUsec
~r/Ash.Error\./
],
Transformers: [~r/\.Transformers\./, Ash.Registry.ResourceValidations],
Internals: ~r/.*/