state_machine 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +54 -84
  4. data/Rakefile +1 -1
  5. data/examples/Car_state.png +0 -0
  6. data/examples/Vehicle_state.png +0 -0
  7. data/examples/auto_shop.rb +11 -0
  8. data/examples/car.rb +19 -0
  9. data/examples/traffic_light.rb +9 -0
  10. data/examples/vehicle.rb +35 -0
  11. data/lib/state_machine.rb +65 -52
  12. data/lib/state_machine/assertions.rb +1 -1
  13. data/lib/state_machine/callback.rb +13 -9
  14. data/lib/state_machine/eval_helpers.rb +4 -3
  15. data/lib/state_machine/event.rb +51 -33
  16. data/lib/state_machine/extensions.rb +2 -2
  17. data/lib/state_machine/guard.rb +47 -41
  18. data/lib/state_machine/integrations.rb +67 -0
  19. data/lib/state_machine/integrations/active_record.rb +62 -36
  20. data/lib/state_machine/integrations/active_record/observer.rb +41 -0
  21. data/lib/state_machine/integrations/data_mapper.rb +23 -37
  22. data/lib/state_machine/integrations/data_mapper/observer.rb +23 -9
  23. data/lib/state_machine/integrations/sequel.rb +23 -24
  24. data/lib/state_machine/machine.rb +380 -277
  25. data/lib/state_machine/node_collection.rb +142 -0
  26. data/lib/state_machine/state.rb +114 -69
  27. data/lib/state_machine/state_collection.rb +38 -0
  28. data/lib/state_machine/transition.rb +36 -17
  29. data/test/active_record.log +2940 -85664
  30. data/test/functional/state_machine_test.rb +49 -53
  31. data/test/sequel.log +747 -11990
  32. data/test/unit/assertions_test.rb +2 -1
  33. data/test/unit/callback_test.rb +14 -12
  34. data/test/unit/eval_helpers_test.rb +25 -6
  35. data/test/unit/event_test.rb +144 -124
  36. data/test/unit/guard_test.rb +118 -140
  37. data/test/unit/integrations/active_record_test.rb +102 -68
  38. data/test/unit/integrations/data_mapper_test.rb +48 -37
  39. data/test/unit/integrations/sequel_test.rb +34 -25
  40. data/test/unit/integrations_test.rb +42 -0
  41. data/test/unit/machine_test.rb +460 -531
  42. data/test/unit/node_collection_test.rb +208 -0
  43. data/test/unit/state_collection_test.rb +167 -0
  44. data/test/unit/state_machine_test.rb +1 -1
  45. data/test/unit/state_test.rb +223 -200
  46. data/test/unit/transition_test.rb +81 -46
  47. metadata +17 -3
  48. data/test/data_mapper.log +0 -30860
