<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,5 +1,9 @@
 == master
 
+== 0.2.1 / 2008-07-05
+
+* Add more descriptive exceptions
+* Assume the default state attribute is &quot;state&quot; if one is not provided
 * Add :except_from option for transitions if you want to blacklist states
 * Add PluginAWeek::StateMachine::Machine#states
 * Add PluginAWeek::StateMachine::Event#transitions</diff>
      <filename>CHANGELOG.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,7 @@
 == state_machine
 
-+state_machine+ adds support for creating state machines for attributes within a model.
++state_machine+ adds support for creating state machines for attributes within
+a model.
 
 == Resources
 
@@ -28,15 +29,21 @@ and deciding how to behave based on the values in those columns.  This can becom
 cumbersome and difficult to maintain when the complexity of your models starts to
 increase.
 
-+state_machine+ simplifies this design by introducing the various parts of a state
-machine, including states, events, and transitions.  However, its api is designed
-to be similar to ActiveRecord in terms of validations and callbacks, making it
-so simple you don't even need to know what a state machine is :)
++state_machine+ simplifies this design by introducing the various parts of a real
+state machine, including states, events, and transitions.  However, the api is
+designed to be similar to ActiveRecord in terms of validations and callbacks,
+making it so simple you don't even need to know what a state machine is :)
 
 == Usage
 
 === Example
 
+Below is an example of many of the features offered by this plugin, including
+* Initial states
+* State callbacks
+* Event callbacks
+* Conditional transitions
+
   class Vehicle &lt; ActiveRecord::Base
     state_machine :state, :initial =&gt; 'idling' do
       before_exit   'parked', :put_on_seatbelt
@@ -74,8 +81,32 @@ so simple you don't even need to know what a state machine is :)
         transition :to =&gt; 'parked', :from =&gt; 'stalled', :if =&gt; :auto_shop_busy?
       end
     end
+    
+    def tow!
+    end
+    
+    def fix!
+    end
+    
+    def auto_shop_busy?
+      false
+    end
   end
 
