finite_machine 0.11.2 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +80 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +679 -624
  5. data/lib/finite_machine.rb +35 -45
  6. data/lib/finite_machine/async_call.rb +5 -21
  7. data/lib/finite_machine/callable.rb +4 -4
  8. data/lib/finite_machine/catchable.rb +24 -14
  9. data/lib/finite_machine/choice_merger.rb +20 -20
  10. data/lib/finite_machine/const.rb +16 -0
  11. data/lib/finite_machine/definition.rb +3 -3
  12. data/lib/finite_machine/dsl.rb +98 -151
  13. data/lib/finite_machine/env.rb +4 -2
  14. data/lib/finite_machine/event_definition.rb +7 -15
  15. data/lib/finite_machine/{events_chain.rb → events_map.rb} +40 -53
  16. data/lib/finite_machine/hook_event.rb +60 -61
  17. data/lib/finite_machine/hooks.rb +44 -36
  18. data/lib/finite_machine/listener.rb +2 -2
  19. data/lib/finite_machine/logger.rb +5 -4
  20. data/lib/finite_machine/{event_queue.rb → message_queue.rb} +76 -32
  21. data/lib/finite_machine/observer.rb +71 -34
  22. data/lib/finite_machine/safety.rb +16 -14
  23. data/lib/finite_machine/state_definition.rb +3 -5
  24. data/lib/finite_machine/state_machine.rb +93 -76
  25. data/lib/finite_machine/state_parser.rb +55 -83
  26. data/lib/finite_machine/subscribers.rb +2 -2
  27. data/lib/finite_machine/threadable.rb +3 -1
  28. data/lib/finite_machine/transition.rb +34 -34
  29. data/lib/finite_machine/transition_builder.rb +23 -32
  30. data/lib/finite_machine/transition_event.rb +12 -11
  31. data/lib/finite_machine/two_phase_lock.rb +8 -6
  32. data/lib/finite_machine/undefined_transition.rb +5 -6
  33. data/lib/finite_machine/version.rb +2 -2
  34. metadata +58 -142
  35. data/.gitignore +0 -18
  36. data/.rspec +0 -5
  37. data/.ruby-gemset +0 -1
  38. data/.ruby-version +0 -1
  39. data/.travis.yml +0 -26
  40. data/Gemfile +0 -15
  41. data/Rakefile +0 -8
  42. data/assets/finite_machine_logo.png +0 -0
  43. data/examples/atm.rb +0 -45
  44. data/examples/bug_system.rb +0 -145
  45. data/finite_machine.gemspec +0 -23
  46. data/lib/finite_machine/async_proxy.rb +0 -30
  47. data/spec/integration/system_spec.rb +0 -95
  48. data/spec/spec_helper.rb +0 -33
  49. data/spec/unit/alias_target_spec.rb +0 -108
  50. data/spec/unit/async_events_spec.rb +0 -138
  51. data/spec/unit/callable/call_spec.rb +0 -113
  52. data/spec/unit/callbacks_spec.rb +0 -942
  53. data/spec/unit/can_spec.rb +0 -98
  54. data/spec/unit/choice_spec.rb +0 -331
  55. data/spec/unit/define_spec.rb +0 -55
  56. data/spec/unit/definition_spec.rb +0 -115
  57. data/spec/unit/event_names_spec.rb +0 -19
  58. data/spec/unit/event_queue_spec.rb +0 -52
  59. data/spec/unit/events_chain/add_spec.rb +0 -25
  60. data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
  61. data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
  62. data/spec/unit/events_chain/clear_spec.rb +0 -15
  63. data/spec/unit/events_chain/events_spec.rb +0 -18
  64. data/spec/unit/events_chain/inspect_spec.rb +0 -24
  65. data/spec/unit/events_chain/match_transition_spec.rb +0 -37
  66. data/spec/unit/events_chain/move_to_spec.rb +0 -48
  67. data/spec/unit/events_chain/states_for_spec.rb +0 -17
  68. data/spec/unit/events_spec.rb +0 -459
  69. data/spec/unit/handlers_spec.rb +0 -152
  70. data/spec/unit/hook_event/build_spec.rb +0 -15
  71. data/spec/unit/hook_event/eql_spec.rb +0 -36
  72. data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
  73. data/spec/unit/hook_event/initialize_spec.rb +0 -25
  74. data/spec/unit/hook_event/notify_spec.rb +0 -14
  75. data/spec/unit/hooks/call_spec.rb +0 -24
  76. data/spec/unit/hooks/clear_spec.rb +0 -16
  77. data/spec/unit/hooks/inspect_spec.rb +0 -17
  78. data/spec/unit/hooks/register_spec.rb +0 -22
  79. data/spec/unit/if_unless_spec.rb +0 -353
  80. data/spec/unit/initial_spec.rb +0 -222
  81. data/spec/unit/inspect_spec.rb +0 -17
  82. data/spec/unit/is_spec.rb +0 -55
  83. data/spec/unit/log_transitions_spec.rb +0 -30
  84. data/spec/unit/logger_spec.rb +0 -38
  85. data/spec/unit/respond_to_spec.rb +0 -38
  86. data/spec/unit/state_parser/inspect_spec.rb +0 -25
  87. data/spec/unit/state_parser/parse_spec.rb +0 -59
  88. data/spec/unit/states_spec.rb +0 -34
  89. data/spec/unit/subscribers_spec.rb +0 -42
  90. data/spec/unit/target_spec.rb +0 -225
  91. data/spec/unit/terminated_spec.rb +0 -95
  92. data/spec/unit/transition/check_conditions_spec.rb +0 -54
  93. data/spec/unit/transition/inspect_spec.rb +0 -25
  94. data/spec/unit/transition/matches_spec.rb +0 -23
  95. data/spec/unit/transition/states_spec.rb +0 -31
  96. data/spec/unit/transition/to_state_spec.rb +0 -27
  97. data/spec/unit/trigger_spec.rb +0 -22
  98. data/spec/unit/undefined_transition/eql_spec.rb +0 -17
  99. data/tasks/console.rake +0 -11
  100. data/tasks/coverage.rake +0 -11
  101. data/tasks/spec.rake +0 -29
