finite_machine 0.11.3 → 0.12.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 (106) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +564 -569
  4. data/Rakefile +5 -1
  5. data/benchmarks/memory_profile.rb +11 -0
  6. data/benchmarks/memory_usage.rb +16 -9
  7. data/finite_machine.gemspec +10 -3
  8. data/lib/finite_machine.rb +34 -46
  9. data/lib/finite_machine/async_call.rb +5 -21
  10. data/lib/finite_machine/callable.rb +4 -4
  11. data/lib/finite_machine/catchable.rb +4 -2
  12. data/lib/finite_machine/choice_merger.rb +19 -19
  13. data/lib/finite_machine/const.rb +16 -0
  14. data/lib/finite_machine/definition.rb +2 -2
  15. data/lib/finite_machine/dsl.rb +66 -149
  16. data/lib/finite_machine/env.rb +4 -2
  17. data/lib/finite_machine/event_definition.rb +7 -15
  18. data/lib/finite_machine/{events_chain.rb → events_map.rb} +39 -51
  19. data/lib/finite_machine/hook_event.rb +60 -61
  20. data/lib/finite_machine/hooks.rb +44 -36
  21. data/lib/finite_machine/listener.rb +2 -2
  22. data/lib/finite_machine/logger.rb +5 -4
  23. data/lib/finite_machine/message_queue.rb +39 -30
  24. data/lib/finite_machine/observer.rb +55 -37
  25. data/lib/finite_machine/safety.rb +12 -10
  26. data/lib/finite_machine/state_definition.rb +3 -5
  27. data/lib/finite_machine/state_machine.rb +83 -64
  28. data/lib/finite_machine/state_parser.rb +51 -79
  29. data/lib/finite_machine/subscribers.rb +1 -1
  30. data/lib/finite_machine/threadable.rb +3 -1
  31. data/lib/finite_machine/transition.rb +30 -31
  32. data/lib/finite_machine/transition_builder.rb +23 -32
  33. data/lib/finite_machine/transition_event.rb +12 -11
  34. data/lib/finite_machine/two_phase_lock.rb +3 -1
  35. data/lib/finite_machine/undefined_transition.rb +5 -6
  36. data/lib/finite_machine/version.rb +2 -2
  37. data/spec/integration/system_spec.rb +36 -38
  38. data/spec/performance/benchmark_spec.rb +13 -21
  39. data/spec/unit/alias_target_spec.rb +22 -41
  40. data/spec/unit/async_callbacks_spec.rb +8 -13
  41. data/spec/unit/auto_methods_spec.rb +44 -0
  42. data/spec/unit/callable/call_spec.rb +1 -3
  43. data/spec/unit/callbacks_spec.rb +372 -463
  44. data/spec/unit/can_spec.rb +13 -23
  45. data/spec/unit/cancel_callbacks_spec.rb +46 -0
  46. data/spec/unit/choice_spec.rb +105 -141
  47. data/spec/unit/define_spec.rb +31 -31
  48. data/spec/unit/definition_spec.rb +24 -41
  49. data/spec/unit/event_names_spec.rb +6 -10
  50. data/spec/unit/events_map/add_spec.rb +23 -0
  51. data/spec/unit/events_map/choice_transition_spec.rb +25 -0
  52. data/spec/unit/events_map/clear_spec.rb +13 -0
  53. data/spec/unit/events_map/events_spec.rb +16 -0
  54. data/spec/unit/events_map/inspect_spec.rb +22 -0
  55. data/spec/unit/{events_chain → events_map}/match_transition_spec.rb +12 -14
  56. data/spec/unit/{events_chain → events_map}/move_to_spec.rb +14 -17
  57. data/spec/unit/events_map/states_for_spec.rb +17 -0
  58. data/spec/unit/events_spec.rb +91 -160
  59. data/spec/unit/handlers_spec.rb +34 -66
  60. data/spec/unit/hook_event/any_state_or_event_spec.rb +13 -0
  61. data/spec/unit/hook_event/build_spec.rb +1 -3
  62. data/spec/unit/hook_event/eql_spec.rb +1 -3
  63. data/spec/unit/hook_event/initialize_spec.rb +2 -4
  64. data/spec/unit/hook_event/notify_spec.rb +2 -4
  65. data/spec/unit/hooks/clear_spec.rb +1 -1
  66. data/spec/unit/hooks/{call_spec.rb → find_spec.rb} +4 -9
  67. data/spec/unit/hooks/inspect_spec.rb +16 -8
  68. data/spec/unit/hooks/register_spec.rb +4 -9
  69. data/spec/unit/if_unless_spec.rb +76 -115
  70. data/spec/unit/initial_spec.rb +50 -82
  71. data/spec/unit/inspect_spec.rb +14 -9
  72. data/spec/unit/is_spec.rb +12 -18
  73. data/spec/unit/log_transitions_spec.rb +4 -10
  74. data/spec/unit/logger_spec.rb +1 -3
  75. data/spec/unit/{event_queue_spec.rb → message_queue_spec.rb} +15 -8
  76. data/spec/unit/new_spec.rb +50 -0
  77. data/spec/unit/respond_to_spec.rb +2 -6
  78. data/spec/unit/state_parser/parse_spec.rb +9 -12
  79. data/spec/unit/states_spec.rb +12 -18
  80. data/spec/unit/subscribers_spec.rb +1 -3
  81. data/spec/unit/target_spec.rb +60 -93
  82. data/spec/unit/terminated_spec.rb +15 -25
  83. data/spec/unit/transition/check_conditions_spec.rb +16 -15
  84. data/spec/unit/transition/inspect_spec.rb +6 -6
  85. data/spec/unit/transition/matches_spec.rb +5 -7
  86. data/spec/unit/transition/states_spec.rb +5 -7
  87. data/spec/unit/transition/to_state_spec.rb +5 -13
  88. data/spec/unit/trigger_spec.rb +5 -9
  89. data/spec/unit/undefined_transition/eql_spec.rb +1 -3
  90. metadata +86 -49
  91. data/.gitignore +0 -18
  92. data/.rspec +0 -5
  93. data/.travis.yml +0 -27
  94. data/Gemfile +0 -16
  95. data/assets/finite_machine_logo.png +0 -0
  96. data/lib/finite_machine/async_proxy.rb +0 -55
  97. data/spec/unit/async_events_spec.rb +0 -107
  98. data/spec/unit/events_chain/add_spec.rb +0 -25
  99. data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
  100. data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
  101. data/spec/unit/events_chain/clear_spec.rb +0 -15
  102. data/spec/unit/events_chain/events_spec.rb +0 -18
  103. data/spec/unit/events_chain/inspect_spec.rb +0 -24
  104. data/spec/unit/events_chain/states_for_spec.rb +0 -17
  105. data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
  106. data/spec/unit/state_parser/inspect_spec.rb +0 -25
