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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Gemfile +1 -1
  4. data/README.md +73 -35
  5. data/assets/finite_machine_logo.png +0 -0
  6. data/lib/finite_machine.rb +0 -7
  7. data/lib/finite_machine/async_proxy.rb +1 -2
  8. data/lib/finite_machine/dsl.rb +13 -14
  9. data/lib/finite_machine/event_definition.rb +32 -35
  10. data/lib/finite_machine/events_chain.rb +183 -37
  11. data/lib/finite_machine/hook_event.rb +47 -42
  12. data/lib/finite_machine/logger.rb +3 -4
  13. data/lib/finite_machine/observer.rb +27 -11
  14. data/lib/finite_machine/state_definition.rb +66 -0
  15. data/lib/finite_machine/state_machine.rb +177 -99
  16. data/lib/finite_machine/subscribers.rb +17 -6
  17. data/lib/finite_machine/thread_context.rb +1 -1
  18. data/lib/finite_machine/transition.rb +45 -173
  19. data/lib/finite_machine/transition_builder.rb +24 -6
  20. data/lib/finite_machine/transition_event.rb +5 -4
  21. data/lib/finite_machine/undefined_transition.rb +32 -0
  22. data/lib/finite_machine/version.rb +1 -1
  23. data/spec/spec_helper.rb +1 -0
  24. data/spec/unit/async_events_spec.rb +24 -18
  25. data/spec/unit/callbacks_spec.rb +0 -19
  26. data/spec/unit/event_names_spec.rb +19 -0
  27. data/spec/unit/events_chain/add_spec.rb +25 -0
  28. data/spec/unit/events_chain/cancel_transitions_spec.rb +22 -0
  29. data/spec/unit/events_chain/choice_transition_spec.rb +28 -0
  30. data/spec/unit/events_chain/clear_spec.rb +7 -18
  31. data/spec/unit/events_chain/events_spec.rb +18 -0
  32. data/spec/unit/events_chain/inspect_spec.rb +14 -17
  33. data/spec/unit/events_chain/match_transition_spec.rb +37 -0
  34. data/spec/unit/events_chain/move_to_spec.rb +48 -0
  35. data/spec/unit/events_chain/states_for_spec.rb +17 -0
  36. data/spec/unit/events_spec.rb +119 -27
  37. data/spec/unit/hook_event/build_spec.rb +15 -0
  38. data/spec/unit/hook_event/eql_spec.rb +3 -4
  39. data/spec/unit/hook_event/initialize_spec.rb +14 -11
  40. data/spec/unit/hook_event/notify_spec.rb +14 -0
  41. data/spec/unit/{initialize_spec.rb → initial_spec.rb} +1 -1
  42. data/spec/unit/inspect_spec.rb +1 -1
  43. data/spec/unit/logger_spec.rb +4 -5
  44. data/spec/unit/subscribers_spec.rb +20 -9
  45. data/spec/unit/transition/check_conditions_spec.rb +54 -0
  46. data/spec/unit/transition/inspect_spec.rb +2 -2
  47. data/spec/unit/transition/matches_spec.rb +23 -0
  48. data/spec/unit/transition/states_spec.rb +31 -0
  49. data/spec/unit/transition/to_state_spec.rb +27 -0
  50. data/spec/unit/trigger_spec.rb +22 -0
  51. data/spec/unit/undefined_transition/eql_spec.rb +17 -0
  52. data/tasks/console.rake +1 -0
  53. metadata +39 -23
  54. data/lib/finite_machine/event.rb +0 -146
  55. data/spec/unit/event/add_spec.rb +0 -16
  56. data/spec/unit/event/eql_spec.rb +0 -37
  57. data/spec/unit/event/initialize_spec.rb +0 -38
  58. data/spec/unit/event/inspect_spec.rb +0 -21
  59. data/spec/unit/event/next_transition_spec.rb +0 -35
  60. data/spec/unit/events_chain/check_choice_conditions_spec.rb +0 -20
  61. data/spec/unit/events_chain/insert_spec.rb +0 -26
  62. data/spec/unit/events_chain/select_transition_spec.rb +0 -23
  63. data/spec/unit/transition/parse_states_spec.rb +0 -42
@@ -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
@@ -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
@@ -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