@@ -1,92 +1,93 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+
5
+ require_relative "hook_event"
2
6
 
3
7
  module FiniteMachine
4
8
  # A class reponsible for registering callbacks
5
9
  class Hooks
6
- include Threadable
7
-
8
- attr_threadsafe :collection
10
+ attr_reader :hooks_map
9
11
 
10
- # Initialize a collection of hoooks
12
+ # Initialize a hooks_map of hooks
11
13
  #
12
14
  # @example
13
15
  # Hoosk.new(machine)
14
16
  #
15
17
  # @api public
16
18
  def initialize
17
- @collection = Hash.new do |events_hash, event_type|
18
- events_hash[event_type] = Hash.new do |state_hash, name|
19
+ @hooks_map = Concurrent::Map.new do |events_hash, hook_event|
20
+ events_hash[hook_event] = Concurrent::Map.new do |state_hash, name|
19
21
  state_hash[name] = []
20
22
  end
21
23
  end
22
24
  end
23
25
 
24
- # Register callback
26
+ # Finds all hooks for the event type
25
27
  #
26
- # @param [String] event_type
27
- # @param [String] name
28
- # @param [Proc] callback
28
+ # @param [Symbol] name
29
29
  #
30
30
  # @example
31
- # hooks.register HookEvent::Enter, :green do ... end
31
+ # hooks[HookEvent::Enter][:go] # => [-> { }]
32
32
  #
33
- # @example
34
- # hooks.register HookEvent::Before, :any do ... end
35
- #
36
- # @return [Hash]
33
+ # @return [Array[Transition]]
34
+ # the transitions matching event name
37
35
  #
38
36
  # @api public
39
- def register(event_type, name, callback)
40
- collection[event_type][name] << callback
37
+ def find(name)
38
+ @hooks_map[name]
41
39
  end
40
+ alias [] find
42
41
 
43
- # Unregister callback
42
+ # Register callback
44
43
  #
45
- # @param [String] event_type
44
+ # @param [String] hook_event
46
45
  # @param [String] name
47
46
  # @param [Proc] callback
48
47
  #
