state_machines 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.idea/.name +1 -0
  4. data/.idea/.rakeTasks +7 -0
  5. data/.idea/cssxfire.xml +9 -0
  6. data/.idea/encodings.xml +5 -0
  7. data/.idea/misc.xml +5 -0
  8. data/.idea/modules.xml +12 -0
  9. data/.idea/scopes/scope_settings.xml +5 -0
  10. data/.idea/state_machine2.iml +34 -0
  11. data/.idea/vcs.xml +9 -0
  12. data/.idea/workspace.xml +1156 -0
  13. data/.rspec +3 -0
  14. data/.travis.yml +8 -0
  15. data/Gemfile +4 -0
  16. data/LICENSE.txt +23 -0
  17. data/README.md +29 -0
  18. data/Rakefile +1 -0
  19. data/lib/state_machines/assertions.rb +40 -0
  20. data/lib/state_machines/branch.rb +187 -0
  21. data/lib/state_machines/callback.rb +220 -0
  22. data/lib/state_machines/core.rb +25 -0
  23. data/lib/state_machines/core_ext/class/state_machine.rb +5 -0
  24. data/lib/state_machines/core_ext.rb +2 -0
  25. data/lib/state_machines/error.rb +13 -0
  26. data/lib/state_machines/eval_helpers.rb +87 -0
  27. data/lib/state_machines/event.rb +246 -0
  28. data/lib/state_machines/event_collection.rb +141 -0
  29. data/lib/state_machines/extensions.rb +148 -0
  30. data/lib/state_machines/helper_module.rb +17 -0
  31. data/lib/state_machines/integrations/base.rb +100 -0
  32. data/lib/state_machines/integrations.rb +113 -0
  33. data/lib/state_machines/machine.rb +2234 -0
  34. data/lib/state_machines/machine_collection.rb +84 -0
  35. data/lib/state_machines/macro_methods.rb +520 -0
  36. data/lib/state_machines/matcher.rb +123 -0
  37. data/lib/state_machines/matcher_helpers.rb +54 -0
  38. data/lib/state_machines/node_collection.rb +221 -0
  39. data/lib/state_machines/path.rb +120 -0
  40. data/lib/state_machines/path_collection.rb +90 -0
  41. data/lib/state_machines/state.rb +276 -0
  42. data/lib/state_machines/state_collection.rb +112 -0
  43. data/lib/state_machines/state_context.rb +138 -0
  44. data/lib/state_machines/transition.rb +470 -0
  45. data/lib/state_machines/transition_collection.rb +245 -0
  46. data/lib/state_machines/version.rb +3 -0
  47. data/lib/state_machines/yard.rb +8 -0
  48. data/lib/state_machines.rb +3 -0
  49. data/spec/errors/default_spec.rb +14 -0
  50. data/spec/errors/with_message_spec.rb +39 -0
  51. data/spec/helpers/helper_spec.rb +14 -0
  52. data/spec/internal/app/models/auto_shop.rb +31 -0
  53. data/spec/internal/app/models/car.rb +19 -0
  54. data/spec/internal/app/models/model_base.rb +6 -0
  55. data/spec/internal/app/models/motorcycle.rb +9 -0
  56. data/spec/internal/app/models/traffic_light.rb +47 -0
  57. data/spec/internal/app/models/vehicle.rb +123 -0
  58. data/spec/machine_spec.rb +3167 -0
  59. data/spec/matcher_helpers_spec.rb +39 -0
  60. data/spec/matcher_spec.rb +157 -0
  61. data/spec/models/auto_shop_spec.rb +41 -0
  62. data/spec/models/car_spec.rb +90 -0
  63. data/spec/models/motorcycle_spec.rb +44 -0
  64. data/spec/models/traffic_light_spec.rb +56 -0
  65. data/spec/models/vehicle_spec.rb +580 -0
  66. data/spec/node_collection_spec.rb +371 -0
  67. data/spec/path_collection_spec.rb +271 -0
  68. data/spec/path_spec.rb +488 -0
  69. data/spec/spec_helper.rb +6 -0
  70. data/spec/state_collection_spec.rb +352 -0
  71. data/spec/state_context_spec.rb +442 -0
  72. data/spec/state_machine_spec.rb +29 -0
  73. data/spec/state_spec.rb +970 -0
  74. data/spec/support/migration_helpers.rb +50 -0
  75. data/spec/support/models.rb +6 -0
  76. data/spec/transition_collection_spec.rb +2199 -0
  77. data/spec/transition_spec.rb +1558 -0
  78. data/state_machines.gemspec +23 -0
  79. metadata +194 -0
@@ -0,0 +1,3 @@
1
+ module StateMachines
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,8 @@
1
+ module StateMachines
2
+ # YARD plugin for automated documentation
3
+ module YARD
4
+ end
5
+ end
6
+
7
+ require 'state_machines/yard/handlers'
8
+ require 'state_machines/yard/templates'
@@ -0,0 +1,3 @@
1
+ require 'state_machines/version'
2
+ require 'state_machines/core'
3
+ require 'state_machines/core_ext'
@@ -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,6 @@
1
+ class ModelBase
2
+ def save
3
+ @saved = true
4
+ self
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ class Motorcycle < Vehicle
2
+ state_machine initial: :idling do
3
+ state :first_gear do
4
+ def decibels
5
+ 1.0
6
+ end
7
+ end
8
+ end
9
+ 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