finite_machine 0.12.1 → 0.13.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 (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