49
48
  # @example
50
- # hooks.unregister HookEvent::Enter, :green do ... end
49
+ # hooks.register HookEvent::Enter, :green do ... end
50
+ #
51
+ # @example
52
+ # hooks.register HookEvent::Before, any_state do ... end
51
53
  #
52
54
  # @return [Hash]
53
55
  #
54
56
  # @api public
55
- def unregister(event_type, name, callback)
56
- callbacks = collection[event_type][name]
57
- callbacks.delete(callback)
57
+ def register(hook_event, name, callback)
58
+ @hooks_map[hook_event][name] << callback
58
59
  end
59
60
 
60
- # Return all hooks matching event and state
61
+ # Unregister callback
61
62
  #
62
- # @param [String] event_type
63
- # @param [String] event_state
64
- # @param [Event] event
63
+ # @param [String] hook_event
64
+ # @param [String] name
65
+ # @param [Proc] callback
65
66
  #
66
67
  # @example
67
- # hooks.call(HookEvent::Enter, :green, Event.new)
68
+ # hooks.unregister HookEvent::Enter, :green do ... end
68
69
  #
69
70
  # @return [Hash]
70
71
  #
71
72
  # @api public
72
- def call(event_type, event_state, &block)
73
- collection[event_type][event_state].each(&block)
73
+ def unregister(hook_event, name, callback)
74
+ @hooks_map[hook_event][name].delete(callback)
74
75
  end
75
76
 
76
- # Check if collection has any elements
77
+ # Check if hooks_map has any elements
77
78
  #
78
79
  # @return [Boolean]
79
80
  #
80
81
  # @api public
81
82
  def empty?
82
- collection.empty?
83
+ @hooks_map.empty?
83
84
  end
84
85
 
85
86
  # Remove all callbacks
86
87
  #
87
88
  # @api public
88
89
  def clear
89
- collection.clear
90
+ @hooks_map.clear
90
91
  end
91
92
 
92
93
  # String representation
@@ -95,7 +96,14 @@ module FiniteMachine
95
96
  #
96
97
  # @api public
97
98
  def to_s
98
- self.inspect
99
+ hash = {}
100
+ @hooks_map.each_pair do |hook_event, nested_hash|
101
+ hash[hook_event] = {}
102
+ nested_hash.each_pair do |name, callbacks|
103
+ hash[hook_event][name] = callbacks
104
+ end
105
+ end
106
+ hash.to_s
99
107
  end
100
108
 
101
109
  # String representation
@@ -104,7 +112,7 @@ module FiniteMachine
104
112
  #
105
113
  # @api public
106
114
  def inspect
107
- "<##{self.class}:0x#{object_id.to_s(16)} @collection=#{collection.inspect}>"
115
+ "<##{self.class}:0x#{object_id.to_s(16)} @hooks_map=#{self}>"
108
116
  end
109
117
  end # Hooks
110
118
  end # FiniteMachine
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
4
  # A generic listener interface
@@ -24,6 +24,6 @@ module FiniteMachine
24
24
  def call(*args)
25
25
  @on_delivery.call(*args) if @on_delivery
26
26
  end
27
- alias_method :handle_delivery, :call
27
+ alias handle_delivery call
28
28
  end # Listener
29
29
  end # FiniteMachine
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module FiniteMachine
4
4
  module Logger
@@ -21,21 +21,22 @@ module FiniteMachine
21
21
  end
22
22
 
23
23
  def format_error(error)
24
- message = "#{error.class}: #{error.message}\n\t"
24
+ message = ["#{error.class}: #{error.message}\n\t"]
25
25
  if error.backtrace
26
26
  message << "occured at #{error.backtrace.join("\n\t")}"
27
27
  else
28
28
  message << "EMPTY BACKTRACE\n\t"
29
29
  end
30
+ message.join
30
31
  end
31
32
 
32
33
  def report_transition(name, from, to, *args)
33
- message = "Transition: @event=#{name} "
34
+ message = ["Transition: @event=#{name} "]
34
35
  unless args.empty?
35
36
  message << "@with=[#{args.join(',')}] "
36
37
  end
37
38
  message << "#{from} -> #{to}"
