state_machines 0.0.1
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 +7 -0
- data/.gitignore +21 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/cssxfire.xml +9 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +12 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/state_machine2.iml +34 -0
- data/.idea/vcs.xml +9 -0
- data/.idea/workspace.xml +1156 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/state_machines/assertions.rb +40 -0
- data/lib/state_machines/branch.rb +187 -0
- data/lib/state_machines/callback.rb +220 -0
- data/lib/state_machines/core.rb +25 -0
- data/lib/state_machines/core_ext/class/state_machine.rb +5 -0
- data/lib/state_machines/core_ext.rb +2 -0
- data/lib/state_machines/error.rb +13 -0
- data/lib/state_machines/eval_helpers.rb +87 -0
- data/lib/state_machines/event.rb +246 -0
- data/lib/state_machines/event_collection.rb +141 -0
- data/lib/state_machines/extensions.rb +148 -0
- data/lib/state_machines/helper_module.rb +17 -0
- data/lib/state_machines/integrations/base.rb +100 -0
- data/lib/state_machines/integrations.rb +113 -0
- data/lib/state_machines/machine.rb +2234 -0
- data/lib/state_machines/machine_collection.rb +84 -0
- data/lib/state_machines/macro_methods.rb +520 -0
- data/lib/state_machines/matcher.rb +123 -0
- data/lib/state_machines/matcher_helpers.rb +54 -0
- data/lib/state_machines/node_collection.rb +221 -0
- data/lib/state_machines/path.rb +120 -0
- data/lib/state_machines/path_collection.rb +90 -0
- data/lib/state_machines/state.rb +276 -0
- data/lib/state_machines/state_collection.rb +112 -0
- data/lib/state_machines/state_context.rb +138 -0
- data/lib/state_machines/transition.rb +470 -0
- data/lib/state_machines/transition_collection.rb +245 -0
- data/lib/state_machines/version.rb +3 -0
- data/lib/state_machines/yard.rb +8 -0
- data/lib/state_machines.rb +3 -0
- data/spec/errors/default_spec.rb +14 -0
- data/spec/errors/with_message_spec.rb +39 -0
- data/spec/helpers/helper_spec.rb +14 -0
- data/spec/internal/app/models/auto_shop.rb +31 -0
- data/spec/internal/app/models/car.rb +19 -0
- data/spec/internal/app/models/model_base.rb +6 -0
- data/spec/internal/app/models/motorcycle.rb +9 -0
- data/spec/internal/app/models/traffic_light.rb +47 -0
- data/spec/internal/app/models/vehicle.rb +123 -0
- data/spec/machine_spec.rb +3167 -0
- data/spec/matcher_helpers_spec.rb +39 -0
- data/spec/matcher_spec.rb +157 -0
- data/spec/models/auto_shop_spec.rb +41 -0
- data/spec/models/car_spec.rb +90 -0
- data/spec/models/motorcycle_spec.rb +44 -0
- data/spec/models/traffic_light_spec.rb +56 -0
- data/spec/models/vehicle_spec.rb +580 -0
- data/spec/node_collection_spec.rb +371 -0
- data/spec/path_collection_spec.rb +271 -0
- data/spec/path_spec.rb +488 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/state_collection_spec.rb +352 -0
- data/spec/state_context_spec.rb +442 -0
- data/spec/state_machine_spec.rb +29 -0
- data/spec/state_spec.rb +970 -0
- data/spec/support/migration_helpers.rb +50 -0
- data/spec/support/models.rb +6 -0
- data/spec/transition_collection_spec.rb +2199 -0
- data/spec/transition_spec.rb +1558 -0
- data/state_machines.gemspec +23 -0
- metadata +194 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ErrorByDefault' do
|
4
|
+
let(:klass) { Class.new }
|
5
|
+
let(:machine) { StateMachines::Machine.new(klass) }
|
6
|
+
let(:collection) { StateMachines::NodeCollection.new(machine) }
|
7
|
+
it '#length' do
|
8
|
+
expect(collection.length).to eq(0)
|
9
|
+
end
|
10
|
+
|
11
|
+
it do
|
12
|
+
expect(collection.machine).to eq(machine)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ErrorWithMessage' do
|
4
|
+
let(:klass) { Class.new }
|
5
|
+
let(:machine) { StateMachines::Machine.new(klass) }
|
6
|
+
let(:collection) { StateMachines::NodeCollection.new(machine) }
|
7
|
+
it 'should_raise_exception_if_invalid_option_specified' do
|
8
|
+
expect { StateMachines::NodeCollection.new(machine, invalid: true) }.to raise_error(ArgumentError)
|
9
|
+
# assert_equal 'Invalid key(s): invalid', exception.message
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should_raise_exception_on_lookup_if_invalid_index_specified' do
|
13
|
+
expect { collection[:something, :invalid] }.to raise_error(ArgumentError)
|
14
|
+
# assert_equal 'Invalid index: :invalid', exception.message
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should raise exception on fetch if invalid index specified' do
|
18
|
+
expect { collection[:something, :invalid] }.to raise_error(ArgumentError)
|
19
|
+
# assert_equal 'Invalid index: :invalid', exception.message
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# class ErrorWithMessageTest < Test::Unit::TestCase
|
24
|
+
#
|
25
|
+
# it 'should_raise_exception_if_invalid_option_specified
|
26
|
+
# exception = assert_raise(ArgumentError) { StateMachines::NodeCollection.new(@machine, :invalid => true) }
|
27
|
+
# assert_equal 'Invalid key(s): invalid', exception.message
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# it 'should_raise_exception_on_lookup_if_invalid_index_specified
|
31
|
+
# exception = assert_raise(ArgumentError) { @collection[:something, :invalid] }
|
32
|
+
# assert_equal 'Invalid index: :invalid', exception.message
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# it 'should_raise_exception_on_fetch_if_invalid_index_specified
|
36
|
+
# exception = assert_raise(ArgumentError) { @collection.fetch(:something, :invalid) }
|
37
|
+
# assert_equal 'Invalid index: :invalid', exception.message
|
38
|
+
# end
|
39
|
+
# end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StateMachines::HelperModule do
|
4
|
+
let(:klass) { Class.new }
|
5
|
+
let(:machine) { StateMachines::Machine.new(klass) }
|
6
|
+
let(:helper_module) { StateMachines::HelperModule.new(machine, :instance) }
|
7
|
+
it 'should not have a name' do
|
8
|
+
expect(helper_module.name.to_s).to eq('')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should_provide_human_readable_to_s' do
|
12
|
+
expect(helper_module.to_s).to eq("#{klass} :state instance helpers")
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class AutoShop
|
2
|
+
attr_accessor :num_customers
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@num_customers = 0
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
state_machine initial: :available do
|
10
|
+
after_transition available: any, do: :increment_customers
|
11
|
+
after_transition busy: any, do: :decrement_customers
|
12
|
+
|
13
|
+
event :tow_vehicle do
|
14
|
+
transition available: :busy
|
15
|
+
end
|
16
|
+
|
17
|
+
event :fix_vehicle do
|
18
|
+
transition busy: :available
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Increments the number of customers in service
|
23
|
+
def increment_customers
|
24
|
+
self.num_customers += 1
|
25
|
+
end
|
26
|
+
|
27
|
+
# Decrements the number of customers in service
|
28
|
+
def decrement_customers
|
29
|
+
self.num_customers -= 1
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Car < Vehicle
|
2
|
+
state_machine do
|
3
|
+
event :reverse do
|
4
|
+
transition [:parked, :idling, :first_gear] => :backing_up
|
5
|
+
end
|
6
|
+
|
7
|
+
event :park do
|
8
|
+
transition backing_up: :parked
|
9
|
+
end
|
10
|
+
|
11
|
+
event :idle do
|
12
|
+
transition backing_up: :idling
|
13
|
+
end
|
14
|
+
|
15
|
+
event :shift_up do
|
16
|
+
transition backing_up: :first_gear
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class TrafficLight
|
2
|
+
state_machine initial: :stop do
|
3
|
+
event :cycle do
|
4
|
+
transition stop: :proceed, proceed: :caution, caution: :stop
|
5
|
+
end
|
6
|
+
|
7
|
+
state :stop do
|
8
|
+
def color(transform)
|
9
|
+
value = 'red'
|
10
|
+
|
11
|
+
if block_given?
|
12
|
+
yield value
|
13
|
+
else
|
14
|
+
value.send(transform)
|
15
|
+
end
|
16
|
+
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
state all - :proceed do
|
22
|
+
def capture_violations?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
state :proceed do
|
28
|
+
def color(transform)
|
29
|
+
'green'
|
30
|
+
end
|
31
|
+
|
32
|
+
def capture_violations?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
state :caution do
|
38
|
+
def color(transform)
|
39
|
+
'yellow'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def color(transform = :to_s)
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
class Vehicle < ModelBase
|
2
|
+
attr_accessor :auto_shop, :seatbelt_on, :insurance_premium, :force_idle, :callbacks, :saved, :time_elapsed, :last_transition_args
|
3
|
+
|
4
|
+
def initialize(attributes = {})
|
5
|
+
attributes = {
|
6
|
+
auto_shop: AutoShop.new,
|
7
|
+
seatbelt_on: false,
|
8
|
+
insurance_premium: 50,
|
9
|
+
force_idle: false,
|
10
|
+
callbacks: [],
|
11
|
+
saved: false
|
12
|
+
}.merge(attributes)
|
13
|
+
|
14
|
+
attributes.each { |attr, value| send("#{attr}=", value) }
|
15
|
+
super()
|
16
|
+
end
|
17
|
+
|
18
|
+
# Defines the state machine for the state of the vehicled
|
19
|
+
state_machine initial: lambda { |vehicle| vehicle.force_idle ? :idling : :parked }, action: :save do
|
20
|
+
before_transition { |vehicle, transition| vehicle.last_transition_args = transition.args }
|
21
|
+
before_transition parked: any, do: :put_on_seatbelt
|
22
|
+
before_transition any => :stalled, :do => :increase_insurance_premium
|
23
|
+
after_transition any => :parked, :do => lambda { |vehicle| vehicle.seatbelt_on = false }
|
24
|
+
after_transition on: :crash, do: :tow
|
25
|
+
after_transition on: :repair, do: :fix
|
26
|
+
|
27
|
+
# Callback tracking for initial state callbacks
|
28
|
+
after_transition any => :parked, :do => lambda { |vehicle| vehicle.callbacks << 'before_enter_parked' }
|
29
|
+
before_transition any => :idling, :do => lambda { |vehicle| vehicle.callbacks << 'before_enter_idling' }
|
30
|
+
|
31
|
+
around_transition do |vehicle, transition, block|
|
32
|
+
time = Time.now
|
33
|
+
block.call
|
34
|
+
vehicle.time_elapsed = Time.now - time
|
35
|
+
end
|
36
|
+
|
37
|
+
event all do
|
38
|
+
transition locked: :parked
|
39
|
+
end
|
40
|
+
|
41
|
+
event :park do
|
42
|
+
transition [:idling, :first_gear] => :parked
|
43
|
+
end
|
44
|
+
|
45
|
+
event :ignite do
|
46
|
+
transition stalled: :stalled
|
47
|
+
transition parked: :idling
|
48
|
+
end
|
49
|
+
|
50
|
+
event :idle do
|
51
|
+
transition first_gear: :idling
|
52
|
+
end
|
53
|
+
|
54
|
+
event :shift_up do
|
55
|
+
transition idling: :first_gear, first_gear: :second_gear, second_gear: :third_gear
|
56
|
+
end
|
57
|
+
|
58
|
+
event :shift_down do
|
59
|
+
transition third_gear: :second_gear
|
60
|
+
transition second_gear: :first_gear
|
61
|
+
end
|
62
|
+
|
63
|
+
event :crash do
|
64
|
+
transition [:first_gear, :second_gear, :third_gear] => :stalled, :if => lambda { |vehicle| vehicle.auto_shop.available? }
|
65
|
+
end
|
66
|
+
|
67
|
+
event :repair do
|
68
|
+
transition stalled: :parked, if: :auto_shop_busy?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
state_machine :insurance_state, initial: :inactive, namespace: 'insurance' do
|
73
|
+
event :buy do
|
74
|
+
transition inactive: :active
|
75
|
+
end
|
76
|
+
|
77
|
+
event :cancel do
|
78
|
+
transition active: :inactive
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def save
|
83
|
+
super
|
84
|
+
end
|
85
|
+
|
86
|
+
def new_record?
|
87
|
+
@saved == false
|
88
|
+
end
|
89
|
+
|
90
|
+
def park
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
# Tows the vehicle to the auto shop
|
95
|
+
def tow
|
96
|
+
auto_shop.tow_vehicle
|
97
|
+
end
|
98
|
+
|
99
|
+
# Fixes the vehicle; it will no longer be in the auto shop
|
100
|
+
def fix
|
101
|
+
auto_shop.fix_vehicle
|
102
|
+
end
|
103
|
+
|
104
|
+
def decibels
|
105
|
+
0.0
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
# Safety first! Puts on our seatbelt
|
110
|
+
def put_on_seatbelt
|
111
|
+
self.seatbelt_on = true
|
112
|
+
end
|
113
|
+
|
114
|
+
# We crashed! Increase the insurance premium on the vehicle
|
115
|
+
def increase_insurance_premium
|
116
|
+
self.insurance_premium += 100
|
117
|
+
end
|
118
|
+
|
119
|
+
# Is the auto shop currently servicing another customer?
|
120
|
+
def auto_shop_busy?
|
121
|
+
auto_shop.busy?
|
122
|
+
end
|
123
|
+
end
|