@@ -0,0 +1,142 @@
1
+ require 'state_machine/assertions'
2
+
3
+ module StateMachine
4
+ # Represents a collection of nodes in a state machine, be it events or states.
5
+ class NodeCollection
6
+ include Enumerable
7
+ include Assertions
8
+
9
+ # Creates a new collection of nodes for the given state machine. By default,
10
+ # the collection is empty.
11
+ #
12
+ # Configuration options:
13
+ # * <tt>:index</tt> - One or more attributes to automatically generate
14
+ # hashed indices for in order to perform quick lookups. Default is to
15
+ # index by the :name attribute
16
+ def initialize(options = {})
17
+ assert_valid_keys(options, :index)
18
+ options = {:index => :name}.merge(options)
19
+
20
+ @nodes = []
21
+ @indices = Array(options[:index]).inject({}) {|indices, attribute| indices[attribute] = {}; indices}
22
+ @default_index = Array(options[:index]).first
23
+ end
24
+
25
+ # Creates a copy of this collection such that modifications don't affect
26
+ # the original collection
27
+ def initialize_copy(orig) #:nodoc:
28
+ super
29
+
30
+ nodes = @nodes
31
+ @nodes = []
32
+ @indices = @indices.inject({}) {|indices, (name, index)| indices[name] = {}; indices}
33
+ nodes.each {|node| self << node.dup}
34
+ end
35
+
36
+ # Changes the current machine associated with the collection. In turn, this
37
+ # will change the state machine associated with each node in the collection.
38
+ def machine=(new_machine)
39
+ each {|node| node.machine = new_machine}
40
+ end
41
+
42
+ # Gets the number of nodes in this collection
43
+ def length
44
+ @nodes.length
45
+ end
46
+
47
+ # Gets the set of unique keys for the given index
48
+ def keys(index_name = @default_index)
49
+ index(index_name).keys
50
+ end
51
+
52
+ # Adds a new node to the collection. By doing so, this will also add it to
53
+ # the configured indices.
54
+ def <<(node)
55
+ @nodes << node
56
+ @indices.each {|attribute, index| index[node.send(attribute)] = node}
57
+ self
58
+ end
59
+
60
+ # Updates the indexed keys for the given node. If the node's attribute
61
+ # has changed since it was added to the collection, the old indexed keys
62
+ # will be replaced with the updated ones.
63
+ def update(node)
64
+ @indices.each do |attribute, index|
65
+ old_key = index.respond_to?(:key) ? index.key(node) : index.index(node)
66
+ new_key = node.send(attribute)
67
+
68
+ # Only replace the key if it's changed
69
+ if old_key != new_key
70
+ index.delete(old_key)
71
+ index[new_key] = node
72
+ end
73
+ end
74
+ end
75
+
76
+ # Calls the block once for each element in self, passing that element as a
77
+ # parameters.
78
+ #
79
+ # states = StateMachine::NodeCollection.new
80
+ # states << StateMachine::State.new(machine, :parked)
81
+ # states << StateMachine::State.new(machine, :idling)
82
+ # states.each {|state| puts state.name, ' -- '}
83
+ #
84
+ # ...produces:
85
+ #
86
+ # parked -- idling --
87
+ def each
88
+ @nodes.each {|node| yield node}
89
+ self
90
+ end
91
+
92
+ # Gets the node at the given index.
93
+ #
94
+ # states = StateMachine::NodeCollection.new
95
+ # states << StateMachine::State.new(machine, :parked)
96
+ # states << StateMachine::State.new(machine, :idling)
97
+ #
98
+ # states.at(0).name # => :parked
99
+ # states.at(1).name # => :idling
100
+ def at(index)
101
+ @nodes[index]
102
+ end
103
+
104
+ # Gets the node indexed by the given key. By default, this will look up the
105
+ # key in the first index configured for the collection. A custom index can
106
+ # be specified like so:
107
+ #
108
+ # collection['parked', :value]
109
+ #
110
+ # The above will look up the "parked" key in a hash indexed by each node's
111
+ # +value+ attribute.
112
+ #
113
+ # If the key cannot be found, then nil will be returned.
114
+ def [](key, index_name = @default_index)
115
+ index(index_name)[key]
116
+ end
117
+
118
+ # Gets the node indexed by the given key. By default, this will look up the
119
+ # key in the first index configured for the collection. A custom index can
120
+ # be specified like so:
121
+ #
122
+ # collection['parked', :value]
123
+ #
124
+ # The above will look up the "parked" key in a hash indexed by each node's
125
+ # +value+ attribute.
126
+ #
127
+ # If the key cannot be found, then an IndexError exception will be raised:
128
+ #
129
+ # collection['invalid', :value] # => IndexError: "invalid" is an invalid value
130
+ def fetch(key, index_name = @default_index)
131
+ self[key, index_name] || raise(ArgumentError, "#{key.inspect} is an invalid #{index_name}")
132
+ end
133
+
134
+ private
135
+ # Gets the given index. If the index does not exist, then an ArgumentError
136
+ # is raised.
137
+ def index(name)
138
+ raise ArgumentError, 'No indices configured' unless @indices.any?
139
+ @indices[name] || raise(ArgumentError, "Invalid index: #{name.inspect}")
140
+ end
141
+ end
142
+ end
@@ -3,54 +3,55 @@ require 'state_machine/assertions'
3
3
  module StateMachine
4
4
  # A state defines a value that an attribute can be in after being transitioned
5
5
  # 0 or more times. States can represent a value of any type in Ruby, though
6
- # the most common type is String.
6
+ # the most common (and default) type is String.
7
7
  #
8
- # In addition to defining the machine's value, states can also define a
8
+ # In addition to defining the machine's value, a state can also define a
9
9
  # behavioral context for an object when that object is in the state. See
10
10
  # StateMachine::Machine#state for more information about how state-driven