38
- info(message)
39
+ info(message.join)
39
40
  end
40
41
  end # Logger
41
42
  end # FiniteMachine
@@ -1,36 +1,55 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- module FiniteMachine
4
- # A class responsible for running asynchronous events
5
- class EventQueue
6
- include Threadable
3
+ require_relative "listener"
4
+ require "thread"
7
5
 
8
- # Initialize an event queue
6
+ module FiniteMachine
7
+ # Allows for storage of asynchronous messages such as events
8
+ # and callbacks.
9
+ #
10
+ # Used internally by {Observer} and {StateMachine}
11
+ #
12
+ # @api private
13
+ class MessageQueue
14
+ # Initialize an event queue in separate thread
9
15
  #
10
16
  # @example
11
- # EventQueue.new
17
+ # MessageQueue.new
12
18
  #
13
19
  # @api public
14
20
  def initialize
21
+ @not_empty = ConditionVariable.new
22
+ @mutex = Mutex.new
15
23
  @queue = Queue.new
16
24
  @dead = false
17
25
  @listeners = []
26
+ @thread = nil
27
+ end
18
28
 
29
+ # Start a new thread with a queue of callback events to run
30
+ #
31
+ # @api private
32
+ def start
33
+ return if running?
34
+
35
+ @mutex.synchronize { spawn_thread }
36
+ end
37
+
38
+ # Spawn new background thread
39
+ #
40
+ # @api private
41
+ def spawn_thread
19
42
  @thread = Thread.new do
43
+ Thread.current.abort_on_exception = true
20
44
  process_events
21
45
  end
22
46
  end
23
47
 
24
- # Retrieve the next event
25
- #
26
- # @return [AsyncCall]
27
- #
28
- # @api private
29
- def next_event
30
- sync_shared { @queue.pop }
48
+ def running?
49
+ !@thread.nil? && alive?
31
50
  end
32
51
 
33
- # Add asynchronous event to the event queue
52
+ # Add asynchronous event to the event queue to process
34
53
  #
35
54
  # @example
36
55
  # event_queue << AsyncCall.build(...)
@@ -41,15 +60,21 @@ module FiniteMachine
41
60
  #
42
61
  # @api public
43
62
  def <<(event)
44
- sync_exclusive { @queue << event }
45
- self
63
+ @mutex.synchronize do
64
+ if @dead
65
+ discard_message(event)
66
+ else
67
+ @queue << event
68
+ @not_empty.signal
69
+ end
70
+ end
46
71
  end
47
72
 
48
73
  # Add listener to the queue to receive messages
49
74
  #
50
75
  # @api public
51
76
  def subscribe(*args, &block)
52
- sync_exclusive do
77
+ @mutex.synchronize do
53
78
  listener = Listener.new(*args)
54
79
  listener.on_delivery(&block)
55
80
  @listeners << listener
@@ -63,7 +88,7 @@ module FiniteMachine
63
88
  #
64
89
  # @api public
65
90
  def empty?
66
- sync_shared { @queue.empty? }
91
+ @mutex.synchronize { @queue.empty? }
67
92
  end
68
93
 
69
94
  # Check if the event queue is alive
@@ -75,7 +100,7 @@ module FiniteMachine
75
100
  #
76
101
  # @api public
77
102
  def alive?
78
- sync_shared { !@dead }
103
+ @mutex.synchronize { !@dead }
79
104
  end
80
105
 
81
106
  # Join the event queue from current thread
@@ -89,6 +114,8 @@ module FiniteMachine
89
114
  #
90
115
  # @api public
91
116
  def join(timeout = nil)
117
+ return unless @thread
118
+
92
119
  timeout.nil? ? @thread.join : @thread.join(timeout)
93
120
  end
94
121
 
@@ -101,16 +128,18 @@ module FiniteMachine
101
128
  #
102
129
  # @api public
103
130
  def shutdown
104
- fail EventQueueDeadError, 'event queue already dead' if @dead
131
+ raise EventQueueDeadError, "event queue already dead" if @dead
105
132
 
106
133
  queue = []
107
- sync_exclusive do
134
+ @mutex.synchronize do
135
+ @dead = true
136
+ @not_empty.broadcast
137
+
108
138
  queue = @queue