+Using the above model as an example, you can interact with the state machine
+like so:
+
+  vehicle = Vehicle.create    # =&gt; #&lt;Vehicle id: 1, seatbelt_on: false, state: &quot;parked&quot;&gt;
+  vehicle.ignite              # =&gt; true
+  vehicle                     # =&gt; #&lt;Vehicle id: 1, seatbelt_on: true, state: &quot;idling&quot;&gt;
+  vehicle.shift_up            # =&gt; true
+  vehicle                     # =&gt; #&lt;Vehicle id: 1, seatbelt_on: true, state: &quot;first_gear&quot;&gt;
+  vehicle.shift_up            # =&gt; true
+  vehicle                     # =&gt; #&lt;Vehicle id: 1, seatbelt_on: true, state: &quot;second_gear&quot;&gt;
+  
+  # The bang (!) operator can raise exceptions if the event fails
+  vehicle.park!               # =&gt; PluginAWeek::StateMachine::InvalidTransition: Cannot transition via :park from &quot;second_gear&quot;
+
 == Tools
 
 Jean Bovet - {Visual Automata Simulator}[http://www.cs.usfca.edu/~jbovet/vas.html].</diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -5,7 +5,7 @@ require 'rake/contrib/sshpublisher'
 
 spec = Gem::Specification.new do |s|
   s.name              = 'state_machine'
-  s.version           = '0.2.0'
+  s.version           = '0.2.1'
   s.platform          = Gem::Platform::RUBY
   s.summary           = 'Adds support for creating state machines for attributes within a model'
   </diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -12,29 +12,65 @@ module PluginAWeek #:nodoc:
     end
     
     module MacroMethods
-      # Creates a state machine for the given attribute.
+      # Creates a state machine for the given attribute.  The default attribute
+      # is &quot;state&quot;.
       # 
       # Configuration options:
       # * +initial+ - The initial value of the attribute.  This can either be the actual value or a Proc for dynamic initial states.
       # 
-      # == Example
+      # This also requires a block which will be used to actually configure the
+      # events and transitions for the state machine.  *Note* that this block will
+      # be executed within the context of the state machine.  As a result, you will
+      # not be able to access any class methods on the model unless you refer to
+      # them directly (i.e. specifying the class name).
       # 
-      # With a static state:
+      # For examples on the types of configured state machines and blocks, see
+      # the section below.
+      # 
+      # == Examples
+      # 
+      # With the default attribute and no initial state:
+      # 
+      #   class Switch &lt; ActiveRecord::Base
+      #     state_machine do
+      #       event :park do
+      #         ...
+      #       end
+      #     end
+      #   end
+      # 
+      # The above example will define a state machine for the attribute &quot;state&quot;
+      # on the model.  Every switch will start with no initial state.
+      # 
+      # With a custom attribute:
       # 
       #   class Switch &lt; ActiveRecord::Base
-      #     state_machine :state, :initial =&gt; 'off' do
+      #     state_machine :status do
       #       ...
       #     end
       #   end
       # 
-      # With a dynamic state:
+      # With a static initial state:
       # 
       #   class Switch &lt; ActiveRecord::Base
-      #     state_machine :state, :initial =&gt; Proc.new {|switch| (8..22).include?(Time.now.hour) ? 'on' : 'off'} do
+      #     state_machine :status, :initial =&gt; 'off' do
       #       ...
       #     end
       #   end
-      def state_machine(attribute, options = {}, &amp;block)
+      # 
+      # With a dynamic initial state:
+      # 
+      #   class Switch &lt; ActiveRecord::Base
+      #     state_machine :status, :initial =&gt; Proc.new {|switch| (8..22).include?(Time.now.hour) ? 'on' : 'off'} do
+      #       ...
+      #     end
+      #   end
+      # 
+      # == Events and Transitions
+      # 
+      # For more information about how to configure an event and its associated
+      # transitions, see PluginAWeek::StateMachine::Machine#event
+      def state_machine(*args, &amp;block)
         unless included_modules.include?(PluginAWeek::StateMachine::InstanceMethods)
           write_inheritable_attribute :state_machines, {}
           class_inheritable_reader :state_machines
@@ -44,10 +80,12 @@ module PluginAWeek #:nodoc:
           include PluginAWeek::StateMachine::InstanceMethods
         end
         
+        options = args.extract_options!
+        attribute = args.any? ? args.first.to_s : 'state'
+        options[:initial] = state_machines[attribute].initial_state_without_processing if !options.include?(:initial) &amp;&amp; state_machines[attribute]
+        
         # This will create a new machine for subclasses as well so that the owner_class and
         # initial state can be overridden
-        attribute = attribute.to_s
-        options[:initial] = state_machines[attribute].initial_state_without_processing if !options.include?(:initial) &amp;&amp; state_machines[attribute]
         machine = state_machines[attribute] = PluginAWeek::StateMachine::Machine.new(self, attribute, options)
         machine.instance_eval(&amp;block) if block
         machine</diff>
      <filename>lib/state_machine.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,7 +17,11 @@ module PluginAWeek #:nodoc:
       delegate  :owner_class,
                   :to =&gt; :machine
       
-      # Creates a new event with the given name
+      # Creates a new event within the context of the given machine.
+      # 
+      # Configuration options:
+      # * +before+ - Callbacks to invoke before the event is fired
+      # * +after+ - Callbacks to invoke after the event is fired
       def initialize(machine, name, options = {})
         options.assert_valid_keys(:before, :after)
         
@@ -68,19 +72,27 @@ module PluginAWeek #:nodoc:
         transition
       end
       
-      # Attempts to perform one of the event's transitions for the given record
+      # Attempts to perform one of the event's transitions for the given record.
+      # Any additional arguments will be passed to the event's callbacks.
       def fire(record, *args)
-        record.class.transaction {invoke_transition_callbacks(record, false, *args) || raise(ActiveRecord::Rollback)} || false
+        fire_with_optional_bang(record, false, *args) || false
       end
       
       # Attempts to perform one of the event's transitions for the given record.
       # If the transition cannot be made, then a PluginAWeek::StateMachine::InvalidTransition
       # error will be raised.
       def fire!(record, *args)
-        record.class.transaction {invoke_transition_callbacks(record, true, *args) || raise(ActiveRecord::Rollback)} || raise(PluginAWeek::StateMachine::InvalidTransition)
+        fire_with_optional_bang(record, true, *args) || raise(PluginAWeek::StateMachine::InvalidTransition, &quot;Cannot transition via :#{name} from #{record.send(machine.attribute).inspect}&quot;)
       end
       
       private
+        # Fires the event
+        def fire_with_optional_bang(record, bang, *args)
+          record.class.transaction do
+            invoke_transition_callbacks(record, bang, *args) || raise(ActiveRecord::Rollback)
+          end
+        end
+        
         # Add the various instance methods that can transition the record using
         # the current event
         def add_transition_actions</diff>
      <filename>lib/state_machine/event.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,15 +2,34 @@ require 'state_machine/event'
 
 module PluginAWeek #:nodoc:
   module StateMachine
-    # Represents a state machine for a particular attribute
+    # Represents a state machine for a particular attribute.  State machines
+    # consist of events (a.k.a. actions) and a set of transitions that define
+    # how the state changes after a particular event is fired.
     # 
-    # == State callbacks
+    # A state machine may not necessarily know all of the possible states for
+    # an object since they can be any arbitrary value.
     # 
-    # These callbacks are invoked in the following order:
-    # 1. before_exit (old state)
-    # 2. before_enter (new state)
-    # 3. after_exit (old state)
-    # 4. after_enter (new state)
+    # == Callbacks
+    # 
+    # Callbacks are supported for hooking into event calls and state transitions.
+    # The order in which these callbacks are invoked is shown below:
+    # * (1) before_exit (from state)
+    # * (2) before_enter (to state)
+    # * (3) before (event)
+    # * (-) update state
+    # * (4) after_exit (from state)
+    # * (5) after_enter (to state)
+    # * (6) after (event)
+    # 
+    # == Cancelling callbacks
+    # 
+    # If a &lt;tt&gt;before_*&lt;/tt&gt; callback returns +false+, all the later callbacks
+    # and associated event are cancelled.  If an &lt;tt&gt;after_*&lt;/tt&gt; callback returns
+    # false, all the later callbacks are cancelled.  Callbacks are run in the
+    # order in which they are defined.
+    # 
+    # Note that if a &lt;tt&gt;before_*&lt;/tt&gt; callback fails and the bang version of an
+    # event was invoked, an exception will be raised instaed of returning false.
     class Machine
       # The events that trigger transitions
       attr_reader :events
@@ -30,17 +49,17 @@ module PluginAWeek #:nodoc:
       # Creates a new state machine for the given attribute
       # 
       # Configuration options:
-      # * +initial+ - The initial value to set the attribute to
+      # * +initial+ - The initial value to set the attribute to. This can be an actual value or a proc, which will be evaluated at runtime.
       # 
       # == Scopes
       # 
-      # This will automatically created a named scope called with_#{attribute}
+      # This will automatically create a named scope called with_#{attribute}
       # that will find all records that have the attribute set to a given value.
       # For example,
       # 
       #   Switch.with_state('on') # =&gt; Finds all switches where the state is on
       #   Switch.with_states('on', 'off') # =&gt; Finds all switches where the state is either on or off
-      def initialize(owner_class, attribute, options = {})
+      def initialize(owner_class, attribute = 'state', options = {})
         options.assert_valid_keys(:initial)
         
         @owner_class = owner_class
@@ -53,7 +72,7 @@ module PluginAWeek #:nodoc:
       end
       
       # Gets the initial state of the machine for the given record. The record
-      # is only used if a dynamic initial state is being used
+      # is only used if a dynamic initial state was configured.
       def initial_state(record)
         @initial_state.is_a?(Proc) ? @initial_state.call(record) : @initial_state
       end
@@ -64,25 +83,19 @@ module PluginAWeek #:nodoc:
       end
       
       # Defines an event of the system.  This can take an optional hash that
-      # defines callbacks which will be invoked when the object enters/exits
-      # the event.
+      # defines callbacks which will be invoked before and after the event is
+      # invoked on the object.
       # 
       # Configuration options:
-      # * +before+ - Invoked before the event has been executed
-      # * +after+ - Invoked after the event has been executed
-      # 
-      # == Callback order
-      # 
-      # These callbacks are invoked in the following order:
-      # 1. before
-      # 2. after
+      # * +before+ - One or more callbacks that will be invoked before the event has been fired
+      # * +after+ - One or more callbacks that will be invoked after the event has been fired
       # 
       # == Instance methods
       # 
       # The following instance methods are generated when a new event is defined
       # (the &quot;park&quot; event is used as an example):
-      # * &lt;tt&gt;park(*args)&lt;/tt&gt; - Fires the &quot;park&quot; event, transitioning from the current state to the next valid state.  This takes an optional +args+ list which is passed to the event callbacks.
-      # * &lt;tt&gt;park!(*args)&lt;/tt&gt; - Fires the &quot;park&quot; event, transitioning from the current state to the next valid state.  This takes an optional +args+ list which is passed to the event callbacks.  If the transition cannot happen (for validation, database, etc. reasons), then an error will be raised
+      # * &lt;tt&gt;park(*args)&lt;/tt&gt; - Fires the &quot;park&quot; event, transitioning from the current state to the next valid state.  This takes an optional list of arguments which are passed to the event callbacks.
+      # * &lt;tt&gt;park!(*args)&lt;/tt&gt; - Fires the &quot;park&quot; event, transitioning from the current state to the next valid state.  This takes an optional list of arguments which are passed to the event callbacks.  If the transition cannot happen (for validation, database, etc. reasons), then an error will be raised
       # 
       # == Defining transitions
       # 
@@ -100,6 +113,19 @@ module PluginAWeek #:nodoc:
       # See PluginAWeek::StateMachine::Event#transition for more information on
       # the possible options that can be passed in.
       # 
+      # *Note* that this block is executed within the context of the actual event
+      # object.  As a result, you will not be able to reference any class methods
+      # on the model without referencing the class itself.  For example,
+      # 
+      #   class Car &lt; ActiveRecord::Base
+      #     def self.safe_states
+      #       %w(parked idling first_gear)
+      #     end
+      #     
+      #     state_machine :state do
+      #   event :park do
+      #     transition :to 
+      # 
       # == Example
       # 
       #   class Car &lt; ActiveRecord::Base</diff>
      <filename>lib/state_machine/machine.rb</filename>
    </modified>
    <modified>
      <diff>@@ -23,6 +23,12 @@ module PluginAWeek #:nodoc:
       delegate  :machine,
                   :to =&gt; :event
       
+      # Creates a new transition within the context of the given event.
+      # 
+      # Configuration options:
+      # * +to+ - The state being transitioned to
+      # * +from+ - One or more states being transitioned from.  Default is nil (can transition from any state)
+      # * +except_from+ - One or more states that *can't* be transitioned from.
       def initialize(event, options) #:nodoc:
         @event = event
         
@@ -43,22 +49,28 @@ module PluginAWeek #:nodoc:
       end
       
       # Determines whether or not this transition can be performed on the given
-      # states
+      # record.  The transition can be performed if the record's state matches
+      # one of the states that are valid in this transition.
       def can_perform_on?(record)
         from_states.empty? || from_states.include?(record.send(machine.attribute)) == @require_match
       end
       
       # Runs the actual transition and any callbacks associated with entering
-      # and exiting the states
+      # and exiting the states.  Any additional arguments are passed to the
+      # callbacks.
+      # 
+      # *Note* that the caller should check &lt;tt&gt;can_perform_on?&lt;/tt&gt; before calling
+      # perform.  This will *not* check whether transition should be performed.
       def perform(record, *args)
         perform_with_optional_bang(record, false, *args)
       end
       
       # Runs the actual transition and any callbacks associated with entering
       # and exiting the states. Any errors during validation or saving will be
-      # raised.
+      # raised.  If any +before+ callbacks fail, a PluginAWeek::StateMachine::InvalidTransition
+      # error will be raised.
       def perform!(record, *args)
-        perform_with_optional_bang(record, true, *args) || raise(PluginAWeek::StateMachine::InvalidTransition)
+        perform_with_optional_bang(record, true, *args) || raise(PluginAWeek::StateMachine::InvalidTransition, &quot;Cannot transition via :#{event.name} from #{record.send(machine.attribute).inspect} to #{to_state.inspect}&quot;)
       end
       
       private</diff>
      <filename>lib/state_machine/transition.rb</filename>
    </modified>
    <modified>
      <diff>@@ -37,12 +37,7 @@ module Factory
   end
   
   build Car do |attributes|
-    attributes[:highway] = create_highway unless attributes.include?(:highway)
-    attributes[:auto_shop] = create_auto_shop unless attributes.include?(:auto_shop)
-    attributes.reverse_merge!(
-      :seatbelt_on =&gt; false,
-      :insurance_premium =&gt; 50
-    )
+    valid_vehicle_attributes(attributes)
   end
   
   build Highway do |attributes|
@@ -62,6 +57,11 @@ module Factory
   end
   
   build Vehicle do |attributes|
-    valid_car_attributes(attributes)
+    attributes[:highway] = create_highway unless attributes.include?(:highway)
+    attributes[:auto_shop] = create_auto_shop unless attributes.include?(:auto_shop)
+    attributes.reverse_merge!(
+      :seatbelt_on =&gt; false,
+      :insurance_premium =&gt; 50
+    )
   end
 end</diff>
      <filename>test/factory.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
 
 class MachineByDefaultTest &lt; Test::Unit::TestCase
   def setup
-    @machine = PluginAWeek::StateMachine::Machine.new(Switch, 'state')
+    @machine = PluginAWeek::StateMachine::Machine.new(Switch)
   end
   
   def test_should_have_an_attribute</diff>
      <filename>test/unit/machine_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>09b35aad26bc46766d7a19dbb5e5930155cdec92</id>
    </parent>
  </parents>
  <author>
    <name>Aaron Pfeifer</name>
    <email>aaron.pfeifer@gmail.com</email>
  </author>
  <url>http://github.com/pluginaweek/state_machine/commit/179ce54ff1f6468d44903516674b49829f7a79e4</url>
  <id>179ce54ff1f6468d44903516674b49829f7a79e4</id>
  <committed-date>2008-07-05T19:47:45-07:00</committed-date>
  <authored-date>2008-07-05T19:47:45-07:00</authored-date>
  <message>Add more descriptive exceptions
Assume the default state attribute is &quot;state&quot; if one is not provided
Tag 0.2.1 release</message>
  <tree>fc3345c5dee0f4588e2c00402b7eb7761084e535</tree>
  <committer>
    <name>Aaron Pfeifer</name>
    <email>aaron.pfeifer@gmail.com</email>
  </committer>
</commit>
