state_machine 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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