109
139
  @queue.clear
110
- @dead = true
111
140
  end
112
141
  while !queue.empty?
113
- Logger.debug "Discarded message: #{queue.pop}"
142
+ discard_message(queue.pop)
114
143
  end
115
144
  true
116
145
  end
@@ -124,20 +153,24 @@ module FiniteMachine
124
153
  #
125
154
  # @api public
126
155
  def size
127
- sync_shared { @queue.size }
156
+ @mutex.synchronize { @queue.size }
157
+ end
158
+
159
+ def inspect
160
+ @mutex.synchronize do
161
+ "#<#{self.class}:#{object_id.to_s(16)} @size=#{size}, @dead=#{@dead}>"
162
+ end
128
163
  end
129
164
 
130
165
  private
131
166
 
132
167
  # Notify consumers about process event
133
168
  #
134
- # @param [FiniteMachine::AsyncCall] event
169
+ # @param [AsyncCall] event
135
170
  #
136
171
  # @api private
137
172
  def notify_listeners(event)
138
- sync_shared do
139
- @listeners.each { |listener| listener.handle_delivery(event) }
140
- end
173
+ @listeners.each { |listener| listener.handle_delivery(event) }
141
174
  end
142
175
 
143
176
  # Process all the events
@@ -147,12 +180,23 @@ module FiniteMachine
147
180
  # @api private
148
181
  def process_events
149
182
  until @dead
150
- event = next_event
151
- notify_listeners(event)
152
- event.dispatch
183
+ @mutex.synchronize do
184
+ while @queue.empty?
185
+ @not_empty.wait(@mutex)
186
+ end
187
+ event = @queue.pop
188
+ break unless event
189
+
190
+ notify_listeners(event)
191
+ event.dispatch
192
+ end
153
193
  end
154
194
  rescue Exception => ex
155
195
  Logger.error "Error while running event: #{Logger.format_error(ex)}"
156
196
  end
197
+
198
+ def discard_message(message)
199
+ Logger.debug "Discarded message: #{message}" if $DEBUG
200
+ end
157
201
  end # EventQueue
158
202
  end # FiniteMachine
@@ -1,26 +1,54 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'finite_machine/hooks'
3
+ require_relative "async_call"
4
+ require_relative "callable"
5
+ require_relative "hook_event"
6
+ require_relative "hooks"
7
+ require_relative "message_queue"
8
+ require_relative "safety"
9
+ require_relative "transition_event"
4
10
 
5
11
  module FiniteMachine
6
12
  # A class responsible for observing state changes
7
- class Observer
8
- include Threadable
13
+ class Observer < GenericDSL
9
14
  include Safety
10
15
 
16
+ # Clean up callback queue
17
+ #
18
+ # @api private
19
+ def self.cleanup_callback_queue
20
+ proc do
21
+ begin
22
+ if callback_queue.alive?
23
+ callback_queue.shutdown
24
+ end
25
+ rescue MessageQueueDeadError
26
+ end
27
+ end
28
+ end
29
+
11
30
  # The current state machine
12
- attr_threadsafe :machine
31
+ attr_reader :machine
13
32
 
14
33
  # The hooks to trigger around the transition lifecycle.
15
- attr_threadsafe :hooks
34
+ attr_reader :hooks
16
35
 
17
36
  # Initialize an Observer
18
37
  #
38
+ # @param [StateMachine] machine
39
+ # reference to the current machine
40
+ #
19
41
  # @api public
20
42
  def initialize(machine)
21
43
  @machine = machine
44
+ @hooks = Hooks.new
45
+
22
46
  @machine.subscribe(self)
23
- @hooks = FiniteMachine::Hooks.new
47
+ ObjectSpace.define_finalizer(self, self.class.cleanup_callback_queue)
48
+ end
49
+
50
+ def callback_queue
51
+ @callback_queue ||= MessageQueue.new
24
52
  end
25
53
 
26
54
  # Evaluate in current context
@@ -43,7 +71,7 @@ module FiniteMachine
43
71
  def on(hook_type, state_or_event_name = nil, async = nil, &callback)
