mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
docs: more DSL docs, create shell guides
This commit is contained in:
parent
6a7f36a122
commit
73124c43fd
17 changed files with 349 additions and 132 deletions
|
@ -39,8 +39,13 @@ defmodule Ash.Resource.Actions.Create do
|
|||
type: {:list, :atom},
|
||||
doc: """
|
||||
A list of attributes that would normally be required, but should not be for this action. They will still be validated just before
|
||||
the record is created, but this allows for setting required attributes in your changes under the hood.
|
||||
"""
|
||||
the record is created.
|
||||
""",
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Actions"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|> Ash.OptionsHelpers.merge_schemas(
|
||||
|
|
|
@ -5,16 +5,21 @@ defmodule Ash.Resource.Actions.SharedOptions do
|
|||
name: [
|
||||
type: :atom,
|
||||
required: true,
|
||||
doc: "The name of the action"
|
||||
doc: "The name of the action",
|
||||
links: []
|
||||
],
|
||||
primary?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
doc: "Whether or not this action should be used when no action is specified by the caller."
|
||||
doc: "Whether or not this action should be used when no action is specified by the caller.",
|
||||
links: []
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the action"
|
||||
doc: "An optional description for the action",
|
||||
links: [
|
||||
"ash:guide:Documentation"
|
||||
]
|
||||
],
|
||||
transaction?: [
|
||||
type: :boolean,
|
||||
|
@ -22,24 +27,27 @@ 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: [
|
||||
"ash:guide:Transactions"
|
||||
]
|
||||
],
|
||||
touches_resources: [
|
||||
type: {:list, :atom},
|
||||
doc: """
|
||||
A list of resources that the action may touch.
|
||||
|
||||
If your action has custom code (i.e custom changes) that touch resources that use a different data layer,
|
||||
this can be used to inform the transaction logic that these resource's data layer's should be involved in the
|
||||
transaction. In most standard set ups, this should not be necessary.
|
||||
"""
|
||||
A list of resources that the action may touch, used when building transactions.
|
||||
""",
|
||||
links: [
|
||||
"ash:guide:Transactions"
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@create_update_opts [
|
||||
accept: [
|
||||
type: {:or, [in: [:all], list: :atom]},
|
||||
doc: "The list of attributes to accept. Defaults to all attributes on the resource"
|
||||
doc: "The list of attributes to accept. Defaults to all attributes on the resource",
|
||||
links: []
|
||||
],
|
||||
reject: [
|
||||
type: {:or, [in: [:all], list: :atom]},
|
||||
|
@ -48,59 +56,32 @@ 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.
|
||||
A list of attributes that would normally `allow_nil?`, to require for this action.
|
||||
|
||||
No need to include attributes that are `allow_nil?: false`.
|
||||
No need to include attributes that already do not allow nil?
|
||||
"""
|
||||
],
|
||||
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.
|
||||
|
||||
All validation still takes place, but the `result` in any `after_action` callbacks
|
||||
attached to that action will simply be the record that was read from the database initially.
|
||||
For creates, the `result` will be `nil`, and you will be expected to handle the changeset in
|
||||
an after_action callback and return an instance of the record. This is a good way to prevent
|
||||
Ash from issuing an unnecessary update to the record, e.g updating the `updated_at` of the record
|
||||
when an action actually only involves modifying relating records.
|
||||
|
||||
You could then handle the changeset automatically.
|
||||
|
||||
For example:
|
||||
|
||||
# in the action
|
||||
|
||||
```elixir
|
||||
action :special_create do
|
||||
manual? true
|
||||
change MyApp.DoCreate
|
||||
end
|
||||
|
||||
# The change
|
||||
defmodule MyApp.DoCreate do
|
||||
use Ash.Resource.Change
|
||||
|
||||
def change(changeset, _, _) do
|
||||
Ash.Changeset.after_action(changeset, fn changeset, _result ->
|
||||
# result will be `nil`, because this is a manual action
|
||||
|
||||
result = do_something_that_creates_the_record(changeset)
|
||||
|
||||
{:ok, result}
|
||||
end)
|
||||
end
|
||||
end
|
||||
```
|
||||
Instructs Ash to *skip* the actual update/create/destroy step at the data layer. See the manual action guides for more.
|
||||
"""
|
||||
]
|
||||
]
|
||||
|
|
|
@ -55,6 +55,13 @@ defmodule Ash.Resource.Attribute do
|
|||
modules: ["ash:module:Ash.Type"]
|
||||
]
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the attribute.",
|
||||
links: [
|
||||
modules: ["ash:guide:Documentation"]
|
||||
]
|
||||
],
|
||||
sensitive?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
|
@ -97,7 +104,9 @@ defmodule Ash.Resource.Attribute do
|
|||
type: :boolean,
|
||||
default: false,
|
||||
doc: "Whether or not the value may be generated by the data layer.",
|
||||
links: []
|
||||
links: [
|
||||
guides: ["ash:guide:Actions"]
|
||||
]
|
||||
],
|
||||
writable?: [
|
||||
type: :boolean,
|
||||
|
@ -114,22 +123,25 @@ defmodule Ash.Resource.Attribute do
|
|||
guides: ["ash:guide:Security"]
|
||||
]
|
||||
],
|
||||
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"]
|
||||
]
|
||||
],
|
||||
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."
|
||||
doc: "A value to be set on all updates, unless a value is being provided already.",
|
||||
links: [
|
||||
guides: ["ash:guide:Actions"]
|
||||
]
|
||||
],
|
||||
filterable?: [
|
||||
type: {:or, [:boolean, {:in, [:simple_equality]}]},
|
||||
default: true,
|
||||
doc: "Whether or not the attribute can be referenced in filters."
|
||||
],
|
||||
default: [
|
||||
type: {:or, [{:mfa_or_fun, 0}, :literal]},
|
||||
doc: "A value to be set on all creates, unless a value is being provided already."
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the attribute."
|
||||
doc: "Whether or not the attribute can be referenced in filters.",
|
||||
links: []
|
||||
],
|
||||
match_other_defaults?: [
|
||||
type: :boolean,
|
||||
|
@ -139,7 +151,10 @@ 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"]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
|
|
@ -19,64 +19,88 @@ 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"
|
||||
]
|
||||
],
|
||||
required: true
|
||||
],
|
||||
constraints: [
|
||||
type: :keyword_list,
|
||||
default: [],
|
||||
links: [
|
||||
modules: [
|
||||
"ash:module:Ash.Type"
|
||||
]
|
||||
],
|
||||
doc: "Constraints to provide to the type."
|
||||
],
|
||||
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.
|
||||
|
||||
This is useful for calculations that are very expensive, especially when combined with complex filters/join
|
||||
scenarios. By adding this, we will rerun a trimmed down version of the main query, using the primary keys for
|
||||
fast access. This will be done asynchronously for each calculation that has `allow_async?: true`.
|
||||
|
||||
Keep in mind that if the calculation is used in a filter or sort, it cannot be done asynchrnously,
|
||||
and *must* be done in the main query.
|
||||
"""
|
||||
],
|
||||
calculation: [
|
||||
type: {:custom, __MODULE__, :calculation, []},
|
||||
required: true,
|
||||
links: [],
|
||||
doc: "The module or `{module, opts}` to use for the calculation"
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Documentation"
|
||||
]
|
||||
],
|
||||
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"
|
||||
],
|
||||
select: [
|
||||
type: {:list, :atom},
|
||||
default: [],
|
||||
doc: "A list of fields to ensure selected in the case that the calculation is run."
|
||||
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."
|
||||
]
|
||||
]
|
||||
|
@ -98,26 +122,39 @@ defmodule Ash.Resource.Calculation do
|
|||
name: [
|
||||
type: :atom,
|
||||
required: true,
|
||||
doc: "The name to use for the argument"
|
||||
doc: "The name to use for the argument",
|
||||
links: []
|
||||
],
|
||||
type: [
|
||||
type: :ash_type,
|
||||
required: true,
|
||||
links: [
|
||||
modules: [
|
||||
"ash:module:Ash.Type"
|
||||
]
|
||||
],
|
||||
doc: "The type of the argument"
|
||||
],
|
||||
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,
|
||||
doc: "Whether or not the argument value may be nil"
|
||||
links: [],
|
||||
doc: "Whether or not the argument value may be nil (or may be not provided)"
|
||||
],
|
||||
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."
|
||||
]
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Ash.Resource.Change do
|
|||
when this change was configured on a resource, and the context, which currently only has
|
||||
the actor.
|
||||
"""
|
||||
defstruct [:change, :on, :only_when_valid?, where: []]
|
||||
defstruct [:change, :on, :only_when_valid?, :description, where: []]
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
|
@ -21,22 +21,30 @@ defmodule Ash.Resource.Change do
|
|||
type: {:custom, __MODULE__, :on, []},
|
||||
default: [:create, :update],
|
||||
doc: """
|
||||
The action types the validation should run on.
|
||||
|
||||
Many validations don't make sense in the context of deletion, so by default it is left out of the list.
|
||||
"""
|
||||
The action types the validation should run on. 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.
|
||||
|
||||
For 2.0 this may become the default.
|
||||
"""
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the change",
|
||||
links: []
|
||||
],
|
||||
change: [
|
||||
type: {:ash_behaviour, Ash.Resource.Change, Ash.Resource.Change.Builtins},
|
||||
links: [
|
||||
modules: [
|
||||
"ash:module:Ash.Resource.Change.Builtins"
|
||||
]
|
||||
],
|
||||
doc: """
|
||||
The module and options for a change.
|
||||
""",
|
||||
|
@ -46,6 +54,11 @@ defmodule Ash.Resource.Change do
|
|||
type:
|
||||
{:list, {:ash_behaviour, Ash.Resource.Validation, Ash.Resource.Validation.Builtins}},
|
||||
required: false,
|
||||
links: [
|
||||
modules: [
|
||||
"ash:module:Ash.Resource.Validation.Builtins"
|
||||
]
|
||||
],
|
||||
default: [],
|
||||
doc: """
|
||||
Validations that should pass in order for this validation to apply.
|
||||
|
|
|
@ -160,6 +160,11 @@ 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(),
|
||||
|
@ -180,6 +185,11 @@ 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(),
|
||||
|
@ -193,6 +203,11 @@ defmodule Ash.Resource.Dsl do
|
|||
|
||||
A join table is typically a table who's primary key consists of one foreign key to each resource.
|
||||
""",
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Relationships"
|
||||
]
|
||||
],
|
||||
examples: [
|
||||
"""
|
||||
# In a resource called `Word`
|
||||
|
@ -223,6 +238,11 @@ defmodule Ash.Resource.Dsl do
|
|||
|
||||
This creates a field on the resource with the corresponding name and type, unless `define_field?: false` is provided.
|
||||
""",
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Relationships"
|
||||
]
|
||||
],
|
||||
examples: [
|
||||
"""
|
||||
# In a resource called `Word`
|
||||
|
@ -246,6 +266,11 @@ 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`).
|
||||
""",
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Relationships"
|
||||
]
|
||||
],
|
||||
examples: [
|
||||
"""
|
||||
relationships do
|
||||
|
@ -348,6 +373,14 @@ defmodule Ash.Resource.Dsl do
|
|||
|
||||
@change %Ash.Dsl.Entity{
|
||||
name: :change,
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Actions"
|
||||
],
|
||||
modules: [
|
||||
"ash:module:Ash.Resource.Change"
|
||||
]
|
||||
],
|
||||
describe: """
|
||||
A change to be applied to the changeset after it is generated. They are run in order, from top to bottom.
|
||||
|
||||
|
@ -376,6 +409,14 @@ defmodule Ash.Resource.Dsl do
|
|||
"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],
|
||||
|
@ -548,6 +589,11 @@ defmodule Ash.Resource.Dsl do
|
|||
Ash.Resource.Validation.Builtins,
|
||||
Ash.Filter.TemplateHelpers
|
||||
],
|
||||
links: [
|
||||
guides: [
|
||||
"ash:guide:Actions"
|
||||
]
|
||||
],
|
||||
schema: [
|
||||
defaults: [
|
||||
type: {:list, {:in, [:create, :read, :update, :destroy]}},
|
||||
|
@ -558,10 +604,14 @@ 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}
|
||||
type: {:list, :atom},
|
||||
doc:
|
||||
"A default value for the `accept` option for each action. Defaults to all public attributes.",
|
||||
links: []
|
||||
]
|
||||
],
|
||||
examples: [
|
||||
|
@ -745,6 +795,9 @@ defmodule Ash.Resource.Dsl do
|
|||
describe: """
|
||||
Declare validations prior to performing actions against the resource
|
||||
""",
|
||||
links: [
|
||||
guides: ["ash:guides:Actions"]
|
||||
],
|
||||
imports: [Ash.Resource.Validation.Builtins],
|
||||
examples: [
|
||||
"""
|
||||
|
@ -923,6 +976,11 @@ 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()
|
||||
|
|
|
@ -61,34 +61,51 @@ defmodule Ash.Resource.Relationships.BelongsTo do
|
|||
[
|
||||
primary_key?: [
|
||||
type: :boolean,
|
||||
links: [
|
||||
guides: ["ash:guide:Attributes"]
|
||||
],
|
||||
default: false,
|
||||
doc: "Whether this field is, or is part of, the primary key of a resource."
|
||||
doc:
|
||||
"Whether the generated attribute is, or is part of, the primary key of a resource."
|
||||
],
|
||||
required?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
links: [
|
||||
guides: ["ash:guide:Attributes"]
|
||||
],
|
||||
doc:
|
||||
"Whether this relationship must always be present, e.g: must be included on creation, and never removed (it can still be changed)"
|
||||
"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: [
|
||||
guides: ["ash:guide:Attributes"]
|
||||
],
|
||||
doc: """
|
||||
Whether this relationship's generated attribute will be marked as public & writable.
|
||||
|
||||
Has no effect when combined with `define_field?: false`.
|
||||
Whether the generated attribute will be marked as public & writable.
|
||||
"""
|
||||
],
|
||||
define_field?: [
|
||||
type: :boolean,
|
||||
default: true,
|
||||
links: [
|
||||
guides: ["ash:guide:Attributes"]
|
||||
],
|
||||
doc:
|
||||
"If set to `false` a field is not created on the resource for this relationship, and one must be manually added in `attributes`."
|
||||
"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."
|
||||
],
|
||||
field_type: [
|
||||
type: :any,
|
||||
links: [
|
||||
modules: [
|
||||
"ash:module:Ash.Type"
|
||||
],
|
||||
guides: ["ash:guide:Attributes"]
|
||||
],
|
||||
default: :uuid,
|
||||
doc: "The field type of the automatically created field."
|
||||
doc: "The type of the generated created attribute."
|
||||
]
|
||||
],
|
||||
@global_opts,
|
||||
|
|
|
@ -57,10 +57,9 @@ defmodule Ash.Resource.Relationships.HasOne do
|
|||
[
|
||||
required?: [
|
||||
type: :boolean,
|
||||
links: [],
|
||||
doc: """
|
||||
Marks the relationship as required. This is *not* currently validated anywhere, since the
|
||||
relationship is managed by the destination, but ash_graphql uses it for type information,
|
||||
and it can be used for expressiveness.
|
||||
Marks the relationship as required. Has no effect on validations, but can inform extensions that there will always be a related entity.
|
||||
"""
|
||||
]
|
||||
],
|
||||
|
|
|
@ -57,22 +57,34 @@ defmodule Ash.Resource.Relationships.ManyToMany do
|
|||
source_field_on_join_table: [
|
||||
type: :atom,
|
||||
required: true,
|
||||
links: [
|
||||
dsls: [
|
||||
"ash:dsl:attributes/attribute"
|
||||
]
|
||||
],
|
||||
doc:
|
||||
"The field on the join table that should line up with `source_field` on this resource."
|
||||
"The attribute on the join resource that should line up with `source_field` on this resource."
|
||||
],
|
||||
destination_field_on_join_table: [
|
||||
type: :atom,
|
||||
required: true,
|
||||
links: [
|
||||
dsls: [
|
||||
"ash:dsl:attributes/attribute"
|
||||
]
|
||||
],
|
||||
doc:
|
||||
"The field on the join table that should line up with `destination_field` on the related resource."
|
||||
"The attribute on the join resource that should line up with `destination_field` on the related resource."
|
||||
],
|
||||
through: [
|
||||
type: :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 table. Defaults to <relationship_name>_join_assoc"
|
||||
]
|
||||
|
|
|
@ -4,102 +4,125 @@ defmodule Ash.Resource.Relationships.SharedOptions do
|
|||
@shared_options [
|
||||
name: [
|
||||
type: :atom,
|
||||
doc: "The name of the relationship"
|
||||
doc: "The name of the relationship",
|
||||
links: []
|
||||
],
|
||||
destination: [
|
||||
type: :ash_resource,
|
||||
doc: "The destination resource"
|
||||
doc: "The destination resource",
|
||||
links: []
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the relationship",
|
||||
links: [
|
||||
modules: ["ash:guide:Documentation"]
|
||||
]
|
||||
],
|
||||
destination_field: [
|
||||
type: :atom,
|
||||
links: [
|
||||
dsls: [
|
||||
"ash:dsl:attributes/attribute"
|
||||
]
|
||||
],
|
||||
doc:
|
||||
"The field on the related resource that should match the `source_field` on this resource."
|
||||
"The attribute on the related resource that should match the `source_field` configured on this resource."
|
||||
],
|
||||
validate_destination_field?: [
|
||||
type: :boolean,
|
||||
default: true,
|
||||
doc:
|
||||
"Whether or not to validate that the destination field exists on the destination resource"
|
||||
"Whether or not to validate that the destination field exists on the destination resource",
|
||||
links: []
|
||||
],
|
||||
source_field: [
|
||||
type: :atom,
|
||||
links: [
|
||||
dsls: [
|
||||
"ash:dsl:attributes/attribute"
|
||||
]
|
||||
],
|
||||
doc:
|
||||
"The field on this resource that should match the `destination_field` on the related resource."
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the relationship"
|
||||
],
|
||||
relationship_context: [
|
||||
type: :any,
|
||||
as: :context,
|
||||
links: [],
|
||||
doc: """
|
||||
Context to be set on any queries or changesets generated for this relationship.
|
||||
|
||||
This is used by ash_postgres for polymorphic resources.
|
||||
Context to be set on any queries or changesets generated for managing or querying this relationship.
|
||||
"""
|
||||
],
|
||||
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"
|
||||
],
|
||||
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.
|
||||
|
||||
For example, if a value is added that has no match in the destination (very hard to do with the way Ash relationship changes work).
|
||||
"""
|
||||
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: """
|
||||
Wether or not the relationship may be edited.
|
||||
"""
|
||||
Wether 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.
|
||||
|
||||
Keep in mind, any filters that exist on the destination action are not honored when filtering on this
|
||||
relationship. The only time the read action comes into play is when loading the actual relationship, which happens when they are loaded
|
||||
explicitly and when the relationship is managed.
|
||||
"""
|
||||
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: [
|
||||
guides: [
|
||||
"ash:guide:Multiple Apis"
|
||||
]
|
||||
]
|
||||
],
|
||||
filter: [
|
||||
type: :any,
|
||||
doc: """
|
||||
A filter to be applied when reading the relationship.
|
||||
"""
|
||||
""",
|
||||
links: []
|
||||
],
|
||||
sort: [
|
||||
type: :any,
|
||||
doc: """
|
||||
A sort statement to be applied when reading the relationship.
|
||||
"""
|
||||
A sort statement to be applied when loading the relationship.
|
||||
""",
|
||||
links: []
|
||||
],
|
||||
could_be_related_at_creation?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
links: [],
|
||||
doc: """
|
||||
This toggle modifies the management of the relationship. Typically, on creation,
|
||||
the existing value of the relationship isn't loaded. However, if it is possible for things
|
||||
to be related *before* this record is created, for example if your data layers does not support
|
||||
primary keys, then you should set this to true.
|
||||
Wether 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)
|
||||
|
@ -115,6 +138,7 @@ defmodule Ash.Resource.Relationships.SharedOptions do
|
|||
{:no_fields?,
|
||||
[
|
||||
type: :boolean,
|
||||
links: [],
|
||||
doc: """
|
||||
If true, all existing entities are considered related, i.e this relationship is not based on any fields, and `source_field` and
|
||||
`destination_field` are ignored.
|
||||
|
@ -140,6 +164,7 @@ defmodule Ash.Resource.Relationships.SharedOptions do
|
|||
def manual do
|
||||
{:manual,
|
||||
type: {:ash_behaviour, Ash.Resource.ManualRelationship},
|
||||
links: [],
|
||||
doc: """
|
||||
Allows for relationships that are fetched manually. WARNING: EXPERIMENTAL
|
||||
|
||||
|
|
|
@ -69,20 +69,27 @@ defmodule Ash.Resource.Validation do
|
|||
validation: [
|
||||
type: {:ash_behaviour, Ash.Resource.Validation, Ash.Resource.Validation.Builtins},
|
||||
required: true,
|
||||
doc: "The module/opts pair of the validation"
|
||||
doc: "The module/opts pair of the validation",
|
||||
links: []
|
||||
],
|
||||
where: [
|
||||
type: {:list, {:ash_behaviour, Ash.Resource.Validation, Ash.Resource.Validation.Builtins}},
|
||||
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 instead just result in this validation being ignored.
|
||||
These validations failing will not invalidate the changes, but will instead result in this validation being ignored.
|
||||
"""
|
||||
],
|
||||
on: [
|
||||
type: {:custom, __MODULE__, :on, []},
|
||||
default: [:create, :update],
|
||||
links: [],
|
||||
doc: """
|
||||
The action types the validation should run on.
|
||||
|
||||
|
@ -92,22 +99,25 @@ defmodule Ash.Resource.Validation do
|
|||
expensive?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
links: [],
|
||||
doc:
|
||||
"If a validation is expensive, it won't be run on invalid changes. All inexpensive validations are always run, to provide informative errors."
|
||||
],
|
||||
message: [
|
||||
type: :string,
|
||||
doc: "If provided, overrides any message set by the validation error"
|
||||
doc: "If provided, overrides any message set by the validation error",
|
||||
links: []
|
||||
],
|
||||
description: [
|
||||
type: :string,
|
||||
doc: "An optional description for the validation"
|
||||
doc: "An optional description for the validation",
|
||||
links: []
|
||||
],
|
||||
before_action?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
doc:
|
||||
"If set to `true`, the validation is not run when building changesets using `Ash.Changeset.for_*`. The validation will only ever be run once the action itself is called."
|
||||
links: [],
|
||||
doc: "If set to `true`, the validation will be run in a before_action hook"
|
||||
]
|
||||
]
|
||||
|
||||
|
|
9
priv/documentation/topics/calculations.md
Normal file
9
priv/documentation/topics/calculations.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
taken from `allow_async?`
|
||||
This is useful for calculations that are very expensive, especially when combined with complex filters/join
|
||||
scenarios. By adding this, we will rerun a trimmed down version of the main query, using the primary keys for
|
||||
fast access. This will be done asynchronously for each calculation that has `allow_async?: true`.
|
||||
|
||||
Keep in mind that if the calculation is used in a filter or sort, it cannot be done asynchronously,
|
||||
and *must* be done in the main query.
|
0
priv/documentation/topics/documentation.md
Normal file
0
priv/documentation/topics/documentation.md
Normal file
36
priv/documentation/topics/manual-actions.md
Normal file
36
priv/documentation/topics/manual-actions.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
|
||||
All validation still takes place, but the `result` in any `after_action` callbacks
|
||||
attached to that action will simply be the record that was read from the database initially.
|
||||
For creates, the `result` will be `nil`, and you will be expected to handle the changeset in
|
||||
an after_action callback and return an instance of the record. This is a good way to prevent
|
||||
Ash from issuing an unnecessary update to the record, e.g updating the `updated_at` of the record
|
||||
when an action actually only involves modifying relating records.
|
||||
|
||||
You could then handle the changeset automatically.
|
||||
|
||||
For example:
|
||||
|
||||
# in the action
|
||||
|
||||
```elixir
|
||||
action :special_create do
|
||||
manual? true
|
||||
change MyApp.DoCreate
|
||||
end
|
||||
|
||||
# The change
|
||||
defmodule MyApp.DoCreate do
|
||||
use Ash.Resource.Change
|
||||
|
||||
def change(changeset, _, _) do
|
||||
Ash.Changeset.after_action(changeset, fn changeset, _result ->
|
||||
# result will be `nil`, because this is a manual action
|
||||
|
||||
result = do_something_that_creates_the_record(changeset)
|
||||
|
||||
{:ok, result}
|
||||
end)
|
||||
end
|
||||
end
|
||||
```
|
0
priv/documentation/topics/multiple_apis.md
Normal file
0
priv/documentation/topics/multiple_apis.md
Normal file
0
priv/documentation/topics/relationships.md
Normal file
0
priv/documentation/topics/relationships.md
Normal file
0
priv/documentation/topics/transactions.md
Normal file
0
priv/documentation/topics/transactions.md
Normal file
Loading…
Reference in a new issue