11
11
  # behavior can be utilized.
12
12
  class State
13
13
  include Assertions
14
14
 
15
- class << self
16
- # Generates a unique identifier for the given state value. Ids are based
17
- # on the object type:
18
- # * Proc - "lambda#{object_id}"
19
- # * NilClass - "nil"
20
- # * Everything else - Stringified version of the object
21
- def id_for(value)
22
- case value
23
- when Proc
24
- "lambda#{value.object_id.abs}"
25
- when nil
26
- 'nil'
27
- else
28
- value.to_s
29
- end
30
- end
31
- end
32
-
33
15
  # The state machine for which this state is defined
34
16
  attr_accessor :machine
35
17
 
18
+ # The unique identifier for the state used in event and callback definitions
19
+ attr_reader :name
20
+
21
+ # The value that is written to a machine's attribute when an object
22
+ # transitions into this state
23
+ attr_writer :value
24
+
25
+ # Whether or not this state is the initial state to use for new objects
26
+ attr_accessor :initial
27
+
28
+ # A custom lambda block for determining whether a given value matches this
29
+ # state
30
+ attr_accessor :matcher
31
+
36
32
  # Tracks all of the methods that have been defined for the machine's owner
37
33
  # class when objects are in this state.
38
34
  #
39
- # Maps "method name" => UnboundMethod
35
+ # Maps :method_name => UnboundMethod
40
36
  attr_reader :methods
41
37
 
42
- # Whether or not this state in the initial state to use for new objects
43
- attr_accessor :initial
44
-
45
- # Creates a new state within the context of the given machine
38
+ # Creates a new state within the context of the given machine.
46
39
  #
47
40
  # Configuration options:
48
- # * +initial+ - Whether this state is the beginning state for the machine. Default is false.
49
- def initialize(machine, value, options = {}) #:nodoc:
50
- assert_valid_keys(options, :initial)
41
+ # * <tt>:initial</tt> - Whether this state is the beginning state for the
42
+ # machine. Default is false.
43
+ # * <tt>:value</tt> - The value to store when an object transitions to this
44
+ # state. Default is the name (stringified).
45
+ # * <tt>:if</tt> - Determines whether a value matches this state
46
+ # (e.g. :value => lambda {Time.now}, :if => lambda {|state| !state.nil?}).
47
+ # By default, the configured value is matched.
48
+ def initialize(machine, name, options = {}) #:nodoc:
49
+ assert_valid_keys(options, :initial, :value, :if)
51
50
 
52
51
  @machine = machine
53
- @value = value
52
+ @name = name
53
+ @value = options.include?(:value) ? options[:value] : name && name.to_s
54
+ @matcher = options[:if]
54
55
  @methods = {}
55
56
  @initial = options.include?(:initial) && options[:initial]
56
57
 
@@ -61,14 +62,53 @@ module StateMachine
61
62
  # methods to prevent conflicts across different states.
62
63
  def initialize_copy(orig) #:nodoc:
63
64
  super
64
- @methods = @methods.dup
65
+ @methods = methods.dup
65
66
  end
66
67
 
67
- # The value (of any type) that represents this state. If an object is
68
- # passed in and the state's value is a lambda block, then the object will be
69
- # passed into the block to generate the actual value of the state.
70
- def value(object = nil)
71
- @value.is_a?(Proc) && object ? @value.call(object) : @value
68
+ # Generates a human-readable description of this state's name / value:
69
+ #
70
+ # For example,
71
+ #
72
+ # State.new(machine, :parked).description # => "parked"
73
+ # State.new(machine, :parked, :value => :parked).description # => "parked"
74
+ # State.new(machine, :parked, :value => nil).description # => "parked (nil)"
75
+ # State.new(machine, :parked, :value => 1).description # => "parked (1)"
76
+ # State.new(machine, :parked, :value => lambda {Time.now}).description # => "parked (*)
77
+ def description
78
+ description = name ? name.to_s : name.inspect
79
+ description << " (#{@value.is_a?(Proc) ? '*' : @value.inspect})" unless name.to_s == @value.to_s
80
+ description
81
+ end
82
+
83
+ # The value that represents this state. If the value is a lambda block,
84
+ # then it will be evaluated at this time. Otherwise, the static value is
85
+ # returned.
86
+ #
87
+ # For example,
88
+ #
89
+ # State.new(machine, :parked, :value => 1).value # => 1
90
+ # State.new(machine, :parked, :value => lambda {Time.now}).value # => Tue Jan 01 00:00:00 UTC 2008
91
+ def value
92
+ @value.is_a?(Proc) ? @value.call : @value
93
+ end
94
+
95
+ # Determines whether this state matches the given value. If no matcher is
96
+ # configured, then this will check whether the values are equivalent.
97
+ # Otherwise, the matcher will determine the result.
98
+ #
99
+ # For example,
100
+ #
101
+ # # Without a matcher
102
+ # state = State.new(machine, :parked, :value => 1)
103
+ # state.matches?(1) # => true
104
+ # state.matches?(2) # => false
105
+ #
106
+ # # With a matcher
107
+ # state = State.new(machine, :parked, :value => lambda {Time.now}, :if => lambda {|value| !value.nil?})
108
+ # state.matches?(nil) # => false
109
+ # state.matches?(Time.now) # => true
110
+ def matches?(other_value)
111
+ matcher ? matcher.call(other_value) : other_value == value
72
112
  end