44
72
  sync_exclusive do
45
73
  if state_or_event_name.nil?
46
- state_or_event_name = HookEvent.infer_default_name(hook_type)
74
+ state_or_event_name = HookEvent.any_state_or_event(hook_type)
47
75
  end
48
76
  async = false if async.nil?
49
77
  ensure_valid_callback_name!(hook_type, state_or_event_name)
@@ -118,8 +146,9 @@ module FiniteMachine
118
146
  def emit(event, *data)
119
147
  sync_exclusive do
120
148
  [event.type].each do |hook_type|
121
- [event.name, ANY_STATE, ANY_EVENT].each do |event_name|
122
- hooks.call(hook_type, event_name) do |hook|
149
+ any_state_or_event = HookEvent.any_state_or_event(hook_type)
150
+ [any_state_or_event, event.name].each do |event_name|
151
+ hooks[hook_type][event_name].each do |hook|
123
152
  handle_callback(hook, event, *data)
124
153
  off(hook_type, event_name, &hook) if hook.is_a?(Once)
125
154
  end
@@ -128,26 +157,21 @@ module FiniteMachine
128
157
  end
129
158
  end
130
159
 
131
- private
132
-
133
- # Defer callback execution
160
+ # Cancel the current event
134
161
  #
135
- # @api private
136
- def defer(callable, trans_event, *data)
137
- async_call = AsyncCall.new(machine, callable, trans_event, *data)
138
- machine.event_queue << async_call
139
- end
140
-
141
- # Create callable instance
162
+ # This should be called inside a on_before or on_exit callbacks
163
+ # to prevent event transition.
142
164
  #
143
- # @api private
144
- def create_callable(hook)
145
- callback = proc do |trans_event, *data|
146
- machine.instance_exec(trans_event, *data, &hook)
147
- end
148
- Callable.new(callback)
165
+ # @param [String] msg
166
+ # the message used for failure
167
+ #
168
+ # @api public
169
+ def cancel_event(msg = nil)
170
+ raise CallbackError.new(msg)
149
171
  end
150
172
 
173
+ private
174
+
151
175
  # Handle callback and decide if run synchronously or asynchronously
152
176
  #
153
177
  # @param [Proc] :hook
@@ -160,21 +184,34 @@ module FiniteMachine
160
184
  #
161
185
  # @api private
162
186
  def handle_callback(hook, event, *data)
163
- to = machine.events_chain.move_to(event.event_name, event.from, *data)
164
- trans_event = TransitionEvent.new(event, to)
187
+ to = machine.events_map.move_to(event.event_name, event.from, *data)
188
+ trans_event = TransitionEvent.new(event.event_name, event.from, to)
165
189
  callable = create_callable(hook)
166
190
 
167
191
  if hook.is_a?(Async)
168
192
  defer(callable, trans_event, *data)
169
- result = nil
170
193
  else
171
- result = callable.call(trans_event, *data)
194
+ callable.(trans_event, *data)
172
195
  end
196
+ end
197
+
198
+ # Defer callback execution
199
+ #
200
+ # @api private
201
+ def defer(callable, trans_event, *data)
202
+ async_call = AsyncCall.new(machine, callable, trans_event, *data)
203
+ callback_queue.start unless callback_queue.running?
204
+ callback_queue << async_call
205
+ end
173
206
 
174
- if result == CANCELLED
175
- hooks.clear
176
- machine.events_chain.cancel_transitions(event.event_name)
207
+ # Create callable instance
208
+ #
209
+ # @api private
210
+ def create_callable(hook)
211
+ callback = proc do |trans_event, *data|
212
+ machine.instance_exec(trans_event, *data, &hook)
177
213
  end
214
+ Callable.new(callback)
178
215
  end
179
216
 
180
217
  # Callback names including all states and events
@@ -184,7 +221,7 @@ module FiniteMachine
184
221
  #
185
222
  # @api private
186
223
  def callback_names
187
- machine.states + machine.event_names + [ANY_EVENT, ANY_STATE]
224
+ machine.states + machine.events + [ANY_EVENT, ANY_STATE]
188
225
  end
189
226
 
190
227
  # Forward the message to observer