Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: next_state change function #5

Closed
sevenseacat opened this issue Sep 6, 2023 · 2 comments
Closed

Proposal: next_state change function #5

sevenseacat opened this issue Sep 6, 2023 · 2 comments
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@sevenseacat
Copy link

sevenseacat commented Sep 6, 2023

Is your feature request related to a problem? Please describe.

If a state machine has two transition definitions, eg:

transition(:initialise, from: :enqueued, to: :initialising)
transition(:initialise, from: :paused, to: :resuming)

Then the initialise action can't neatly change states with transition_state because the target state isn't static, it depends on the resource being initialised.

Describe the solution you'd like

A custom change action could be written to pick the state to transition to, eg.

update :foobar do
  change fn changeset, _ -> 
    case something(changeset) do
      :foo -> AshStateMachine.transition_state(changeset, :foo)
      :bar -> AshStateMachine.transition_state(changeset, :bar)
    end
  end
end

Based on this, I think it should be possible to add another change function like transition_state but called something like next_state.

next_state could introspect the defined transitions for a resource and find one that:

  • matches the current action
  • has the current state in the from list
  • has a single state in the to list (that isn't :*)

and then transition to that state.

An error could be raised (similar to NoValidTransition, maybe NoValidNextState) if there is no single state that can be determined to transition to.

Express the feature either with a change to resource syntax, or with a change to the resource interface

  update :initialise do 
    change next_state() # new function!
  end

Additional context

My initial attempt at starting to implement this that doesn't even compile and is probably pretty dodgy (based on copypasting and tweaking the code for transition_state) -

defmodule AshStateMachine.BuiltinChanges.NextState do
  use Ash.Resource.Change

  def next_state, do: {__MODULE__, []}

  def change(%{action_type: :update} = changeset) do
    transitions =
      AshStateMachine.Info.state_machine_transitions(changeset.resource, changeset.action.name)

    attribute = AshStateMachine.Info.state_machine_state_attribute!(changeset.resource)
    current_state = Map.get(changeset.data, attribute)

    case Enum.filter(transitions, &(current_state in List.wrap(&1.from))) do
      [%{to: to}] when to != [:*] and to != :* ->
        target = List.unwrap(to)
        Ash.Changeset.force_change_attribute(changeset, attribute, target)

      list ->
        Ash.Changeset.add_error(
          changeset,
          AshStateMachine.Errors.NoValidNextState.exception(
            old_state: current_state,
            action: changeset.action.name,
            next_states: Enum.flat_map(list, & &1.to)
          )
        )
    end
  end
end
@sevenseacat sevenseacat added the enhancement New feature or request label Sep 6, 2023
@sevenseacat sevenseacat changed the title next_state change function Proposal: next_state change function Sep 6, 2023
@zachdaniel zachdaniel added the good first issue Good for newcomers label Sep 6, 2023
@zachdaniel
Copy link
Contributor

Sounds like a neat idea! We can likely check this at compile time as well :)

@jimsynz
Copy link
Contributor

jimsynz commented Sep 8, 2023

Closed by #6

@jimsynz jimsynz closed this as completed Sep 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

3 participants