Open zachdaniel opened 3 months ago
This is definitely an interesting idea. There are some complexities w/ the construction if the action type is itself extend
. But if we were to do it like so:
read :sign_in_with_password_for_graphql do
extends :sign_in_with_password
prepare set_context(%{token_type: :user, strategy_name: :password})
end
then we could make it work.
You mentioned idea of templates (fragments for actions) and I think it might be better.
actions do
create :create do
uses :validate_email
end
update :update_email do
uses :validate_email
end
template :validate_email do
validate ...
validate ...
change ...
end
end
Because if you have two methods A
and B
that have the same parts but also different ones you would need something C
. In case of extending it would be a method, but you don't actually want it to be a separate method (to expose it and so on) and in this template example action types are even different - create and update - which won't be possible with simple extension of an action. Templates also allow multiple uses
in the same action.
The only advantage action extension can theoretically provide is when you do not define an action that you want to extend by yourself - if it is automatically defined by some extension.
I'm on board for the structure laid out, but not the term template
(which was my term in the first place I'm aware 🙃) Ultimately we're talking about something that we compose, a simple named container of changes/validations.
Synonyms for template
:
Synonyms for uses
:
template
replacement)I think fragment
is the best name for it, but unfortunately that is kind of taken...We could call them action_fragments
i.e
action_fragments do
...
end
Alternatively, what we could do is do this using modules:
defmodule Change do
use Ash.Resource.Change.Composer
...some DSL here?
end
Its a bit heavier, and seems a bit obtuse, but has the benefit that they can be shared across resources.
If you want fragment
, why not just fragment
? Yes, there is fragment
in a query expression and there is Spark fragments, but that should be ok. And there should not be any import conflicts with those.
As for modules - I assume it will be both, like with most other things, where you will be able to define it inline (with fragment
call) or in a module (to reuse between multiple resources). Both will be identified by atoms in uses
call but collisions are unlikely (inline are lowercase and modules are capitalized).
I'm cautious of conflating terms, and these terms are kind of related. i.e "how do I share action behavior? With fragments, no, not that kind of fragment, this kind of fragment". Just want to make sure we've considered all the options. Haven't seen anything I really like yet TBH.
I'm not english native, but what do you think about "piece"?
There is also "pattern" and "recipe".
The idea is that sometimes you have small variations on the same action that you want with slightly different modifiers.
In my case I wanted to expose the generated
sign_in_with_password
read action generated byAshAuthentication
but change the token type (from:sign_in
to:user
) via GraphQL. What I had to do was copy the entire action and add aset_context/1
preparation. What I wanted to do was this:Behind the scenes I expect that the transformer will load all the entities from the other action, and then prepend them to all the entities in the extend action.
This could be a bad idea. I am sure Zach will tell me why.