finite_machine 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/README.md +1 -1
  4. data/lib/finite_machine.rb +3 -1
  5. data/lib/finite_machine/choice_merger.rb +2 -2
  6. data/lib/finite_machine/dsl.rb +4 -4
  7. data/lib/finite_machine/message_queue.rb +0 -1
  8. data/lib/finite_machine/state_machine.rb +3 -3
  9. data/lib/finite_machine/two_phase_lock.rb +6 -6
  10. data/lib/finite_machine/version.rb +1 -1
  11. metadata +20 -146
  12. data/Rakefile +0 -12
  13. data/benchmarks/memory_profile.rb +0 -11
  14. data/benchmarks/memory_usage.rb +0 -28
  15. data/examples/atm.rb +0 -45
  16. data/examples/bug_system.rb +0 -145
  17. data/finite_machine.gemspec +0 -30
  18. data/spec/integration/system_spec.rb +0 -93
  19. data/spec/performance/benchmark_spec.rb +0 -54
  20. data/spec/spec_helper.rb +0 -34
  21. data/spec/unit/alias_target_spec.rb +0 -89
  22. data/spec/unit/async_callbacks_spec.rb +0 -28
  23. data/spec/unit/auto_methods_spec.rb +0 -44
  24. data/spec/unit/callable/call_spec.rb +0 -111
  25. data/spec/unit/callbacks_spec.rb +0 -851
  26. data/spec/unit/can_spec.rb +0 -88
  27. data/spec/unit/cancel_callbacks_spec.rb +0 -46
  28. data/spec/unit/choice_spec.rb +0 -295
  29. data/spec/unit/define_spec.rb +0 -55
  30. data/spec/unit/definition_spec.rb +0 -98
  31. data/spec/unit/event_names_spec.rb +0 -15
  32. data/spec/unit/events_map/add_spec.rb +0 -23
  33. data/spec/unit/events_map/choice_transition_spec.rb +0 -25
  34. data/spec/unit/events_map/clear_spec.rb +0 -13
  35. data/spec/unit/events_map/events_spec.rb +0 -16
  36. data/spec/unit/events_map/inspect_spec.rb +0 -22
  37. data/spec/unit/events_map/match_transition_spec.rb +0 -35
  38. data/spec/unit/events_map/move_to_spec.rb +0 -45
  39. data/spec/unit/events_map/states_for_spec.rb +0 -17
  40. data/spec/unit/events_spec.rb +0 -390
  41. data/spec/unit/handlers_spec.rb +0 -120
  42. data/spec/unit/hook_event/any_state_or_event_spec.rb +0 -13
  43. data/spec/unit/hook_event/build_spec.rb +0 -13
  44. data/spec/unit/hook_event/eql_spec.rb +0 -34
  45. data/spec/unit/hook_event/initialize_spec.rb +0 -23
  46. data/spec/unit/hook_event/notify_spec.rb +0 -12
  47. data/spec/unit/hooks/clear_spec.rb +0 -16
  48. data/spec/unit/hooks/find_spec.rb +0 -19
  49. data/spec/unit/hooks/inspect_spec.rb +0 -25
  50. data/spec/unit/hooks/register_spec.rb +0 -17
  51. data/spec/unit/if_unless_spec.rb +0 -314
  52. data/spec/unit/initial_spec.rb +0 -190
  53. data/spec/unit/inspect_spec.rb +0 -22
  54. data/spec/unit/is_spec.rb +0 -49
  55. data/spec/unit/log_transitions_spec.rb +0 -24
  56. data/spec/unit/logger_spec.rb +0 -36
  57. data/spec/unit/message_queue_spec.rb +0 -62
  58. data/spec/unit/new_spec.rb +0 -50
  59. data/spec/unit/respond_to_spec.rb +0 -34
  60. data/spec/unit/state_parser/parse_spec.rb +0 -56
  61. data/spec/unit/states_spec.rb +0 -28
  62. data/spec/unit/subscribers_spec.rb +0 -40
  63. data/spec/unit/target_spec.rb +0 -225
  64. data/spec/unit/terminated_spec.rb +0 -85
  65. data/spec/unit/transition/check_conditions_spec.rb +0 -55
  66. data/spec/unit/transition/inspect_spec.rb +0 -25
  67. data/spec/unit/transition/matches_spec.rb +0 -21
  68. data/spec/unit/transition/states_spec.rb +0 -29
  69. data/spec/unit/transition/to_state_spec.rb +0 -19
  70. data/spec/unit/trigger_spec.rb +0 -18
  71. data/spec/unit/undefined_transition/eql_spec.rb +0 -15
  72. data/tasks/console.rake +0 -11
  73. data/tasks/coverage.rake +0 -11
  74. data/tasks/spec.rake +0 -34
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../lib/finite_machine'
4
-
5
- 5.times do
6
- puts
7
-
8
- GC.start
9
-
10
- gc_before = GC.stat
11
- objects_before = ObjectSpace.count_objects
12
- p objects_before[:T_OBJECT]
13
-
14
- 1_000.times do
15
- FiniteMachine.new do
16
- initial :green
17
-
18
- events { event :slow, :green => :yellow }
19
- end
20
- end
21
-
22
- objects_after = ObjectSpace.count_objects
23
- gc_after = GC.stat
24
- p objects_after[:T_OBJECT]
25
-
26
- p "GC count: #{gc_after[:count] - gc_before[:count]}"
27
- p "Objects count: #{objects_after[:T_OBJECT] - objects_before[:T_OBJECT]}"
28
- end
@@ -1,45 +0,0 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
- require 'finite_machine'
4
-
5
- class Account
6
- attr_accessor :number
7
-
8
- def verify(account_number, pin)
9
- return account_number == 123456 && pin == 666
10
- end
11
- end
12
-
13
- account = Account.new
14
-
15
- atm = FiniteMachine.define do
16
- initial :unauthorized
17
-
18
- target account
19
-
20
- events {
21
- event :authorize, :unauthorized => :authorized, if: -> (account, account_number, pin) {
22
- account.verify(account_number, pin)
23
- }
24
- event :deauthorize, :authorized => :unauthorized
25
- }
26
-
27
- callbacks {
28
- on_exit :unauthorized do |event, account_number, pin|
29
- # if verify(account_number, pin)
30
- self.number = account_number
31
- # else
32
- # puts "Invalid Account and/or PIN"
33
- # FiniteMachine::CANCELLED
34
- # end
35
- end
36
- }
37
- end
38
-
39
- atm.authorize(111222, 666)
40
- puts "authorized: #{atm.authorized?}"
41
- puts "Number: #{account.number}"
42
-
43
- atm.authorize(123456, 666)
44
- puts "authorized: #{atm.authorized?}"
45
- puts "Number: #{account.number}"
@@ -1,145 +0,0 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
- require 'finite_machine'
4
-
5
- class User
6
- attr_accessor :name
7
-
8
- def initialize(name)
9
- @name = name
10
- end
11
- end
12
-
13
- class Manager < User
14
- attr_accessor :developers
15
-
16
- def initialize(name)
17
- super
18
- @developers = []
19
- end
20
-
21
- def manages(developer)
22
- @developers << developer
23
- end
24
-
25
- def assign(bug)
26
- developer = @developers.first
27
- bug.assign
28
- developer.bug = bug
29
- end
30
- end
31
-
32
- class Tester < User
33
- def report(bug)
34
- bug.report
35
- end
36
-
37
- def reopen(bug)
38
- bug.reopen
39
- end
40
- end
41
-
42
- class Developer < User
43
- attr_accessor :bug
44
-
45
- def work_on
46
- bug.start
47
- end
48
-
49
- def resolve
50
- bug.close
51
- end
52
- end
53
-
54
- class BugSystem
55
- attr_accessor :managers
56
-
57
- def initialize(managers = [])
58
- @managers = managers
59
- end
60
-
61
- def notify_manager(bug)
62
- manager = @managers.first
63
- manager.assign(bug)
64
- end
65
- end
66
-
67
- class Bug
68
- attr_accessor :name
69
- attr_accessor :priority
70
- # fake belongs_to relationship
71
- attr_accessor :bug_system
72
-
73
- def initialize(name, priority)
74
- @name = name
75
- @priority = priority
76
- end
77
-
78
- def report
79
- status.report
80
- end
81
-
82
- def assign
83
- status.assign
84
- end
85
-
86
- def start
87
- status.start
88
- end
89
-
90
- def close
91
- status.close
92
- end
93
-
94
- def reopen
95
- status.reopen
96
- end
97
-
98
- def status
99
- context = self
100
- @status ||= FiniteMachine.define do
101
- target context
102
-
103
- events {
104
- event :report, :none => :new
105
- event :assign, :new => :assigned
106
- event :start, :assigned => :in_progress
107
- event :close, [:in_progress, :reopened] => :resolved
108
- event :reopen, :resolved => :reopened
109
- }
110
-
111
- callbacks {
112
- on_enter :new do |event|
113
- bug_system.notify_manager(self)
114
- end
115
- }
116
- end
117
- end
118
- end
119
-
120
- tester = Tester.new("John")
121
- manager = Manager.new("David")
122
- developer = Developer.new("Piotr")
123
- manager.manages(developer)
124
-
125
- bug_system = BugSystem.new([manager])
126
- bug = Bug.new(:trojan, :high)
127
- bug.bug_system = bug_system
128
-
129
- puts "A BUG's LIFE"
130
- puts "#1 #{bug.status.current}"
131
-
132
- tester.report(bug)
133
- puts "#2 #{bug.status.current}"
134
-
135
- developer.work_on
136
- puts "#3 #{bug.status.current}"
137
-
138
- developer.resolve
139
- puts "#4 #{bug.status.current}"
140
-
141
- tester.reopen(bug)
142
- puts "#5 #{bug.status.current}"
143
-
144
- developer.resolve
145
- puts "#6 #{bug.status.current}"
@@ -1,30 +0,0 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'finite_machine/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "finite_machine"
7
- spec.version = FiniteMachine::VERSION
8
- spec.authors = ["Piotr Murach"]
9
- spec.email = ["me@piotrmurach.com"]
10
- spec.description = %q{A minimal finite state machine with a straightforward syntax. You can quickly model states, add callbacks and use object-oriented techniques to integrate with ORMs.}
11
- spec.summary = %q{A minimal finite state machine with a straightforward syntax.}
12
- spec.homepage = "http://piotrmurach.github.io/finite_machine/"
13
- spec.license = "MIT"
14
-
15
- spec.files = Dir['{lib,spec,examples,benchmarks}/**/*.rb']
16
- spec.files += Dir['tasks/*', 'finite_machine.gemspec']
17
- spec.files += Dir['README.md', 'CHANGELOG.md', 'LICENSE.txt', 'Rakefile']
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
- spec.require_paths = ["lib"]
21
-
22
- spec.required_ruby_version = '>= 2.0.0'
23
-
24
- spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
25
-
26
- spec.add_development_dependency 'bundler', '>= 1.5'
27
- spec.add_development_dependency 'rspec', '~> 3.1'
28
- spec.add_development_dependency 'rspec-benchmark', '~> 0.4.0'
29
- spec.add_development_dependency 'rake'
30
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, 'system' do
4
-
5
- it "doesn't share state between machine callbacks" do
6
- callbacks = []
7
- stub_const("FSM_A", Class.new(FiniteMachine::Definition) do
8
- event :init, :none => :green
9
- event :start, any_state => :green
10
-
11
- on_before do |event|
12
- callbacks << "fsmA on_before(#{event.name})"
13
- end
14
- on_enter_green do |event|
15
- target.fire
16
- callbacks << "fsmA on_enter(:green)"
17
- end
18
- once_on_enter_green do |event|
19
- callbacks << "fsmA once_on_enter(:green)"
20
- end
21
- end)
22
-
23
- stub_const("FSM_B", Class.new(FiniteMachine::Definition) do
24
- event :init, :none => :stopped
25
- event :start, :stopped => :started
26
-
27
- on_before do |event|
28
- callbacks << "fsmB on_before(#{event.name})"
29
- end
30
- on_enter_stopped do |event|
31
- callbacks << "fsmB on_enter(:stopped)"
32
- end
33
- on_enter_started do |event|
34
- callbacks << "fsmB on_enter(:started)"
35
- end
36
- end)
37
-
38
- class Backend
39
- def initialize
40
- @fsmB = FSM_B.new
41
- @fsmB.init
42
- @signal = Mutex.new
43
- end
44
-
45
- def operate
46
- @signal.unlock if @signal.locked?
47
- @worker = Thread.new do
48
- while !@signal.locked? do
49
- sleep 0.01
50
- end
51
- Thread.current.abort_on_exception = true
52
- @fsmB.start
53
- end
54
- end
55
-
56
- def stopit
57
- @signal.lock
58
- @worker.join
59
- end
60
- end
61
-
62
- class Fire
63
- def initialize
64
- @fsmA = FSM_A.new(self)
65
-
66
- @backend = Backend.new
67
- @backend.operate
68
- end
69
-
70
- def fire
71
- @backend.stopit
72
- end
73
-
74
- def operate
75
- #@fsmA.start # should trigger as well
76
- @fsmA.init
77
- end
78
- end
79
-
80
- fire = Fire.new
81
- fire.operate
82
-
83
- expect(callbacks).to match_array([
84
- 'fsmB on_before(init)',
85
- 'fsmB on_enter(:stopped)',
86
- 'fsmA on_before(init)',
87
- 'fsmA on_enter(:green)',
88
- 'fsmA once_on_enter(:green)',
89
- 'fsmB on_before(start)',
90
- 'fsmB on_enter(:started)'
91
- ])
92
- end
93
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine, perf: true do
4
- include RSpec::Benchmark::Matchers
5
-
6
- class Measurement
7
- attr_reader :steps, :loops
8
-
9
- def initialize
10
- @steps = 0
11
- @loops = 0
12
- end
13
-
14
- def inc_step
15
- @steps += 1
16
- end
17
-
18
- def inc_loop
19
- @loops += 1
20
- end
21
- end
22
-
23
- it "correctly loops through events" do
24
- measurement = Measurement.new
25
-
26
- fsm = FiniteMachine.new(measurement) do
27
- initial :green
28
-
29
- event :next, :green => :yellow,
30
- :yellow => :red,
31
- :red => :green
32
-
33
- on_enter do |event| target.inc_step; true end
34
- on_enter :red do |event| target.inc_loop; true end
35
- end
36
-
37
- 100.times { fsm.next }
38
-
39
- expect(measurement.steps).to eq(100)
40
- expect(measurement.loops).to eq(100 / 3)
41
- end
42
-
43
- it "performs at least 400 ips" do
44
- fsm = FiniteMachine.new do
45
- initial :green
46
-
47
- event :next, :green => :yellow,
48
- :yellow => :red,
49
- :red => :green
50
- end
51
-
52
- expect { fsm.next }.to perform_at_least(400).ips
53
- end
54
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- if ENV['COVERAGE'] || ENV['TRAVIS']
4
- require 'simplecov'
5
- require 'coveralls'
6
-
7
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
8
- SimpleCov::Formatter::HTMLFormatter,
9
- Coveralls::SimpleCov::Formatter
10
- ])
11
-
12
- SimpleCov.start do
13
- command_name 'spec'
14
- add_filter 'spec'
15
- end
16
- end
17
-
18
- require 'finite_machine'
19
- require 'thwait'
20
- require 'rspec-benchmark'
21
-
22
- RSpec.configure do |config|
23
- config.run_all_when_everything_filtered = true
24
- config.filter_run :focus
25
- config.raise_errors_for_deprecations!
26
- config.mock_with :rspec do |mocks|
27
- mocks.verify_partial_doubles = true
28
- end
29
- config.disable_monkey_patching!
30
- if config.files_to_run.one?
31
- config.default_formatter = 'doc'
32
- end
33
- config.order = :random
34
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe FiniteMachine::Definition, '#alias_target' do
4
-
5
- before do
6
- stub_const("Car", Class.new do
7
- def turn_reverse_lights_off
8
- @reverse_lights = false
9
- end
10
-
11
- def turn_reverse_lights_on
12
- @reverse_lights = true
13
- end
14
-
15
- def reverse_lights?
16
- @reverse_lights ||= false
17
- end
18
- end)
19
- end
20
-
21
- it "aliases target" do
22
- car = Car.new
23
- fsm = FiniteMachine.new(car, alias_target: :delorean)
24
-
25
- expect(fsm.target).to eq(car)
26
- expect { fsm.car }.to raise_error(NoMethodError)
27
- expect(fsm.delorean).to eq(car)
28
- end
29
-
30
- it "scopes the target alias to a state machine instance" do
31
- delorean = Car.new
32
- batmobile = Car.new
33
- fsm_a = FiniteMachine.new(delorean, alias_target: :delorean)
34
- fsm_b = FiniteMachine.new(batmobile, alias_target: :batmobile)
35
-
36
- expect(fsm_a.delorean).to eq(delorean)
37
- expect { fsm_a.batmobile }.to raise_error(NameError)
38
-
39
- expect(fsm_b.batmobile).to eq(batmobile)
40
- expect { fsm_b.delorean }.to raise_error(NameError)
41
- end
42
-
43
- context 'when inside definition' do
44
- before do
45
- stub_const("Engine", Class.new(FiniteMachine::Definition) do
46
- initial :neutral
47
-
48
- event :forward, [:reverse, :neutral] => :one
49
- event :shift, :one => :two
50
- event :shift, :two => :one
51
- event :back, [:neutral, :one] => :reverse
52
-
53
- on_enter :reverse do |event|
54
- car.turn_reverse_lights_on
55
- end
56
-
57
- on_exit :reverse do |event|
58
- car.turn_reverse_lights_off
59
- end
60
-
61
- handle FiniteMachine::InvalidStateError do |exception| end
62
- end)
63
- end
64
-
65
- it "creates unique instances" do
66
- engine_a = Engine.new(alias_target: :car)
67
- engine_b = Engine.new(alias_target: :car)
68
- expect(engine_a).not_to be(engine_b)
69
-
70
- engine_a.forward
71
- expect(engine_a.current).to eq(:one)
72
- expect(engine_b.current).to eq(:neutral)
73
- end
74
-
75
- it "allows to create standalone machine" do
76
- car = Car.new
77
- engine = Engine.new(car, alias_target: :car)
78
- expect(engine.current).to eq(:neutral)
79
-
80
- engine.forward
81
- expect(engine.current).to eq(:one)
82
- expect(car.reverse_lights?).to be false
83
-
84
- engine.back
85
- expect(engine.current).to eq(:reverse)
86
- expect(car.reverse_lights?).to be true
87
- end
88
- end
89
- end