Rails AASM жемчужина, как вы могли бы достичь подсостояние? - PullRequest
1 голос
/ 11 апреля 2020

Я впервые использую AASM, и мне интересно, как я могу достичь реализации в подсостоянии. Чистый пример, как я учусь через это. Допустим, мы отслеживаем состояние пациента, когда он проходит через больницу. Вот воображаемый список состояний и подсостояний:

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 правила:

  • Major states обычно продвигаются вперед линейно, но откат иногда возможен
  • в пределах данного state доступны только те, которые связаны substates, хотя они не являются линейными и могут двигаться вперед и назад

Например, после того, как вы попали в больницу, вы можете подождать где-нибудь, подсостояния похожи на места. Однако, как только вы with_doctor, вы больше не можете находиться в гостиной, месте для курения или уборной, если только по какой-то причине врач не отправит вас обратно к entered.

Моему нынешнему воображаемому , непроверенное решение (некоторые синтаксис может быть не идеальным, но идея несет в себе) состоит в том, чтобы использовать 2 конечных автомата:

  • Первичный отслеживает основной state и не обновляется напрямую, он функционирует исключительно как защита
  • Вторичное обновление напрямую обновляется, обратные вызовы в дополнительном состоянии используются для обновления основного состояния

Например:

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

Впервые используя этот драгоценный камень, так что интересно, если это супер хакерский или есть другие, более традиционные способы сделать это.

...