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/env.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "threadable"
|
2
4
|
|
3
5
|
module FiniteMachine
|
4
6
|
# Holds references to targets and aliases
|
@@ -11,7 +13,7 @@ module FiniteMachine
|
|
11
13
|
|
12
14
|
attr_threadsafe :aliases
|
13
15
|
|
14
|
-
def initialize(target, aliases)
|
16
|
+
def initialize(target, aliases = [])
|
15
17
|
@target = target
|
16
18
|
@aliases = aliases
|
17
19
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
4
|
# A class responsible for defining event methods on state machine
|
@@ -8,10 +8,8 @@ module FiniteMachine
|
|
8
8
|
#
|
9
9
|
# @api private
|
10
10
|
class EventDefinition
|
11
|
-
include Threadable
|
12
|
-
|
13
11
|
# The current state machine
|
14
|
-
|
12
|
+
attr_reader :machine
|
15
13
|
|
16
14
|
# Initialize an EventDefinition
|
17
15
|
#
|
@@ -19,7 +17,7 @@ module FiniteMachine
|
|
19
17
|
#
|
20
18
|
# @api private
|
21
19
|
def initialize(machine)
|
22
|
-
|
20
|
+
@machine = machine
|
23
21
|
end
|
24
22
|
|
25
23
|
# Define transition event names as state machine events
|
@@ -50,11 +48,8 @@ module FiniteMachine
|
|
50
48
|
# @api private
|
51
49
|
def define_event_transition(event_name, silent)
|
52
50
|
machine.send(:define_singleton_method, event_name) do |*data, &block|
|
53
|
-
|
54
|
-
|
55
|
-
else
|
56
|
-
machine.trigger(event_name, *data, &block)
|
57
|
-
end
|
51
|
+
method = silent ? :transition : :trigger
|
52
|
+
machine.public_send(method, event_name, *data, &block)
|
58
53
|
end
|
59
54
|
end
|
60
55
|
|
@@ -71,11 +66,8 @@ module FiniteMachine
|
|
71
66
|
# @api private
|
72
67
|
def define_event_bang(event_name, silent)
|
73
68
|
machine.send(:define_singleton_method, "#{event_name}!") do |*data, &block|
|
74
|
-
|
75
|
-
|
76
|
-
else
|
77
|
-
machine.trigger!(event_name, *data, &block)
|
78
|
-
end
|
69
|
+
method = silent ? :transition! : :trigger!
|
70
|
+
machine.public_send(method, event_name, *data, &block)
|
79
71
|
end
|
80
72
|
end
|
81
73
|
end # EventBuilder
|
@@ -1,35 +1,34 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "concurrent/map"
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
require_relative "threadable"
|
7
|
+
require_relative "undefined_transition"
|
4
8
|
|
5
9
|
module FiniteMachine
|
6
|
-
# A class responsible for storing
|
10
|
+
# A class responsible for storing mappings between event namess and
|
11
|
+
# their transition objects.
|
7
12
|
#
|
8
13
|
# Used internally by {StateMachine}.
|
9
14
|
#
|
10
15
|
# @api private
|
11
|
-
class
|
12
|
-
include Threadable
|
16
|
+
class EventsMap
|
13
17
|
extend Forwardable
|
14
18
|
|
15
|
-
|
16
|
-
#
|
17
|
-
# @api private
|
18
|
-
attr_threadsafe :chain
|
19
|
-
|
20
|
-
def_delegators :@chain, :empty?, :size
|
19
|
+
def_delegators :@events_map, :empty?, :size
|
21
20
|
|
22
|
-
# Initialize a
|
21
|
+
# Initialize a EventsMap
|
23
22
|
#
|
24
23
|
# @api private
|
25
24
|
def initialize
|
26
|
-
@
|
25
|
+
@events_map = Concurrent::Map.new
|
27
26
|
end
|
28
27
|
|
29
28
|
# Check if event is present
|
30
29
|
#
|
31
30
|
# @example
|
32
|
-
#
|
31
|
+
# events_map.exists?(:go) # => true
|
33
32
|
#
|
34
33
|
# @param [Symbol] name
|
35
34
|
# the event name
|
@@ -39,7 +38,7 @@ module FiniteMachine
|
|
39
38
|
#
|
40
39
|
# @api public
|
41
40
|
def exists?(name)
|
42
|
-
|
41
|
+
@events_map.key?(name)
|
43
42
|
end
|
44
43
|
|
45
44
|
# Add transition under name
|
@@ -54,9 +53,9 @@ module FiniteMachine
|
|
54
53
|
# @api public
|
55
54
|
def add(name, transition)
|
56
55
|
if exists?(name)
|
57
|
-
|
56
|
+
@events_map[name] << transition
|
58
57
|
else
|
59
|
-
|
58
|
+
@events_map[name] = [transition]
|
60
59
|
end
|
61
60
|
self
|
62
61
|
end
|
@@ -66,41 +65,41 @@ module FiniteMachine
|
|
66
65
|
# @param [Symbol] name
|
67
66
|
#
|
68
67
|
# @example
|
69
|
-
#
|
68
|
+
# events_map[:start] # => []
|
70
69
|
#
|
71
70
|
# @return [Array[Transition]]
|
72
71
|
# the transitions matching event name
|
73
72
|
#
|
74
73
|
# @api public
|
75
74
|
def find(name)
|
76
|
-
|
75
|
+
@events_map.fetch(name) { [] }
|
77
76
|
end
|
78
|
-
|
77
|
+
alias [] find
|
79
78
|
|
80
79
|
# Retrieve all event names
|
81
80
|
#
|
82
81
|
# @example
|
83
|
-
#
|
82
|
+
# events_map.events # => [:init, :start, :stop]
|
84
83
|
#
|
85
84
|
# @return [Array[Symbol]]
|
86
85
|
# All event names
|
87
86
|
#
|
88
87
|
# @api public
|
89
88
|
def events
|
90
|
-
|
89
|
+
@events_map.keys
|
91
90
|
end
|
92
91
|
|
93
92
|
# Retreive all unique states
|
94
93
|
#
|
95
94
|
# @example
|
96
|
-
#
|
95
|
+
# events_map.states # => [:yellow, :green, :red]
|
97
96
|
#
|
98
97
|
# @return [Array[Symbol]]
|
99
98
|
# the array of all unique states
|
100
99
|
#
|
101
100
|
# @api public
|
102
101
|
def states
|
103
|
-
|
102
|
+
@events_map.values.flatten.map(&:states).map(&:to_a).flatten.uniq
|
104
103
|
end
|
105
104
|
|
106
105
|
# Retrieves all state transitions
|
@@ -109,7 +108,7 @@ module FiniteMachine
|
|
109
108
|
#
|
110
109
|
# @api public
|
111
110
|
def state_transitions
|
112
|
-
|
111
|
+
@events_map.values.flatten.map(&:states)
|
113
112
|
end
|
114
113
|
|
115
114
|
# Retrieve from states for the event name
|
@@ -117,7 +116,7 @@ module FiniteMachine
|
|
117
116
|
# @param [Symbol] event_name
|
118
117
|
#
|
119
118
|
# @example
|
120
|
-
#
|
119
|
+
# events_map.states_for(:start) # => [:yellow, :green]
|
121
120
|
#
|
122
121
|
# @api public
|
123
122
|
def states_for(name)
|
@@ -136,7 +135,7 @@ module FiniteMachine
|
|
136
135
|
# Check if event has branching choice transitions or not
|
137
136
|
#
|
138
137
|
# @example
|
139
|
-
#
|
138
|
+
# events_map.choice_transition?(:go, :green) # => true
|
140
139
|
#
|
141
140
|
# @param [Symbol] name
|
142
141
|
# the event name
|
@@ -183,8 +182,7 @@ module FiniteMachine
|
|
183
182
|
# @api public
|
184
183
|
def match_transition_with(name, from_state, *conditions)
|
185
184
|
find(name).find do |trans|
|
186
|
-
trans.matches?(from_state) &&
|
187
|
-
trans.check_conditions(*conditions)
|
185
|
+
trans.matches?(from_state) && trans.check_conditions(*conditions)
|
188
186
|
end
|
189
187
|
end
|
190
188
|
|
@@ -211,7 +209,7 @@ module FiniteMachine
|
|
211
209
|
# Find state that this machine can move to
|
212
210
|
#
|
213
211
|
# @example
|
214
|
-
#
|
212
|
+
# evenst_map.move_to(:go, :green) # => :red
|
215
213
|
#
|
216
214
|
# @param [Symbol] name
|
217
215
|
# the event name
|
@@ -229,53 +227,42 @@ module FiniteMachine
|
|
229
227
|
def move_to(name, from_state, *conditions)
|
230
228
|
transition = select_transition(name, from_state, *conditions)
|
231
229
|
transition ||= UndefinedTransition.new(name)
|
232
|
-
|
233
230
|
transition.to_state(from_state)
|
234
231
|
end
|
235
232
|
|
236
|
-
#
|
237
|
-
#
|
238
|
-
# @param [Symbol] name
|
239
|
-
# the event name
|
240
|
-
#
|
241
|
-
# @return [nil]
|
242
|
-
#
|
243
|
-
# @api public
|
244
|
-
def cancel_transitions(name)
|
245
|
-
chain[name].each do |trans|
|
246
|
-
trans.cancelled = true
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
# Reset chain
|
233
|
+
# Reset map
|
251
234
|
#
|
252
235
|
# @return [self]
|
253
236
|
#
|
254
237
|
# @api public
|
255
238
|
def clear
|
256
|
-
@
|
239
|
+
@events_map.clear
|
257
240
|
self
|
258
241
|
end
|
259
242
|
|
260
|
-
# Return string representation of this
|
243
|
+
# Return string representation of this map
|
261
244
|
#
|
262
245
|
# @return [String]
|
263
246
|
#
|
264
247
|
# @api public
|
265
248
|
def to_s
|
266
|
-
|
249
|
+
hash = {}
|
250
|
+
@events_map.each_pair do |name, trans|
|
251
|
+
hash[name] = trans
|
252
|
+
end
|
253
|
+
hash.to_s
|
267
254
|
end
|
268
255
|
|
269
|
-
# Inspect
|
256
|
+
# Inspect map content
|
270
257
|
#
|
271
258
|
# @example
|
272
|
-
#
|
259
|
+
# events_map.inspect
|
273
260
|
#
|
274
261
|
# @return [String]
|
275
262
|
#
|
276
263
|
# @api public
|
277
264
|
def inspect
|
278
|
-
"<##{self.class} @
|
265
|
+
"<##{self.class} @events_map=#{self}>"
|
279
266
|
end
|
280
|
-
end #
|
267
|
+
end # EventsMap
|
281
268
|
end # FiniteMachine
|
@@ -1,9 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
4
|
# A class responsible for event notification
|
5
5
|
class HookEvent
|
6
|
-
include Threadable
|
7
6
|
include Comparable
|
8
7
|
|
9
8
|
class Anystate < HookEvent; end
|
@@ -24,38 +23,34 @@ module FiniteMachine
|
|
24
23
|
|
25
24
|
MESSAGE = :emit
|
26
25
|
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
# The event name triggering this hook event
|
37
|
-
attr_threadsafe :event_name
|
26
|
+
# Extract event name
|
27
|
+
#
|
28
|
+
# @return [String] the event name
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def self.event_name
|
32
|
+
name.split("::").last.downcase.to_sym
|
33
|
+
end
|
38
34
|
|
39
|
-
#
|
35
|
+
# String representation
|
40
36
|
#
|
41
|
-
# @
|
42
|
-
# The action or state name
|
37
|
+
# @return [String] the event name
|
43
38
|
#
|
44
|
-
# @
|
45
|
-
|
39
|
+
# @api public
|
40
|
+
def self.to_s
|
41
|
+
event_name.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
# Choose any state or event name based on even type
|
46
45
|
#
|
47
|
-
# @
|
48
|
-
# HookEvent.new(:green, :move, :green)
|
46
|
+
# @param [HookEvent] event_type
|
49
47
|
#
|
50
|
-
# @return [
|
48
|
+
# @return [Symbol]
|
49
|
+
# out of :any or :any_event
|
51
50
|
#
|
52
51
|
# @api public
|
53
|
-
def
|
54
|
-
|
55
|
-
@type = self.class
|
56
|
-
@event_name = event_name
|
57
|
-
@from = from
|
58
|
-
freeze
|
52
|
+
def self.any_state_or_event(event_type)
|
53
|
+
event_type < Anyaction ? ANY_EVENT : ANY_STATE
|
59
54
|
end
|
60
55
|
|
61
56
|
# Build event hook
|
@@ -74,16 +69,44 @@ module FiniteMachine
|
|
74
69
|
new(state_or_action, event_name, from)
|
75
70
|
end
|
76
71
|
|
77
|
-
|
72
|
+
EVENTS.each do |event|
|
73
|
+
(class << self; self; end).class_eval do
|
74
|
+
define_method(event.event_name) { event }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# HookEvent state or action name
|
79
|
+
attr_reader :name
|
80
|
+
|
81
|
+
# HookEvent type
|
82
|
+
attr_reader :type
|
83
|
+
|
84
|
+
# The from state this hook has been fired
|
85
|
+
attr_reader :from
|
86
|
+
|
87
|
+
# The event name triggering this hook event
|
88
|
+
attr_reader :event_name
|
89
|
+
|
90
|
+
# Instantiate a new HookEvent object
|
78
91
|
#
|
79
|
-
# @param [
|
92
|
+
# @param [Symbol] name
|
93
|
+
# The action or state name
|
80
94
|
#
|
81
|
-
# @
|
82
|
-
#
|
95
|
+
# @param [Symbol] event_name
|
96
|
+
# The event name associated with this hook event.
|
97
|
+
#
|
98
|
+
# @example
|
99
|
+
# HookEvent.new(:green, :move, :green)
|
100
|
+
#
|
101
|
+
# @return [self]
|
83
102
|
#
|
84
103
|
# @api public
|
85
|
-
def
|
86
|
-
|
104
|
+
def initialize(name, event_name, from)
|
105
|
+
@name = name
|
106
|
+
@type = self.class
|
107
|
+
@event_name = event_name
|
108
|
+
@from = from
|
109
|
+
freeze
|
87
110
|
end
|
88
111
|
|
89
112
|
# Notify subscriber about this event
|
@@ -98,27 +121,9 @@ module FiniteMachine
|
|
98
121
|
#
|
99
122
|
# @api public
|
100
123
|
def notify(subscriber, *data)
|
101
|
-
|
102
|
-
subscriber.public_send(MESSAGE, self, *data)
|
103
|
-
end
|
104
|
-
end
|
124
|
+
return unless subscriber.respond_to?(MESSAGE)
|
105
125
|
|
106
|
-
|
107
|
-
#
|
108
|
-
# @return [String] the event name
|
109
|
-
#
|
110
|
-
# @api public
|
111
|
-
def self.event_name
|
112
|
-
name.split('::').last.downcase.to_sym
|
113
|
-
end
|
114
|
-
|
115
|
-
# String representation
|
116
|
-
#
|
117
|
-
# @return [String] the event name
|
118
|
-
#
|
119
|
-
# @api public
|
120
|
-
def self.to_s
|
121
|
-
event_name.to_s
|
126
|
+
subscriber.public_send(MESSAGE, self, *data)
|
122
127
|
end
|
123
128
|
|
124
129
|
# Compare whether the instance is greater, less then or equal to other
|
@@ -128,14 +133,8 @@ module FiniteMachine
|
|
128
133
|
# @api public
|
129
134
|
def <=>(other)
|
130
135
|
other.is_a?(type) &&
|
131
|
-
|
132
|
-
end
|
133
|
-
alias_method :eql?, :==
|
134
|
-
|
135
|
-
EVENTS.each do |event|
|
136
|
-
(class << self; self; end).class_eval do
|
137
|
-
define_method(event.event_name) { event }
|
138
|
-
end
|
136
|
+
[name, from, event_name] <=> [other.name, other.from, other.event_name]
|
139
137
|
end
|
138
|
+
alias eql? ==
|
140
139
|
end # HookEvent
|
141
140
|
end # FiniteMachine
|