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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +34 -0
- data/README.md +564 -569
- data/Rakefile +5 -1
- data/benchmarks/memory_profile.rb +11 -0
- data/benchmarks/memory_usage.rb +16 -9
- data/finite_machine.gemspec +10 -3
- data/lib/finite_machine.rb +34 -46
- data/lib/finite_machine/async_call.rb +5 -21
- data/lib/finite_machine/callable.rb +4 -4
- data/lib/finite_machine/catchable.rb +4 -2
- data/lib/finite_machine/choice_merger.rb +19 -19
- data/lib/finite_machine/const.rb +16 -0
- data/lib/finite_machine/definition.rb +2 -2
- data/lib/finite_machine/dsl.rb +66 -149
- data/lib/finite_machine/env.rb +4 -2
- data/lib/finite_machine/event_definition.rb +7 -15
- data/lib/finite_machine/{events_chain.rb → events_map.rb} +39 -51
- data/lib/finite_machine/hook_event.rb +60 -61
- data/lib/finite_machine/hooks.rb +44 -36
- data/lib/finite_machine/listener.rb +2 -2
- data/lib/finite_machine/logger.rb +5 -4
- data/lib/finite_machine/message_queue.rb +39 -30
- data/lib/finite_machine/observer.rb +55 -37
- data/lib/finite_machine/safety.rb +12 -10
- data/lib/finite_machine/state_definition.rb +3 -5
- data/lib/finite_machine/state_machine.rb +83 -64
- data/lib/finite_machine/state_parser.rb +51 -79
- data/lib/finite_machine/subscribers.rb +1 -1
- data/lib/finite_machine/threadable.rb +3 -1
- data/lib/finite_machine/transition.rb +30 -31
- data/lib/finite_machine/transition_builder.rb +23 -32
- data/lib/finite_machine/transition_event.rb +12 -11
- data/lib/finite_machine/two_phase_lock.rb +3 -1
- data/lib/finite_machine/undefined_transition.rb +5 -6
- data/lib/finite_machine/version.rb +2 -2
- data/spec/integration/system_spec.rb +36 -38
- data/spec/performance/benchmark_spec.rb +13 -21
- data/spec/unit/alias_target_spec.rb +22 -41
- data/spec/unit/async_callbacks_spec.rb +8 -13
- data/spec/unit/auto_methods_spec.rb +44 -0
- data/spec/unit/callable/call_spec.rb +1 -3
- data/spec/unit/callbacks_spec.rb +372 -463
- data/spec/unit/can_spec.rb +13 -23
- data/spec/unit/cancel_callbacks_spec.rb +46 -0
- data/spec/unit/choice_spec.rb +105 -141
- data/spec/unit/define_spec.rb +31 -31
- data/spec/unit/definition_spec.rb +24 -41
- data/spec/unit/event_names_spec.rb +6 -10
- data/spec/unit/events_map/add_spec.rb +23 -0
- data/spec/unit/events_map/choice_transition_spec.rb +25 -0
- data/spec/unit/events_map/clear_spec.rb +13 -0
- data/spec/unit/events_map/events_spec.rb +16 -0
- data/spec/unit/events_map/inspect_spec.rb +22 -0
- data/spec/unit/{events_chain → events_map}/match_transition_spec.rb +12 -14
- data/spec/unit/{events_chain → events_map}/move_to_spec.rb +14 -17
- data/spec/unit/events_map/states_for_spec.rb +17 -0
- data/spec/unit/events_spec.rb +91 -160
- data/spec/unit/handlers_spec.rb +34 -66
- data/spec/unit/hook_event/any_state_or_event_spec.rb +13 -0
- data/spec/unit/hook_event/build_spec.rb +1 -3
- data/spec/unit/hook_event/eql_spec.rb +1 -3
- data/spec/unit/hook_event/initialize_spec.rb +2 -4
- data/spec/unit/hook_event/notify_spec.rb +2 -4
- data/spec/unit/hooks/clear_spec.rb +1 -1
- data/spec/unit/hooks/{call_spec.rb → find_spec.rb} +4 -9
- data/spec/unit/hooks/inspect_spec.rb +16 -8
- data/spec/unit/hooks/register_spec.rb +4 -9
- data/spec/unit/if_unless_spec.rb +76 -115
- data/spec/unit/initial_spec.rb +50 -82
- data/spec/unit/inspect_spec.rb +14 -9
- data/spec/unit/is_spec.rb +12 -18
- data/spec/unit/log_transitions_spec.rb +4 -10
- data/spec/unit/logger_spec.rb +1 -3
- data/spec/unit/{event_queue_spec.rb → message_queue_spec.rb} +15 -8
- data/spec/unit/new_spec.rb +50 -0
- data/spec/unit/respond_to_spec.rb +2 -6
- data/spec/unit/state_parser/parse_spec.rb +9 -12
- data/spec/unit/states_spec.rb +12 -18
- data/spec/unit/subscribers_spec.rb +1 -3
- data/spec/unit/target_spec.rb +60 -93
- data/spec/unit/terminated_spec.rb +15 -25
- data/spec/unit/transition/check_conditions_spec.rb +16 -15
- data/spec/unit/transition/inspect_spec.rb +6 -6
- data/spec/unit/transition/matches_spec.rb +5 -7
- data/spec/unit/transition/states_spec.rb +5 -7
- data/spec/unit/transition/to_state_spec.rb +5 -13
- data/spec/unit/trigger_spec.rb +5 -9
- data/spec/unit/undefined_transition/eql_spec.rb +1 -3
- metadata +86 -49
- data/.gitignore +0 -18
- data/.rspec +0 -5
- data/.travis.yml +0 -27
- data/Gemfile +0 -16
- data/assets/finite_machine_logo.png +0 -0
- data/lib/finite_machine/async_proxy.rb +0 -55
- data/spec/unit/async_events_spec.rb +0 -107
- data/spec/unit/events_chain/add_spec.rb +0 -25
- data/spec/unit/events_chain/cancel_transitions_spec.rb +0 -22
- data/spec/unit/events_chain/choice_transition_spec.rb +0 -28
- data/spec/unit/events_chain/clear_spec.rb +0 -15
- data/spec/unit/events_chain/events_spec.rb +0 -18
- data/spec/unit/events_chain/inspect_spec.rb +0 -24
- data/spec/unit/events_chain/states_for_spec.rb +0 -17
- data/spec/unit/hook_event/infer_default_name_spec.rb +0 -13
- data/spec/unit/state_parser/inspect_spec.rb +0 -25
data/.rspec
DELETED
data/.travis.yml
DELETED
@@ -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
|
Binary file
|
@@ -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
|