Используя plug_week state_machine, могу ли я ссылаться на объект activerecord во время события? - PullRequest
3 голосов
/ 27 апреля 2011

Я пытаюсь реализовать событие "suspend", которое переводит объект в состояние: suspen. Но мне нужно иметь возможность «отстраниться» и вернуться в прежнее состояние. Я добавил в модель поле previous_state, но не вижу, как получить к нему доступ в блоке событий.

Вот основная логика, которую я пытаюсь реализовать:

event :suspend do
  owner.previous_state = self.state
  transition [:new, :old] => :suspended
end

event :unsuspend do
  transition :suspended => owner.previous_state.to_sym
  owner.previous_state = nil
end

Документы state_machine не очень помогли, и я не могу найти примеры в Интернете. Иногда сложно понять, как описать что-то в гугле:)

Ответы [ 3 ]

1 голос
/ 03 марта 2014

Автор state_machine также предоставил здесь альтернативное решение: https://groups.google.com/d/msg/pluginaweek-talk/LL9VJdL_x9c/vP1qv6br734J

К сведению:

Другое возможное решение - проявить немного творческого подхода к работе автомата.В ORM, таких как ActiveRecord, есть множество хуков, которые позволяют нам устанавливать состояние на любом этапе процесса.Рассмотрим следующее:

class Vehicle < ActiveRecord::Base
  before_validation do |vehicle|
    # Set the actual value based on the previous state if we've just restored
    vehicle.state = vehicle.previous_state if vehicle.restored?
  end

  state_machine :initial => :parked do
    event :ignite do
      transition :parked => :idling
    end

    event :restore do
      transition any => :restored
    end

    state :parked do
      validates_presence_of :name
    end
  end

  # Look up previous state here...
  def previous_state
    'parked'
  end
end

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

v = Vehicle.new(:name => 'test')  # => #<Vehicle id: nil, name: "test", state: "parked">
v.save                            # => true
v.name = nil                      # => nil
v.ignite                          # => true
v                                 # => #<Vehicle id: 1, name: nil, state: "idling">
v.restore                         # => false
v.errors                          # => #<OrderedHash {:name=>["can't be blank"]}>
v.state                           # => "idling"
v.name = 'test'                   # => "test"
v.restore                         # => true
v                                 # => #<Vehicle id: 1, name: "test", state: "parked">
v.parked?                         # => true

Это должно потребовать на одно попадание в базу данных меньше, чем до проверки.В моем случае полная картина выглядит так:

module Interpreting::Status

  extend ActiveSupport::Concern

  included do

    before_validation :restore_previous_state, if: :interpreter_cancelled?

    state_machine :state, :initial => :ordered do

      before_transition :to => :interpreter_booked, :do => :set_previous_state

      state :ordered

      state :confirmed

      state :interpreter_booked

      state :interpreter_cancelled # Transient status
    end

  end


protected

  def set_previous_state
    self.previous_state = self.state
  end

  def restore_previous_state
    self.state = self.previous_state
  end

end
0 голосов
/ 27 сентября 2015

Простое решение

Я предоставляю альтернативную версию решения, которое использует owner аргумент блока. Может быть полезно в некоторых случаях.

state_machine :initial => :new do
  state :new
  state :old

  before_transition :on => :suspend do |owner|
    owner.previous_state = owner.state
  end

  before_transition :on => :unsuspend do |owner|
    owner.previous_state.present?
  end

  after_transition :on => :unsuspend do |owner|
    owner.state = owner.previous_state
  end

  event :suspend do
    transition any - :suspended => :suspended
  end

  event :unsuspend do
    transition :suspended => :unsuspended
  end
end

Использование около_перехода

Также обратите внимание, что вы можете заменить два блока unsuspend на around_transition:

around_transition :on => :unsuspend do |owner, transition_block|
  if owner.previous_state.present?
    transition_block.call
    owner.state = owner.previous_state
  end
end
0 голосов
/ 27 апреля 2011

На мой взгляд, это не идеальное решение, но я понял, как выполнить свою задачу:

state_machine :initial => :new do
  state :new

  state :old

  state :suspended
  before_transition :to => :suspended, :do => :set_previous_state

  state :unsuspended
  after_transition :to => :unsuspended, :do => :restore_previous_state

  event :suspend do
    transition any - :suspended => :suspended
  end

  event :unsuspend do
    transition :suspended => :unsuspended, :if => :previous_state_present?
  end
end

private

def previous_state_present?
  previous_state.present?
end

def set_previous_state
  self.previous_state = state
end

def restore_previous_state
  if previous_state
    self.state = previous_state
    self.previous_state = nil
  end
end

Я начал с добавления в компьютер состояния "без приостановки".Хотя я никогда не хочу, чтобы что-то оставалось в этом состоянии, я не могу динамически сообщить state_machine, в какое состояние я хочу отменить приостановку.

Я добавил обратный вызов before_transition к событию suspend, чтобы сохранить состояние до его приостановки.

Я добавил обратный вызов after_transition к событию unsuspend, чтобы состояние немедленно обновлялось до предыдущего состояния, а это предыдущее состояние затем стиралось, чтобы предотвратить проблемы позже в жизни объекта.

Это не идеально.Это работает, но это намного сложнее, чем просто создавать события suspend и unsuspend в качестве автономных методов.Я не пошел по этому пути, потому что я хочу, чтобы state_machine контролировал все изменения состояния, а выход из него снимает защиту от перехода в / из недопустимых состояний, обратных вызовов и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...