I'm trying to implement a "suspend" event that transitions the object to the :suspended state. But I need to be able to "unsuspend", and return to the previous state. I added a previous_state field to the model, but I can't see how to access it inside an event block.
This is the basic logic I'm trying to implement:
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
The state_machine docs haven't been very helpful, and I can't find examples online. Sometimes it's tough to know how to describe something to google :)
The author of state_machine has also provided an alternate solution here: https://groups.google.com/d/msg/pluginaweek-talk/LL9VJdL_x9c/vP1qv6br734J
To wit:
Another possible solution is to be a little creative with how the state machine works. There are plenty of hooks in ORMs like ActiveRecord that give us the ability to set the state at any stage in the process. Consider the following:
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
In this example, a new state, restored, is introduced even though it never actually gets persisted in the database. We instead provide a before_validation hook that rewrites the state based on the previous state. You can see the results below:
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
This should require one less database hit as it occurs before validation. In my case the complete picture looks like this:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With