data/.rspec DELETED
@@ -1,5 +0,0 @@
1
- --color
2
- --format progress
3
- --order random
4
- --warnings
5
- --require spec_helper
@@ -1,27 +0,0 @@
1
- ---
2
- language: ruby
3
- sudo: false
4
- cache: bundler
5
- bundler_args: --without yard benchmarks
6
- script: "bundle exec rake ci"
7
- rvm:
8
- - 1.9.3
9
- - 2.0
10
- - 2.1
11
- - 2.2
12
- - 2.3.0
13
- - ruby-head
14
- - rbx-2
15
- matrix:
16
- include:
17
- - rvm: jruby-19mode
18
- - rvm: jruby-20mode
19
- - rvm: jruby-21mode
20
- - rvm: jruby-head
21
- allow_failures:
22
- - rvm: ruby-head
23
- - rvm: jruby-head
24
- - rvm: rbx-2
25
- fast_finish: true
26
- branches:
27
- only: master
data/Gemfile DELETED
@@ -1,16 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :development do
6
- gem 'pry', '~> 0.10.1'
7
- gem 'rspec', '~> 3.4.0'
8
- gem 'rspec-benchmark', '~> 0.1.0'
9
- gem 'yard', '~> 0.8.7'
10
- end
11
-
12
- group :metrics do
13
- gem 'coveralls', '~> 0.8.1'
14
- gem 'simplecov', '~> 0.10.0'
15
- gem 'yardstick', '~> 0.9.9'
16
- end
@@ -1,55 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module FiniteMachine
4
- # An asynchronous messages proxy
5
- #
6
- # @api private
7
- class AsyncProxy
8
- include Threadable
9
-
10
- attr_threadsafe :context
11
-
12
- # The queue for asynchronoous events
13
- #
14
- # @return [EventQueue]
15
- #
16
- # @api private
17
- attr_threadsafe :event_queue
18
-
19
- # Initialize an AsynxProxy
20
- #
21
- # @param [Object] context
22
- # the context this proxy is associated with
23
- #
24
- # @api private
25
- def initialize(context)
26
- self.context = context
27
- self.event_queue = MessageQueue.new
28
-
29
- ObjectSpace.define_finalizer(self, self.class.cleanup(event_queue))
30
- end
31
-
32
- # Delegate asynchronous event to event queue
33
- #
34
- # @api private
35
- def method_missing(method_name, *args, &block)
36
- callable = Callable.new(method_name)
37
- async_call = AsyncCall.new(context, callable, *args, &block)
38
-
39
- event_queue.start unless event_queue.running?
40
- context.event_queue << async_call
41
- end
42
-
43
- # Clean up event queue
44
- #
45
- # @api private
46
- def self.cleanup(queue)
47
- proc do
48
- begin
49
- queue && queue.shutdown
50
- rescue MessageQueueDeadError
51
- end
52
- end
53
- end
54
- end # AsyncProxy
55
- end # FiniteMachine
@@ -1,107 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe FiniteMachine, 'async events' do
4
-
5
- it 'runs events asynchronously' do
6
- called = []
7
- fsm = FiniteMachine.define do
8
- initial :green
9
-
10
- events {
11
- event :slow, :green => :yellow
12
- event :stop, :yellow => :red
13
- event :ready, :red => :yellow
14
- event :go, :yellow => :green
15
- }
16
-
17
- callbacks {
18
- on_enter :yellow do |event, a| called << "on_enter_yellow_#{a}" end
19
- on_enter :red do |event, a| called << "on_enter_red_#{a}" end
20
- }
21
- end
22
-
23
- expect(fsm.current).to eql(:green)
24
- fsm.async.slow(:foo)
25
- fsm.event_queue.join 0.01
26
- expect(fsm.current).to eql(:yellow)
27
- expect(called).to eql([
28
- 'on_enter_yellow_foo'
29
- ])
30
- fsm.async(:stop, :bar) # execute directly
31
- fsm.event_queue.join 0.01
32
- expect(fsm.current).to eql(:red)
33
- expect(called).to match_array([
34
- 'on_enter_yellow_foo',
35
- 'on_enter_red_bar'
36
- ])
37
- end
38
-
39
- it 'correctly passes parameters to conditionals' do
40
- called = []
41
- fsm = FiniteMachine.define do
42
- events {
43
- event :go, :none => :green,
44
- if: proc { |context, arg|
45
- called << "cond_none_green(#{context},#{arg})"; true
46
- }
47
-
48
- event :stop, from: :any do
49
- choice :red, if: proc { |context, arg|
50
- called << "cond_any_red(#{context},#{arg})"; true
51
- }
52
- end
53
- }
54
- end
55
- expect(fsm.current).to eql(:none)
56
- fsm.async.go(:foo)
57
- fsm.event_queue.join 0.02
58
- expect(fsm.current).to eql(:green)
59
- expect(called).to eql(["cond_none_green(#{fsm},foo)"])
60
-
61
- expect(fsm.current).to eql(:green)
62
- fsm.async.stop(:bar)
63
- fsm.event_queue.join 0.02
64
- expect(fsm.current).to eql(:red)
65
- expect(called).to match_array([
66
- "cond_none_green(#{fsm},foo)",
67
- "cond_any_red(#{fsm},bar)"
68
- ])
69
- end
70
-
71
- it "ensure queue per thread" do
72
- called = []
73
- fsmFoo = nil
74
- fsmBar = nil
75
- foo_thread = Thread.new {
76
- fsmFoo = FiniteMachine.define do
77
- initial :green
78
- events { event :slow, :green => :yellow }
79
-
80
- callbacks {
81
- on_enter :yellow do |event, a| called << "(foo)on_enter_yellow_#{a}" end
82
- }
83
- end
84
- fsmFoo.async.slow(:foo)
85
- }
86
- bar_thread = Thread.new {
87
- fsmBar = FiniteMachine.define do
88
- initial :green
89
- events { event :slow, :green => :yellow }
90
-
91
- callbacks {
92
- on_enter :yellow do |event, a| called << "(bar)on_enter_yellow_#{a}" end
93
- }
94
- end
95
- fsmBar.async.slow(:bar)
96
- }
97
- ThreadsWait.all_waits(foo_thread, bar_thread)
98
- fsmFoo.event_queue.join(0.01)
99
- fsmBar.event_queue.join(0.01)
100
- expect(called).to match_array([
101
- '(foo)on_enter_yellow_foo',
102
- '(bar)on_enter_yellow_bar'
103
- ])
104
- expect(fsmFoo.current).to eql(:yellow)
105
- expect(fsmBar.current).to eql(:yellow)
106
- end
107
- end
@@ -1,25 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::EventsChain, '.add' do
6
- it "adds transitions" do
7
- transition = double(:transition)
8
- events_chain = described_class.new
9
-
10
- events_chain.add(:validated, transition)
11
- expect(events_chain[:validated]).to eq([transition])
12
-
13
- events_chain.add(:validated, transition)
14
- expect(events_chain[:validated]).to eq([transition, transition])
15
- end
16
-
17
- it "allows to chain add operations" do
18
- events_chain = described_class.new
19
- transition = double(:transition)
20
-
21
- events_chain.add(:go, transition).add(:start, transition)
22
-
23
- expect(events_chain.size).to eq(2)
24
- end
25
- end
@@ -1,22 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::EventsChain, '.cancel_transitions' do
6
- it "sets cancel status for chosen transitions" do
7
- events_chain = described_class.new
8
- transition_a = spy(:transition_a, cancelled: false)
9
- transition_b = spy(:transition_b, cancelled: false)
10
- transition_c = spy(:transition_c, cancelled: false)
11
-
12
- events_chain.add(:start, transition_a)
13
- events_chain.add(:start, transition_b)
14
- events_chain.add(:finish, transition_c)
15
-
16
- events_chain.cancel_transitions(:start)
17
-
18
- expect(transition_a).to have_received(:cancelled=).with(true)
19
- expect(transition_b).to have_received(:cancelled=).with(true)
20
- expect(transition_c).not_to have_received(:cancelled=)
21
- end
22
- end
@@ -1,28 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::EventsChain, '.choice_transition?' do
6
-
7
- it "checks if transition has many branches" do
8
- transition_a = double(:transition_a, matches?: true)
9
- transition_b = double(:transition_b, matches?: true)
10
-
11
- events_chain = described_class.new
12
- events_chain.add(:go, transition_a)
13
- events_chain.add(:go, transition_b)
14
-
15
- expect(events_chain.choice_transition?(:go, :green)).to eq(true)
16
- end
17
-
18
- it "checks that transition has no branches" do
19
- transition_a = double(:transition_a, matches?: false)
20
- transition_b = double(:transition_b, matches?: true)
21
-
22
- events_chain = described_class.new
23
- events_chain.add(:go, transition_a)
24
- events_chain.add(:go, transition_b)
25
-
26
- expect(events_chain.choice_transition?(:go, :green)).to eq(false)
27
- end
28
- end
@@ -1,15 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::EventsChain, '#clear' do
6
- it "clears chain events" do
7
- event = double(:event)
8
- events_chain = described_class.new
9
- events_chain.add(:validated, event)
10
- expect(events_chain.empty?).to be(false)
11
-
12
- events_chain.clear
13
- expect(events_chain.empty?).to be(true)
14
- end
15
- end
@@ -1,18 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::EventsChain, '.events' do
6
- it "has no event names" do
7
- events_chain = described_class.new
8
- expect(events_chain.events).to eq([])
9
- end
10
-
11
- it "returns all event names" do
12
- events_chain = described_class.new
13
- transition = double(:transition)
14
- events_chain.add(:ready, transition)
15
- events_chain.add(:go, transition)
16
- expect(events_chain.events).to match_array([:ready, :go])
17
- end
18
- end
@@ -1,24 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::EventsChain, '#inspect' do
6
- it "inspects empty events chain" do
7
- events_chain = described_class.new
8
- expect(events_chain.inspect).to eq("<#FiniteMachine::EventsChain @chain={}>")
9
- end
10
-
11
- it "inspect events chain" do
12
- transition = double(:transition)
13
- events_chain = described_class.new
14
- events_chain.add(:validated, transition)
15
- expect(events_chain.inspect).to eq("<#FiniteMachine::EventsChain @chain=#{{validated: [transition]}}>")
16
- end
17
-
18
- it "prints events chain" do
19
- transition = double(:transition)
20
- events_chain = described_class.new
21
- events_chain.add(:validated, transition)
22
- expect(events_chain.to_s).to eq("#{{validated: [transition]}}")
23
- end
24
- end
@@ -1,17 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe FiniteMachine::EventsChain do
4
- it "finds current states for event name" do
5
- transition = spy(:transition, states: {:red => :yellow, :yellow => :green})
6
- events_chain = described_class.new
7
- events_chain.add(:start, transition)
8
-
9
- expect(events_chain.states_for(:start)).to eq([:red, :yellow])
10
- end
11
-
12
- it "fails to find any states for event name" do
13
- events_chain = described_class.new
14
-
15
- expect(events_chain.states_for(:start)).to eq([])
16
- end
17
- end
@@ -1,13 +0,0 @@
1
- # encoding: utf-8
2
-
3
- RSpec.describe FiniteMachine::HookEvent, '#infer_default_name' do
4
- it "infers default name for state" do
5
- hook_event = described_class::Enter
6
- expect(described_class.infer_default_name(hook_event)).to eq(:any)
7
- end
8
-
9
- it "infers default name for event" do
10
- hook_event = described_class::Before
11
- expect(described_class.infer_default_name(hook_event)).to eq(:any_event)
12
- end
13
- end
@@ -1,25 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe FiniteMachine::StateParser, "#inspect" do
6
- let(:object) { described_class }
7
-
8
- subject(:parser) { object.new(attrs) }
9
-
10
- describe '#inspect' do
11
- let(:attrs) { { green: :yellow } }
12
-
13
- it "inspects parser" do
14
- expect(parser.inspect).to eq("<#FiniteMachine::StateParser @attrs=green:yellow>")
15
- end
16
- end
17
-
18
- describe '#to_s' do
19
- let(:attrs) { { green: :yellow } }
20
-
21
- it "prints parser attributes" do
22
- expect(parser.to_s).to eq(attrs.to_s)
23
- end
24
- end
25
- end