1

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 states generally move forward linearly, but backtracking is occasionally possible
  • within a given state, only those related substates are 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 state and 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.

james
  • 3,989
  • 8
  • 47
  • 102
  • I think the level of complexity here indicates that this should not all be handled by a single object and that you're on the right track by splitting the into multiple state machines rather then one big one with a bunch of substates. – max Apr 11 '20 at 10:01
  • hi again @max! from the other question, I looked into `aasm` and i decided that because it seemed to offer more flexibility, i would take advantage of it by creating `substates`. I.e., the `substates` as I have them here didn't exist at all in the previous example, I'm now just trying to experiment & flex to see what this can do. As it is, does my `state`/`substate` implementation make sense (if it were constrained to one object)? – james Apr 11 '20 at 22:12
  • Unrelated, but the “entry” sub-states don’t seem like states, rather statuses, and you can’t exit the entry state until all three are completed. State machines are best with normalized transitions, and entry doesn’t seem to fit that criteria very well. – Dave Newton Apr 11 '20 at 22:17
  • @DaveNewton that's a good point, more reflective of this made up example. Instead the `substates` for `entered` could be like... `in_lounge`, `in_smoking_area`, `in_restroom`, basically a location state. That works better? – james Apr 11 '20 at 22:27
  • *shrug* I suppose, but unless there’s something specific about those locations I don’t see what value they provide—seems more like a location property than a state. If you’re just trying to figure out sub-states, fine, but please be explicit about that, otherwise you’re likely to get annoying comments like mine trying to figure out the actual problem :) – Dave Newton Apr 11 '20 at 22:32
  • @DaveNewton ok i updated the question to make it more clear. Yeah I'm just trying to figure out substates and how implementation might work with `aasm` – james Apr 11 '20 at 22:45

0 Answers0