73
113
 
74
114
  # Defines a context for the state which will be enabled on instances of the
@@ -77,7 +117,6 @@ module StateMachine
77
117
  # This can be called multiple times. Each time a new context is created, a
78
118
  # new module will be included in the owner class.
79
119
  def context(&block)
80
- value = self.value
81
120
  owner_class = machine.owner_class
82
121
  attribute = machine.attribute
83
122
 
@@ -95,15 +134,14 @@ module StateMachine
95
134
  # not possible with lambdas in Ruby 1.8.6.
96
135
  owner_class.class_eval <<-end_eval, __FILE__, __LINE__
97
136
  def #{method}(*args, &block)
98
- attribute = #{attribute.dump}
99
- self.class.state_machines[attribute].state(send(attribute)).call(self, #{method.dump}, *args, &block)
137
+ self.class.state_machines[#{attribute.inspect}].state_for(self).call(self, #{method.inspect}, *args, &block)
100
138
  end
101
139
  end_eval
102
140
  end
103
141
 
104
142
  # Track the method defined for the context so that it can be invoked
105
143
  # at a later point in time
106
- methods[method] = context.instance_method(method)
144
+ methods[method.to_sym] = context.instance_method(method)
107
145
  end
108
146
 
109
147
  # Include the context so that it can be bound to the owner class (the
@@ -112,7 +150,7 @@ module StateMachine
112
150
  include context
113
151
  end
114
152
 
115
- methods
153
+ context
116
154
  end
117
155
 
118
156
  # Calls a method defined in this state's context on the given object. All
@@ -121,53 +159,60 @@ module StateMachine
121
159
  # If the method has never been defined for this state, then a NoMethodError
122
160
  # will be raised.
123
161
  def call(object, method, *args, &block)
124
- if context_method = methods[method.to_s]
162
+ if context_method = methods[method.to_sym]
125
163
  # Method is defined by the state: proxy it through
126
164
  context_method.bind(object).call(*args, &block)
127
165
  else
128
166
  # Raise exception as if the method never existed on the original object
129
- raise NoMethodError, "undefined method '#{method}' for #{object} in state #{object.send(machine.attribute).inspect}"
167
+ raise NoMethodError, "undefined method '#{method}' for #{object} in state #{machine.state_for(object).name.inspect}"
130
168
  end
131
169
  end
132
170
 
133
171
  # Draws a representation of this state on the given machine. This will
134
172
  # create a new node on the graph with the following properties:
135
- # * +label+ - A human-friendly version of the value. For lambda blocks / procs, "*", otherwise the stringified version of the value is used.
173
+ # * +label+ - The human-friendly description of the state.
136
174
  # * +width+ - The width of the node. Always 1.
137
175
  # * +height+ - The height of the node. Always 1.
138
- # * +fixedsize+ - Whether the size of the node stays the same regardless of its contents. Always true.
139
- # * +shape+ - The actual shape of the node. If the state is the beginning state, then "doublecircle", otherwise "circle".
176
+ # * +fixedsize+ - Whether the size of the node stays the same regardless of
177
+ # its contents. Always true.
178
+ # * +shape+ - The actual shape of the node. If the state is the initial
179
+ # state, then "doublecircle", otherwise "circle".
140
180
  #
141
181
  # The actual node generated on the graph will be returned.
142
182
  def draw(graph)
143
- shape = initial ? 'doublecircle' : 'circle'
144
-
145
- # Use GraphViz-friendly name/label for dynamic/nil states
146
- name = self.class.id_for(value)
147
- if value.is_a?(Proc)
148
- label = '*'
149
- else
150
- label = value.nil? ? 'nil' : value.to_s
151
- end
152
-
153
- graph.add_node(name, :label => label, :width => '1', :height => '1', :fixedsize => 'true', :shape => shape)
183
+ graph.add_node(name ? name.to_s : 'nil',
184
+ :label => description,
185
+ :width => '1',
186
+ :height => '1',
187
+ :fixedsize => 'true',
188
+ :shape => initial ? 'doublecircle' : 'circle'
189
+ )
190
+ end
191
+
192
+ # Generates a nicely formatted description of this state's contents.
193
+ #
194
+ # For example,
195
+ #
196
+ # state = StateMachine::State.new(machine, :parked, :value => 1, :initial => true)
197
+ # state # => #<StateMachine::State name=:parked value=1 initial=true>
198
+ def inspect
199
+ "#<#{self.class} #{%w(name value initial).map {|attr| "#{attr}=#{instance_variable_get("@#{attr}").inspect}"} * ' '}>"
154
200
  end
155
201
 
156
202
  private
157
- # Adds a predicate method to the owner class as long as this state's value
158
- # is a string/symbol
203
+ # Adds a predicate method to the owner class so long as a name has
204
+ # actually been configured for the state
159
205
  def add_predicate
160
- if value && (value.is_a?(String) || value.is_a?(Symbol))
161
- attribute = machine.attribute
162
- value = self.value
163
- name = "#{value}?"
164
- name = "#{machine.namespace}_#{name}" if machine.namespace
165
-
166
- machine.owner_class.class_eval do
167
- # Checks whether the current state is equal to the given value
168
- define_method(name) do
169
- self.send(attribute) == value
170
- end unless method_defined?(name) || private_method_defined?(name)
206
+ return unless name
207
+
208
+ attribute = machine.attribute
209
+ qualified_name = name = self.name
210
+ qualified_name = "#{machine.namespace}_#{name}" if machine.namespace
211
+
212
+ machine.owner_class.class_eval do
213
+ # Checks whether the current value matches this state
214
+ define_method("#{qualified_name}?") do
215
+ self.class.state_machines[attribute].state(name).matches?(send(attribute))
171
216
  end
172
217
  end
173
218
  end
@@ -0,0 +1,38 @@
1
+ require 'state_machine/node_collection'
2
+
3
+ module StateMachine
4
+ # Represents a collection of states in a state machine
5
+ class StateCollection < NodeCollection
6
+ def initialize #:nodoc:
7
+ super(:index => [:name, :value])
8
+ end
9
+
10
+ # Gets the order in which states should be displayed based on where they
11
+ # were first referenced. This will order states in the following priority:
12
+ #
13
+ # 1. Initial state
14
+ # 2. Event transitions (:from, :except_from, :to, :except_to options)
15
+ # 3. States with behaviors
16
+ # 4. States referenced via +state+ or +other_states+
17
+ # 5. States referenced in callbacks
18
+ #
19
+ # This order will determine how the GraphViz visualizations are rendered.
20
+ def by_priority
21
+ if first = @nodes.first
22
+ machine = first.machine
23
+ order = select {|state| state.initial}.map {|state| state.name}
24
+
25
+ machine.events.each {|event| order += event.known_states}
26
+ order += select {|state| state.methods.any?}.map {|state| state.name}
27
+ order += keys(:name) - machine.callbacks.values.flatten.map {|callback| callback.known_states}.flatten
28
+ order += keys(:name)
29
+
30
+ order.uniq!
31
+ order.map! {|name| self[name]}
32
+ order
33
+ else
34
+ []
35
+ end
36
+ end
37
+ end
38
+ end
@@ -4,6 +4,7 @@ module StateMachine
4
4
  end
5
5
 
6
6
  # A transition represents a state change for a specific attribute.
7
+ #
7
8
  # Transitions consist of:
8
9
  # * An event
9
10
  # * A starting state
@@ -18,19 +19,27 @@ module StateMachine
18
19
  # The event that caused the transition
19
20
  attr_reader :event
20
21
 
21
- # The original state *before* the transition
22
+ # The original state value *before* the transition
22
23
  attr_reader :from
23
24
 
24
- # The new state *after* the transition
25
+ # The original state name *before* the transition
26
+ attr_reader :from_name
27
+
28
+ # The new state value *after* the transition
25
29
  attr_reader :to
26
30
 
31
+ # The new state name *after* the transition
32
+ attr_reader :to_name
33
+
27
34
  # Creates a new, specific transition
28
- def initialize(object, machine, event, from, to) #:nodoc:
35
+ def initialize(object, machine, event, from_name, to_name) #:nodoc:
29
36
  @object = object
30
37
  @machine = machine
31
38
  @event = event
32
- @from = from
33
- @to = to
39
+ @from = object.send(machine.attribute)
40
+ @from_name = from_name
41
+ @to = machine.states[to_name].value
42
+ @to_name = to_name
34
43
  end
35
44
 
36
45
  # Gets the attribute which this transition's machine is defined for
@@ -38,14 +47,14 @@ module StateMachine
38
47
  machine.attribute
39
48
  end
40
49
 
41
- # Gets a hash of all the attributes defined for this transition with their
42
- # names as keys and values of the attributes as values.
50
+ # Gets a hash of all the core attributes defined for this transition with
51
+ # their names as keys and values of the attributes as values.
43
52
  #
44
53
  # == Example
45
54
  #
46
- # machine = StateMachine.new(Object, 'state')
47
- # transition = StateMachine::Transition.new(Object.new, machine, 'enable, 'disabled', 'enabled')
48
- # transition.attributes # => {:object => #<Object:0xb7d60ea4>, :attribute => 'state', :event => 'enable', :from => 'disabled', :to => 'enabled'}
55
+ # machine = StateMachine.new(Vehicle)
56
+ # transition = StateMachine::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
57
+ # transition.attributes # => {:object => #<Vehicle:0xb7d60ea4>, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
49
58
  def attributes
50
59
  @attributes ||= {:object => object, :attribute => attribute, :event => event, :from => from, :to => to}
51
60
  end
@@ -63,9 +72,9 @@ module StateMachine
63
72
  # end
64
73
  #
65
74
  # vehicle = Vehicle.new
66
- # transition = StateMachine::Transition.new(vehicle, machine, :turn_on, 'off', 'on')
67
- # transition.perform # => Runs the +save+ action after updating the state attribute
68
- # transition.perform(false) # => Only updates the state attribute
75
+ # transition = StateMachine::Transition.new(vehicle, machine, :ignite, :parked, :idling)
76
+ # transition.perform # => Runs the +save+ action after setting the state attribute
77
+ # transition.perform(false) # => Only sets the state attribute
69
78
  def perform(run_action = true)
70
79
  result = false
71
80
 
@@ -91,17 +100,27 @@ module StateMachine
91
100
  result
92
101
  end
93
102
 
103
+ # Generates a nicely formatted description of this transitions's contents.
104
+ #
105
+ # For example,
106
+ #
107
+ # transition = StateMachine::Transition.new(object, machine, :ignite, :parked, :idling)
108
+ # transition # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
109
+ def inspect
110
+ "#<#{self.class} #{%w(attribute event from from_name to to_name).map {|attr| "#{attr}=#{send(attr).inspect}"} * ' '}>"
111
+ end
112
+
94
113
  protected
95
114
  # Gets a hash of the context defining this unique transition (including
96
115
  # event, from state, and to state).
97
116
  #
98
117
  # == Example
99
118
  #
100
- # machine = StateMachine.new(Object, 'state')
101
- # transition = StateMachine::Transition.new(Object.new, machine, 'enable', 'disabled', 'enabled')
102
- # transition.context # => {:on => "enable", :from => "disabled", :to => "enabled"}
119
+ # machine = StateMachine.new(Vehicle)
120
+ # transition = StateMachine::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
121
+ # transition.context # => {:on => :ignite, :from => :parked, :to => :idling}
103
122
  def context
104
- @context ||= {:on => event, :from => from, :to => to}
123
+ @context ||= {:on => event, :from => from_name, :to => to_name}
105
124
  end
106
125
 
107
126
  # Runs the callbacks of the given type for this transition. This will