finite_machine 0.11.2 → 0.14.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.
- 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
|