state_machine 0.10.0: Let’s rock!

by aaron

As excited as I am to receive my pre-ordered copy of Duke Nukem Forever and quench my 14-year thirst, I’m even more excited to announce the official release of state_machine 0.10.0. After staying underground for more than 7 months, it’s time to come out like Punxsutawney Phil and give some good news! Check out all of the goodies you’ll discover in this release!

It’s time to kick ass and chew bubble gum… and I’m all outta gum.

As was the case with the last major release (0.9.0), the theme of this one continues to be the stabilization of public APIs and integrated ORMs in support of a 1.0 release. With that in mind, this release comes packed with a few major features and upgrades including:

  • Mongoid 2.0.0+ (rc7 or higher) support
  • Upgraded support for MongoMapper 0.9.0+ (including i18n and observer hooks)
  • Path analysis via {state}_paths
  • after_failure callbacks
  • Access to transition context during state_machine exceptions
  • Framework for simplifying the integration of legacy ORM versions
  • Nested attribute assignment support in ORM integrations
  • Optional guard bypass when accessing available events / transitions / paths
  • …and 5 bug fixes along with lots of refactoring

Here’s a very brief look at some of the new features:

Mongoid support – As expected, state_machine definitions are just like any other ORM:

class Vehicle
  include Mongoid::Document
 
  state_machine :initial => :parked do
    before_transition :parked => any - :parked, :do => :put_on_seatbelt
 
    event :park { transition [:idling, :first_gear] => :parked }
    event :shift_up { transition :idling => :first_gear }
 
    ...
  end
end
 
Vehicle.with_states(:parked, :idling) # => [#<Vehicle _id: ... >, ...]

Path analysis – Previously, you could only find the *next* set of transitions via {state}_transitions. Now you can see the whole set of transition paths for an object:

# Building on the example above...
vehicle = Vehicle.new
vehicle.state = "idling"
vehicle.state_paths
# => [
#  [#<StateMachine::Transition event=:park>],
#  [#<StateMachine::Transition event=:shift_up ...>,  #<StateMachine::Transition event=:park ...>]
# ]
 
vehicle.state_paths.to_states
# => [:first_gear, :parked]
 
vehicle.state_paths.events
# => [:shift_up, :park]
 
# Find all paths that start and end on certain states
vehicle.state_paths(:from => :first_gear, :to => :parked)
# => [
#  [#<StateMachine::Transition event=:shift_up ...>]
# ]

after_failure callbacks – Allows you to hook into transitions that fail to complete successfully. Note this replaces the :include_failures option on after_transition hooks.

class Vehicle
  state_machine do
    after_failure do |vehicle, transition|
      logger.error "Vehicle #{vehicle} failed to transition on #{transition.event}"
    end
 
    after_failure :on => :ignite, :do => :log_ignition_failure
    ...
  end
end

See the full list of changes here.

Hail to the contributors, baby!

As always, this release could not have been accomplished without support or inspiration from its community. In particular, these folks deserve a drink on me:

  • VChad Boyd
  • Morgan Christiansson
  • Karim Helal
  • Jon Klein
  • Benjamin ter Kuile
  • Dmitriy Landberg
  • Alexey Palazhchenko
  • Kaleem Ullah

If I missed anyone, please let me know!

We meet again, Doctor Jones!

It was almost a year ago when I asked for your help in making state_machine 1.0 a reality. Although I expected the library to be in feature lockdown, I decided that this interim 0.10.0 release was necessary for it to reach a stable state. Without your help, state_machine would never have become what it is today.

Like last time, I humbly ask for your help to get to 1.0. Report bugs, spread the word, and help me reach this goal by May 2011.

As always, don’t forget to follow the project! The bigger we can make this community, the more successful the project be!

Addendum: Please see state_machine 0.10.1 to avoid issues with multiple machines on a single class.

Comments

5 Responses to “state_machine 0.10.0: Let’s rock!”

  1. Nicolas Blanco on March 21st, 2011 7:41 am

    Thank you very much for your excellent gem.

    Great to see a lib that I’ve started to use in Rails 2 is still heavily under development :).

  2. Joe Van Dyk on March 21st, 2011 11:26 am

    Say I have an Order class that has an execute_payment event. This transition makes a call out to the payment gateway and authorizes a credit card payment.

    I’ve haven’t been able to come up with a good way to capture error messages with state_machine in this type of a situation. I’ve always had to define a method called execute_payment that called super() to handle this.

    Ideas? Did I miss something?

  3. aaron on March 21st, 2011 12:32 pm

    @Nicolas – Great to hear from an early adopter!

    @Joe – Not quite sure I follow the use case. You could potentially define a before_transition that called out to the payment gateway. At that point, you can decide whether to add any errors to the record. You can also return false, which halts the transition.

  4. CodeOfficer on March 23rd, 2011 1:20 am

    I’ve been using your statemachine gem for as long as I can remember. Top notch!

  5. Pete Forde on March 24th, 2011 2:50 pm

    So glad you continue to evolve state_machine, Aaron. This looks awesome (and bonus points for the They Live! reference).

    I am often mystified why people continue to use AASM when state_machine has been the clear winner since 2009.