Add ability to extend actions
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 by AshAuthentication but change the token type (from :sign_in to :user) via GraphQL. What I had to do was copy the entire action and add a set_context/1 preparation. What I wanted to do was this:
extend :sign_in_with_password do
as :sign_in_with_password_for_graphql
prepare set_context(%{token_type: :user, strategy_name: :password})
end
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.
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:
- section
- segment
- fragment
- slice
- part
- share
- block
- step
Synonyms for uses:
- include
- (same as
templatereplacement)
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".