The gem is ready for Rails active record integration (see below and the
examples folder).

Usage and functionality summary

Define the set of states you want your stateful object to have by creating
a class for each state and inheriting from +StatePattern:State+.

All public methods defined in this state classes, except enter and
exit (see below), are then available to the stateful object and
their behaviour will depend on the current state .

If this automatic delegation to the current state public methods is not
enough for your stateful object then you can just reopen the method and use
super whenever you want to call the state implementation.

Inside each state instance you can access the stateful object through the
stateful method.

Inside each state instance you can access the previous state through the
previous_state method.

Define enter or exit methods to hook any behaviour you
want to execute whenever the stateful object enters or exits the state.

An event is just a method that calls transition_to at some point.

If you want guards for some event just use plain old ifs before your
transition_to.

Enter and exit hooks

Inside your state classes, any code that you put inside the enter method
will be executed when the state is instantiated. You can also use the exit
hook which is triggered when a successful transition to another state takes
place.

Overriding automatic delegation

If the automatic delegation to the current state public methods is not
enough for your stateful object then you can just reopen the method and use
super whenever you want to call the state implementation.

class TrafficSemaphore
include StatePattern
set_initial_state Stop
def color
# some great code here
# now we call the current state implementation
super
# more cool hacking here
end
end

Rails

To use the state pattern in your Rails models you need to:

Add a state column for your model table of type string

Include StatePattern::ActiveRecord in your model file

Use the state pattern as you would do in a plain Ruby class as shown above

Please see the examples folder for a Rails 3 example.

Example

Remember to put each class in its correct file following Rails naming
conventions.

module BlogStates
#we can put common state behaviour into a base state class or we could have implemented it inside the model with methods that call super, your choice
class StateBase < StatePattern::State
def submit!
end
def publish!
end
def reject!
transition_to(Rejected)
stateful.save!
end
def verify!
end
end
class Published < StateBase
end
class Pending < StateBase
def publish!
transition_to(Published) if stateful.valid?
stateful.save!
end
end
class Unverified < StateBase
def submit!
if stateful.submitter.manager?
if stateful.profile_complete?
transition_to(Published)
else
transition_to(Pending)
end
stateful.save!
end
end
def verify!
transition_to(Pending)
stateful.save!
end
end
class Rejected < StateBase
def publish!
transition_to(Published) if stateful.valid?
stateful.save!
end
def enter
Notifier.notify_blog_owner(stateful)
end
end
end
class Blog < ActiveRecord::Base
include StatePattern::ActiveRecord
set_initial_state Unverified
.
.
.
end

The state attribute

By default StatePattern::ActiveRecord expects a column named
'state' in the model. If you prefer to use another attribute do: