finite_machine 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|