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.
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