finite_machine 0.10.2 → 0.11.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/README.md +73 -35
- data/assets/finite_machine_logo.png +0 -0
- data/lib/finite_machine.rb +0 -7
- data/lib/finite_machine/async_proxy.rb +1 -2
- data/lib/finite_machine/dsl.rb +13 -14
- data/lib/finite_machine/event_definition.rb +32 -35
- data/lib/finite_machine/events_chain.rb +183 -37
- data/lib/finite_machine/hook_event.rb +47 -42
- data/lib/finite_machine/logger.rb +3 -4
- data/lib/finite_machine/observer.rb +27 -11
- data/lib/finite_machine/state_definition.rb +66 -0
- data/lib/finite_machine/state_machine.rb +177 -99
- data/lib/finite_machine/subscribers.rb +17 -6
- data/lib/finite_machine/thread_context.rb +1 -1
- data/lib/finite_machine/transition.rb +45 -173
- data/lib/finite_machine/transition_builder.rb +24 -6
- data/lib/finite_machine/transition_event.rb +5 -4
- data/lib/finite_machine/undefined_transition.rb +32 -0
- data/lib/finite_machine/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/async_events_spec.rb +24 -18
- data/spec/unit/callbacks_spec.rb +0 -19
- data/spec/unit/event_names_spec.rb +19 -0
- data/spec/unit/events_chain/add_spec.rb +25 -0
- data/spec/unit/events_chain/cancel_transitions_spec.rb +22 -0
- data/spec/unit/events_chain/choice_transition_spec.rb +28 -0
- data/spec/unit/events_chain/clear_spec.rb +7 -18
- data/spec/unit/events_chain/events_spec.rb +18 -0
- data/spec/unit/events_chain/inspect_spec.rb +14 -17
- data/spec/unit/events_chain/match_transition_spec.rb +37 -0
- data/spec/unit/events_chain/move_to_spec.rb +48 -0
- data/spec/unit/events_chain/states_for_spec.rb +17 -0
- data/spec/unit/events_spec.rb +119 -27
- data/spec/unit/hook_event/build_spec.rb +15 -0
- data/spec/unit/hook_event/eql_spec.rb +3 -4
- data/spec/unit/hook_event/initialize_spec.rb +14 -11
- data/spec/unit/hook_event/notify_spec.rb +14 -0
- data/spec/unit/{initialize_spec.rb → initial_spec.rb} +1 -1
- data/spec/unit/inspect_spec.rb +1 -1
- data/spec/unit/logger_spec.rb +4 -5
- data/spec/unit/subscribers_spec.rb +20 -9
- data/spec/unit/transition/check_conditions_spec.rb +54 -0
- data/spec/unit/transition/inspect_spec.rb +2 -2
- data/spec/unit/transition/matches_spec.rb +23 -0
- data/spec/unit/transition/states_spec.rb +31 -0
- data/spec/unit/transition/to_state_spec.rb +27 -0
- data/spec/unit/trigger_spec.rb +22 -0
- data/spec/unit/undefined_transition/eql_spec.rb +17 -0
- data/tasks/console.rake +1 -0
- metadata +39 -23
- data/lib/finite_machine/event.rb +0 -146
- data/spec/unit/event/add_spec.rb +0 -16
- data/spec/unit/event/eql_spec.rb +0 -37
- data/spec/unit/event/initialize_spec.rb +0 -38
- data/spec/unit/event/inspect_spec.rb +0 -21
- data/spec/unit/event/next_transition_spec.rb +0 -35
- data/spec/unit/events_chain/check_choice_conditions_spec.rb +0 -20
- data/spec/unit/events_chain/insert_spec.rb +0 -26
- data/spec/unit/events_chain/select_transition_spec.rb +0 -23
- data/spec/unit/transition/parse_states_spec.rb +0 -42
data/lib/finite_machine/event.rb
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module FiniteMachine
|
4
|
-
# A class representing event with transitions
|
5
|
-
#
|
6
|
-
# Used by {EventDefinition} to create events.
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
class Event
|
10
|
-
include Comparable
|
11
|
-
include Threadable
|
12
|
-
|
13
|
-
# The name of this event
|
14
|
-
#
|
15
|
-
# @return [Symbol]
|
16
|
-
attr_threadsafe :name
|
17
|
-
|
18
|
-
# The state transitions for this event
|
19
|
-
#
|
20
|
-
# @return [Array[Transition]]
|
21
|
-
attr_threadsafe :state_transitions
|
22
|
-
|
23
|
-
# The reference to the state machine for this event
|
24
|
-
#
|
25
|
-
# @return [StateMachine]
|
26
|
-
attr_threadsafe :machine
|
27
|
-
|
28
|
-
# The silent option for this transition
|
29
|
-
#
|
30
|
-
# @return [Boolean]
|
31
|
-
attr_threadsafe :silent
|
32
|
-
|
33
|
-
# Initialize an Event
|
34
|
-
#
|
35
|
-
# @api private
|
36
|
-
def initialize(machine, attrs = {})
|
37
|
-
@machine = machine
|
38
|
-
@name = attrs.fetch(:name, DEFAULT_STATE)
|
39
|
-
@silent = attrs.fetch(:silent, false)
|
40
|
-
@state_transitions = []
|
41
|
-
# TODO: add event conditions
|
42
|
-
freeze
|
43
|
-
end
|
44
|
-
|
45
|
-
protected :machine
|
46
|
-
|
47
|
-
# Add transition for this event
|
48
|
-
#
|
49
|
-
# @param [FiniteMachine::Transition] transition
|
50
|
-
#
|
51
|
-
# @example
|
52
|
-
# event << FiniteMachine::Transition.new machine, :a => :b
|
53
|
-
#
|
54
|
-
# @return [Event]
|
55
|
-
#
|
56
|
-
# @api public
|
57
|
-
def <<(transition)
|
58
|
-
sync_exclusive do
|
59
|
-
Array(transition).flatten.each { |trans| state_transitions << trans }
|
60
|
-
end
|
61
|
-
self
|
62
|
-
end
|
63
|
-
alias_method :add, :<<
|
64
|
-
|
65
|
-
# Find next transition
|
66
|
-
#
|
67
|
-
# @return [Transition]
|
68
|
-
# the next available transition
|
69
|
-
#
|
70
|
-
# @api private
|
71
|
-
def next_transition
|
72
|
-
sync_shared do
|
73
|
-
state_transitions.find { |transition| transition.current? } ||
|
74
|
-
state_transitions.first
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# Find transition matching conditions
|
79
|
-
#
|
80
|
-
# @param [Array[Object]] args
|
81
|
-
#
|
82
|
-
# return [Transition]
|
83
|
-
#
|
84
|
-
# @api private
|
85
|
-
def find_transition(*args)
|
86
|
-
sync_shared do
|
87
|
-
state_transitions.find do |trans|
|
88
|
-
trans.current? && trans.check_conditions(*args)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# Trigger this event
|
94
|
-
#
|
95
|
-
# If silent option is passed the event will not fire any callbacks
|
96
|
-
#
|
97
|
-
# @example
|
98
|
-
# transition = Event.new(machine, {})
|
99
|
-
# transition.trigger
|
100
|
-
#
|
101
|
-
# @return [Boolean]
|
102
|
-
# true is transition succeeded, false otherwise
|
103
|
-
#
|
104
|
-
# @api public
|
105
|
-
def trigger(*args, &block)
|
106
|
-
sync_exclusive do
|
107
|
-
event_transition = next_transition
|
108
|
-
if silent
|
109
|
-
event_transition.execute(*args, &block)
|
110
|
-
else
|
111
|
-
machine.send(:transition, event_transition, *args, &block)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Return event name
|
117
|
-
#
|
118
|
-
# @return [String]
|
119
|
-
#
|
120
|
-
# @api public
|
121
|
-
def to_s
|
122
|
-
name.to_s
|
123
|
-
end
|
124
|
-
|
125
|
-
# Return string representation
|
126
|
-
#
|
127
|
-
# @return [String]
|
128
|
-
#
|
129
|
-
# @api public
|
130
|
-
def inspect
|
131
|
-
"<##{self.class} @name=#{name}, @silent=#{silent}, " \
|
132
|
-
"@transitions=#{state_transitions.inspect}>"
|
133
|
-
end
|
134
|
-
|
135
|
-
# Compare whether the instance is greater, less then or equal to other
|
136
|
-
#
|
137
|
-
# @return [-1 0 1]
|
138
|
-
#
|
139
|
-
# @api public
|
140
|
-
def <=>(other)
|
141
|
-
other.is_a?(self.class) && [name, silent, state_transitions] <=>
|
142
|
-
[other.name, other.silent, other.state_transitions]
|
143
|
-
end
|
144
|
-
alias_method :eql?, :==
|
145
|
-
end # Event
|
146
|
-
end # FiniteMachine
|
data/spec/unit/event/add_spec.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Event, '#<<' do
|
6
|
-
let(:machine) { double(:machine) }
|
7
|
-
|
8
|
-
let(:object) { described_class }
|
9
|
-
|
10
|
-
it "adds multiple transitions" do
|
11
|
-
transition = double(:transition)
|
12
|
-
event = object.new(machine)
|
13
|
-
event << transition << transition
|
14
|
-
expect(event.state_transitions).to match_array([transition, transition])
|
15
|
-
end
|
16
|
-
end
|
data/spec/unit/event/eql_spec.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Event, 'eql?' do
|
6
|
-
let(:machine) { double(:machine) }
|
7
|
-
let(:name) { :green }
|
8
|
-
let(:options) { {} }
|
9
|
-
let(:object) { described_class }
|
10
|
-
|
11
|
-
subject(:event) { object.new(machine, options) }
|
12
|
-
|
13
|
-
context 'with the same object' do
|
14
|
-
let(:other) { event }
|
15
|
-
|
16
|
-
it "equals" do
|
17
|
-
expect(event).to eql(other)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'with an equivalent object' do
|
22
|
-
let(:other) { event.dup }
|
23
|
-
|
24
|
-
it "equals" do
|
25
|
-
expect(event).to eql(other)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context "with an object having different name" do
|
30
|
-
let(:other_name) { :red }
|
31
|
-
let(:other) { object.new(machine, {name: other_name}) }
|
32
|
-
|
33
|
-
it "doesn't equal" do
|
34
|
-
expect(event).to_not eql(other)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Event, 'new' do
|
6
|
-
let(:machine) { double(:machine) }
|
7
|
-
let(:name) { :green }
|
8
|
-
let(:options) { {} }
|
9
|
-
let(:object) { described_class }
|
10
|
-
|
11
|
-
subject(:event) { object.new(machine, options) }
|
12
|
-
|
13
|
-
context "by default" do
|
14
|
-
it "sets name to :none" do
|
15
|
-
expect(event.name).to eql(:none)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "sets silent to false"do
|
19
|
-
expect(event.silent).to eql(false)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context "with custom data" do
|
24
|
-
let(:options) { {silent: true, name: name} }
|
25
|
-
|
26
|
-
it "sets name to :green" do
|
27
|
-
expect(event.name).to eql(name)
|
28
|
-
end
|
29
|
-
|
30
|
-
it "sets silent to true"do
|
31
|
-
expect(event.silent).to eql(true)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
it "freezes object" do
|
36
|
-
expect { event.name = :red }.to raise_error(RuntimeError)
|
37
|
-
end
|
38
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Event, '#inspect' do
|
6
|
-
let(:machine) { double(:machine) }
|
7
|
-
|
8
|
-
let(:object) { described_class }
|
9
|
-
|
10
|
-
subject(:event) { object.new(machine, name: :test) }
|
11
|
-
|
12
|
-
it "adds multiple transitions" do
|
13
|
-
transition = double(:transition)
|
14
|
-
event << transition
|
15
|
-
expect(event.inspect).to eq("<#FiniteMachine::Event @name=test, @silent=false, @transitions=[#{transition.inspect}]>")
|
16
|
-
end
|
17
|
-
|
18
|
-
it "prints event name" do
|
19
|
-
expect(event.to_s).to eq('test')
|
20
|
-
end
|
21
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Event, '#next_transition' do
|
6
|
-
let(:object) { described_class }
|
7
|
-
|
8
|
-
subject(:event) { object.new(machine, name: :test) }
|
9
|
-
|
10
|
-
describe "matches transition by name" do
|
11
|
-
let(:machine) { double(:machine) }
|
12
|
-
|
13
|
-
it "finds matching transition" do
|
14
|
-
transition_a = double(:transition_a, current?: false)
|
15
|
-
transition_b = double(:transition_b, current?: true)
|
16
|
-
event << transition_a
|
17
|
-
event << transition_b
|
18
|
-
|
19
|
-
expect(event.next_transition).to eq(transition_b)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "fails to find" do
|
24
|
-
let(:machine) { double(:machine) }
|
25
|
-
|
26
|
-
it "choses first available transition" do
|
27
|
-
transition_a = double(:transition_a, current?: false)
|
28
|
-
transition_b = double(:transition_b, current?: false)
|
29
|
-
event << transition_a
|
30
|
-
event << transition_b
|
31
|
-
|
32
|
-
expect(event.next_transition).to eq(transition_a)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::EventsChain, '#clear' do
|
6
|
-
let(:object) { described_class }
|
7
|
-
|
8
|
-
let(:machine) { double(:machine) }
|
9
|
-
|
10
|
-
subject(:chain) { object.new(machine) }
|
11
|
-
|
12
|
-
it "clears chain events" do
|
13
|
-
event = double(:event)
|
14
|
-
chain.add(:validated, event)
|
15
|
-
expect(chain.empty?).to be(false)
|
16
|
-
|
17
|
-
chain.clear
|
18
|
-
expect(chain.empty?).to be(true)
|
19
|
-
end
|
20
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::EventsChain, '#insert' do
|
6
|
-
let(:object) { described_class }
|
7
|
-
|
8
|
-
let(:machine) { double(:machine) }
|
9
|
-
|
10
|
-
let(:transition) { double(:transition) }
|
11
|
-
|
12
|
-
subject(:chain) { object.new(machine) }
|
13
|
-
|
14
|
-
it "inserts transition" do
|
15
|
-
event = double(:event)
|
16
|
-
chain.add(:validated, event)
|
17
|
-
expect(chain[:validated]).to eq(event)
|
18
|
-
|
19
|
-
expect(event).to receive(:<<).with(transition)
|
20
|
-
chain.insert(:validated, transition)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "fails to insert transition" do
|
24
|
-
expect(chain.insert(:validated, transition)).to be false
|
25
|
-
end
|
26
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::EventsChain, '#select_transition' do
|
6
|
-
let(:object) { described_class }
|
7
|
-
|
8
|
-
let(:machine) { double(:machine) }
|
9
|
-
|
10
|
-
let(:transition) { double(:transition) }
|
11
|
-
|
12
|
-
subject(:chain) { object.new(machine) }
|
13
|
-
|
14
|
-
it "selects transition" do
|
15
|
-
event = double(:event)
|
16
|
-
args = double(:args)
|
17
|
-
chain.add(:validated, event)
|
18
|
-
expect(chain[:validated]).to eq(event)
|
19
|
-
|
20
|
-
expect(event).to receive(:find_transition).with(args)
|
21
|
-
chain.select_transition(:validated, args)
|
22
|
-
end
|
23
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
RSpec.describe FiniteMachine::Transition, 'parsed_states' do
|
6
|
-
let(:machine) { double(:machine) }
|
7
|
-
|
8
|
-
subject(:transition) { described_class.new(machine, attrs) }
|
9
|
-
|
10
|
-
context 'with :to key only' do
|
11
|
-
let(:attrs) { { parsed_states: { any: :red } } }
|
12
|
-
|
13
|
-
it "groups states" do
|
14
|
-
expect(transition.from_states).to eq([:any])
|
15
|
-
expect(transition.to_states).to eq([:red])
|
16
|
-
expect(transition.states).to eql({any: :red})
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'when from array' do
|
21
|
-
let(:attrs) { {parsed_states: { :green => :red, :yellow => :red} } }
|
22
|
-
|
23
|
-
it "groups states" do
|
24
|
-
expect(transition.from_states).to match_array([:green, :yellow])
|
25
|
-
expect(transition.to_states).to eql([:red, :red])
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'when hash of states' do
|
30
|
-
let(:attrs) {
|
31
|
-
{ parsed_states:
|
32
|
-
{ :initial => :low,
|
33
|
-
:low => :medium,
|
34
|
-
:medium => :high } }
|
35
|
-
}
|
36
|
-
|
37
|
-
it "groups states" do
|
38
|
-
expect(transition.from_states).to match_array([:initial, :low, :medium])
|
39
|
-
expect(transition.to_states).to eql([:low, :medium, :high])
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|