It's my first time using AASM and I'm wondering how I might be able to achieve a substate implementation. Pure example as I'm learning through this. Let's say we're tracking the state of a patient as they move through a hospital. Here's an imaginary list of states & substates:
state :entered, initial: true
substate :in_lounge
substate :in_smoking_area
substate :in_restroom
state :with_doctor
substate :checked_vitals
substate :received_consultation
state :checkout
substate :payment_pending
substate :payment_success
substate :payment_error
state :complete
2 rules:
- major
statesgenerally move forward linearly, but backtracking is occasionally possible - within a given
state, only those relatedsubstatesare available, though they are not linear, and can move back and forth
For example, once you've entered a hospital you can wait anywhere, the substates are like locations. However, once you're with_doctor you can no longer be in the lounge, smoking area, or restroom, unless for some reason the doctor sends you back to entered.
My current imagined, untested solution (some of the syntax may not be perfect, but the idea carries) is to use 2 state machines:
- Primary keeps track of the main
stateand isn't updated directly, it functions purely as a guard - Secondary is directly updated, callbacks on secondary state are used to update primary state
For example:
aasm(:primary, column:"primary_state") do
state :primary_entered, initial: true
state :primary_with_doctor
state :primary_checkout
state :primary_complete
event :to_primary_with_doctor do
transitions from: :primary_entered, to: :primary_with_doctor
end
end
aasm(:secondary, column:"secondary_state") do
state :entered, initial: true
state :in_lounge
state :in_smoking_area
state :in_restroom
state :with_doctor
... # this example only works with substates for first state
[:in_smoking_area, :in_lounge, :in_restroom].each do |this_state|
event :"to_#{this_state}" do
transitions to: this_state, guard: :before_primary_with_doctor
end
# by not specifying a from, this captures notion that any movements in any direction between this set of states is allowable, as long as the guard is met
end
event :to_with_doctor do
transitions to: :with_doctor, guard: :before_primary_with_doctor, success: :set_primary_state("with_doctor")
# secondary state machine has a redundant state, that updates the primary state, allowing primary state to only be used as guards and not to be directly manipulated by the system
end
end
def before_primary_with_doctor
return self.aasm(:primary).current == :entered
# eventually I'm thinking about making primary state enums so I can mathematically do something like...
# self.aasm(:primary).current <= :with_doctor
# obviously syntax not right, but that's how I'm thinking of using this
end
def set_primary_state(state)
self.aasm(:primary).fire(:"to_primary_#{state}")
end
First time using this gem though, so wondering if this is super hacky or if there are other more conventional ways of doing this.