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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +80 -0
- data/LICENSE.txt +1 -1
- data/README.md +679 -624
- data/lib/finite_machine.rb +35 -45
- data/lib/finite_machine/async_call.rb +5 -21
- data/lib/finite_machine/callable.rb +4 -4
- data/lib/finite_machine/catchable.rb +24 -14
- data/lib/finite_machine/choice_merger.rb +20 -20
- data/lib/finite_machine/const.rb +16 -0
- data/lib/finite_machine/definition.rb +3 -3
- data/lib/finite_machine/dsl.rb +98 -151
- data/lib/finite_machine/env.rb +4 -2
- data/lib/finite_machine/event_definition.rb +7 -15
- data/lib/finite_machine/{events_chain.rb → events_map.rb} +40 -53
- data/lib/finite_machine/hook_event.rb +60 -61
- data/lib/finite_machine/hooks.rb +44 -36
- data/lib/finite_machine/listener.rb +2 -2
- data/lib/finite_machine/logger.rb +5 -4
- data/lib/finite_machine/{event_queue.rb → message_queue.rb} +76 -32
- data/lib/finite_machine/observer.rb +71 -34
- data/lib/finite_machine/safety.rb +16 -14
- data/lib/finite_machine/state_definition.rb +3 -5
- data/lib/finite_machine/state_machine.rb +93 -76
- data/lib/finite_machine/state_parser.rb +55 -83
- data/lib/finite_machine/subscribers.rb +2 -2
- data/lib/finite_machine/threadable.rb +3 -1
- data/lib/finite_machine/transition.rb +34 -34
- data/lib/finite_machine/transition_builder.rb +23 -32
- data/lib/finite_machine/transition_event.rb +12 -11
- data/lib/finite_machine/two_phase_lock.rb +8 -6
- data/lib/finite_machine/undefined_transition.rb +5 -6
- data/lib/finite_machine/version.rb +2 -2
- metadata +58 -142
- data/.gitignore +0 -18
- data/.rspec +0 -5
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -26
- data/Gemfile +0 -15
- data/Rakefile +0 -8
- data/assets/finite_machine_logo.png +0 -0
- data/examples/atm.rb +0 -45
- data/examples/bug_system.rb +0 -145
- data/finite_machine.gemspec +0 -23
- data/lib/finite_machine/async_proxy.rb +0 -30
- data/spec/integration/system_spec.rb +0 -95
- data/spec/spec_helper.rb +0 -33
- data/spec/unit/alias_target_spec.rb +0 -108
- data/spec/unit/async_events_spec.rb +0 -138
- data/spec/unit/callable/call_spec.rb +0 -113
- data/spec/unit/callbacks_spec.rb +0 -942
- data/spec/unit/can_spec.rb +0 -98
- data/spec/unit/choice_spec.rb +0 -331
- data/spec/unit/define_spec.rb +0 -55
- data/spec/unit/definition_spec.rb +0 -115
- data/spec/unit/event_names_spec.rb +0 -19
- data/spec/unit/event_queue_spec.rb +0 -52
- data/spec/unit/events_chain/add_spec.rb +0 -25
- data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
- data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
- data/spec/unit/events_chain/clear_spec.rb +0 -15
- data/spec/unit/events_chain/events_spec.rb +0 -18
- data/spec/unit/events_chain/inspect_spec.rb +0 -24
- data/spec/unit/events_chain/match_transition_spec.rb +0 -37
- data/spec/unit/events_chain/move_to_spec.rb +0 -48
- data/spec/unit/events_chain/states_for_spec.rb +0 -17
- data/spec/unit/events_spec.rb +0 -459
- data/spec/unit/handlers_spec.rb +0 -152
- data/spec/unit/hook_event/build_spec.rb +0 -15
- data/spec/unit/hook_event/eql_spec.rb +0 -36
- data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
- data/spec/unit/hook_event/initialize_spec.rb +0 -25
- data/spec/unit/hook_event/notify_spec.rb +0 -14
- data/spec/unit/hooks/call_spec.rb +0 -24
- data/spec/unit/hooks/clear_spec.rb +0 -16
- data/spec/unit/hooks/inspect_spec.rb +0 -17
- data/spec/unit/hooks/register_spec.rb +0 -22
- data/spec/unit/if_unless_spec.rb +0 -353
- data/spec/unit/initial_spec.rb +0 -222
- data/spec/unit/inspect_spec.rb +0 -17
- data/spec/unit/is_spec.rb +0 -55
- data/spec/unit/log_transitions_spec.rb +0 -30
- data/spec/unit/logger_spec.rb +0 -38
- data/spec/unit/respond_to_spec.rb +0 -38
- data/spec/unit/state_parser/inspect_spec.rb +0 -25
- data/spec/unit/state_parser/parse_spec.rb +0 -59
- data/spec/unit/states_spec.rb +0 -34
- data/spec/unit/subscribers_spec.rb +0 -42
- data/spec/unit/target_spec.rb +0 -225
- data/spec/unit/terminated_spec.rb +0 -95
- data/spec/unit/transition/check_conditions_spec.rb +0 -54
- data/spec/unit/transition/inspect_spec.rb +0 -25
- data/spec/unit/transition/matches_spec.rb +0 -23
- data/spec/unit/transition/states_spec.rb +0 -31
- data/spec/unit/transition/to_state_spec.rb +0 -27
- data/spec/unit/trigger_spec.rb +0 -22
- data/spec/unit/undefined_transition/eql_spec.rb +0 -17
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
data/lib/finite_machine/hooks.rb
CHANGED
@@ -1,92 +1,93 @@
|
|
1
|
-
#
|
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
|
-
|
7
|
-
|
8
|
-
attr_threadsafe :collection
|
10
|
+
attr_reader :hooks_map
|
9
11
|
|
10
|
-
# Initialize a
|
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
|
-
@
|
18
|
-
events_hash[
|
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
|
-
#
|
26
|
+
# Finds all hooks for the event type
|
25
27
|
#
|
26
|
-
# @param [
|
27
|
-
# @param [String] name
|
28
|
-
# @param [Proc] callback
|
28
|
+
# @param [Symbol] name
|
29
29
|
#
|
30
30
|
# @example
|
31
|
-
# hooks
|
31
|
+
# hooks[HookEvent::Enter][:go] # => [-> { }]
|
32
32
|
#
|
33
|
-
# @
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# @return [Hash]
|
33
|
+
# @return [Array[Transition]]
|
34
|
+
# the transitions matching event name
|
37
35
|
#
|
38
36
|
# @api public
|
39
|
-
def
|
40
|
-
|
37
|
+
def find(name)
|
38
|
+
@hooks_map[name]
|
41
39
|
end
|
40
|
+
alias [] find
|
42
41
|
|
43
|
-
#
|
42
|
+
# Register callback
|
44
43
|
#
|
45
|
-
# @param [String]
|
44
|
+
# @param [String] hook_event
|
46
45
|
# @param [String] name
|
47
46
|
# @param [Proc] callback
|
48
47
|
#
|
49
48
|
# @example
|
50
|
-
# hooks.
|
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
|
56
|
-
|
57
|
-
callbacks.delete(callback)
|
57
|
+
def register(hook_event, name, callback)
|
58
|
+
@hooks_map[hook_event][name] << callback
|
58
59
|
end
|
59
60
|
|
60
|
-
#
|
61
|
+
# Unregister callback
|
61
62
|
#
|
62
|
-
# @param [String]
|
63
|
-
# @param [String]
|
64
|
-
# @param [
|
63
|
+
# @param [String] hook_event
|
64
|
+
# @param [String] name
|
65
|
+
# @param [Proc] callback
|
65
66
|
#
|
66
67
|
# @example
|
67
|
-
# hooks.
|
68
|
+
# hooks.unregister HookEvent::Enter, :green do ... end
|
68
69
|
#
|
69
70
|
# @return [Hash]
|
70
71
|
#
|
71
72
|
# @api public
|
72
|
-
def
|
73
|
-
|
73
|
+
def unregister(hook_event, name, callback)
|
74
|
+
@hooks_map[hook_event][name].delete(callback)
|
74
75
|
end
|
75
76
|
|
76
|
-
# Check if
|
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
|
-
|
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
|
-
|
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
|
-
|
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)} @
|
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
|
-
#
|
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
|
-
|
27
|
+
alias handle_delivery call
|
28
28
|
end # Listener
|
29
29
|
end # FiniteMachine
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
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
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
class EventQueue
|
6
|
-
include Threadable
|
3
|
+
require_relative "listener"
|
4
|
+
require "thread"
|
7
5
|
|
8
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
131
|
+
raise EventQueueDeadError, "event queue already dead" if @dead
|
105
132
|
|
106
133
|
queue = []
|
107
|
-
|
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
|
-
|
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
|
-
|
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 [
|
169
|
+
# @param [AsyncCall] event
|
135
170
|
#
|
136
171
|
# @api private
|
137
172
|
def notify_listeners(event)
|
138
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
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
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
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
|
-
|
31
|
+
attr_reader :machine
|
13
32
|
|
14
33
|
# The hooks to trigger around the transition lifecycle.
|
15
|
-
|
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
|
-
|
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.
|
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
|
-
|
122
|
-
|
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
|
-
|
132
|
-
|
133
|
-
# Defer callback execution
|
160
|
+
# Cancel the current event
|
134
161
|
#
|
135
|
-
#
|
136
|
-
|
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
|
-
# @
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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.
|
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
|
-
|
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
|
-
|
175
|
-
|
176
|
-
|
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.
|
224
|
+
machine.states + machine.events + [ANY_EVENT, ANY_STATE]
|
188
225
|
end
|
189
226
|
|
190
227
|
# Forward the message to observer
|