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.
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,442 @@
1
+ require 'spec_helper'
2
+
3
+ class Validateable
4
+ class << self
5
+ def validate(*args, &block)
6
+ args << block if block_given?
7
+ args
8
+ end
9
+ end
10
+ end
11
+ describe StateMachines::StateContext do
12
+ context '' do
13
+ before(:each) do
14
+ @klass = Class.new(Validateable)
15
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
16
+ @state = @machine.state :parked
17
+
18
+ @state_context = StateMachines::StateContext.new(@state)
19
+ end
20
+
21
+ it 'should_have_a_machine' do
22
+ assert_equal @machine, @state_context.machine
23
+ end
24
+
25
+ it 'should_have_a_state' do
26
+ assert_equal @state, @state_context.state
27
+ end
28
+ end
29
+
30
+ context 'Transition' do
31
+ before(:each) do
32
+ @klass = Class.new
33
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
34
+ @state = @machine.state :parked
35
+
36
+ @state_context = StateMachines::StateContext.new(@state)
37
+ end
38
+
39
+ it 'should_not_allow_except_to' do
40
+ assert_raise(ArgumentError) { @state_context.transition(:except_to => :idling) }
41
+ # assert_equal 'Invalid key(s): except_to', exception.message
42
+ end
43
+
44
+ it 'should_not_allow_except_from' do
45
+ assert_raise(ArgumentError) { @state_context.transition(:except_from => :idling) }
46
+ #assert_equal 'Invalid key(s): except_from', exception.message
47
+ end
48
+
49
+ it 'should_not_allow_implicit_transitions' do
50
+ assert_raise(ArgumentError) { @state_context.transition(:parked => :idling) }
51
+ #assert_equal 'Invalid key(s): parked', exception.message
52
+ end
53
+
54
+ it 'should_not_allow_except_on' do
55
+ assert_raise(ArgumentError) { @state_context.transition(:except_on => :park) }
56
+ #assert_equal 'Invalid key(s): except_on', exception.message
57
+ end
58
+
59
+ it 'should_require_on_event' do
60
+ assert_raise(ArgumentError) { @state_context.transition(:to => :idling) }
61
+ #assert_equal 'Must specify :on event', exception.message
62
+ end
63
+
64
+ it 'should_not_allow_missing_from_and_to' do
65
+ assert_raise(ArgumentError) { @state_context.transition(:on => :ignite) }
66
+ #assert_equal 'Must specify either :to or :from state', exception.message
67
+ end
68
+
69
+ it 'should_not_allow_from_and_to' do
70
+ assert_raise(ArgumentError) { @state_context.transition(:on => :ignite, :from => :parked, :to => :idling) }
71
+ #assert_equal 'Must specify either :to or :from state', exception.message
72
+ end
73
+
74
+ it 'should_allow_to_state_if_missing_from_state' do
75
+ assert_nothing_raised { @state_context.transition(:on => :park, :from => :parked) }
76
+ end
77
+
78
+ it 'should_allow_from_state_if_missing_to_state' do
79
+ assert_nothing_raised { @state_context.transition(:on => :ignite, :to => :idling) }
80
+ end
81
+
82
+ it 'should_automatically_set_to_option_with_from_state' do
83
+ branch = @state_context.transition(:from => :idling, :on => :park)
84
+ assert_instance_of StateMachines::Branch, branch
85
+
86
+ state_requirements = branch.state_requirements
87
+ assert_equal 1, state_requirements.length
88
+
89
+ from_requirement = state_requirements[0][:to]
90
+ assert_instance_of StateMachines::WhitelistMatcher, from_requirement
91
+ assert_equal [:parked], from_requirement.values
92
+ end
93
+
94
+ it 'should_automatically_set_from_option_with_to_state' do
95
+ branch = @state_context.transition(:to => :idling, :on => :ignite)
96
+ assert_instance_of StateMachines::Branch, branch
97
+
98
+ state_requirements = branch.state_requirements
99
+ assert_equal 1, state_requirements.length
100
+
101
+ from_requirement = state_requirements[0][:from]
102
+ assert_instance_of StateMachines::WhitelistMatcher, from_requirement
103
+ assert_equal [:parked], from_requirement.values
104
+ end
105
+
106
+ it 'should_allow_if_condition' do
107
+ assert_nothing_raised { @state_context.transition(:to => :idling, :on => :park, :if => :seatbelt_on?) }
108
+ end
109
+
110
+ it 'should_allow_unless_condition' do
111
+ assert_nothing_raised { @state_context.transition(:to => :idling, :on => :park, :unless => :seatbelt_off?) }
112
+ end
113
+
114
+ it 'should_include_all_transition_states_in_machine_states' do
115
+ @state_context.transition(:to => :idling, :on => :ignite)
116
+
117
+ assert_equal [:parked, :idling], @machine.states.map { |state| state.name }
118
+ end
119
+
120
+ it 'should_include_all_transition_events_in_machine_events' do
121
+ @state_context.transition(:to => :idling, :on => :ignite)
122
+
123
+ assert_equal [:ignite], @machine.events.map { |event| event.name }
124
+ end
125
+
126
+ it 'should_allow_multiple_events' do
127
+ @state_context.transition(:to => :idling, :on => [:ignite, :shift_up])
128
+
129
+ assert_equal [:ignite, :shift_up], @machine.events.map { |event| event.name }
130
+ end
131
+ end
132
+
133
+ context 'WithMatchingTransition' do
134
+ before(:each) do
135
+ @klass = Class.new
136
+ @machine = StateMachines::Machine.new(@klass, :initial => :parked)
137
+ @state = @machine.state :parked
138
+
139
+ @state_context = StateMachines::StateContext.new(@state)
140
+ @state_context.transition(:to => :idling, :on => :ignite)
141
+
142
+ @event = @machine.event(:ignite)
143
+ @object = @klass.new
144
+ end
145
+
146
+ it 'should_be_able_to_fire' do
147
+ assert @event.can_fire?(@object)
148
+ end
149
+
150
+ it 'should_have_a_transition' do
151
+ transition = @event.transition_for(@object)
152
+ assert_not_nil transition
153
+ assert_equal 'parked', transition.from
154
+ assert_equal 'idling', transition.to
155
+ assert_equal :ignite, transition.event
156
+ end
157
+ end
158
+
159
+ context 'Proxy' do
160
+ before(:each) do
161
+ @klass = Class.new(Validateable)
162
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
163
+ state = machine.state :parked
164
+
165
+ @state_context = StateMachines::StateContext.new(state)
166
+ end
167
+
168
+ it 'should_call_class_with_same_arguments' do
169
+ options = {}
170
+ validation = @state_context.validate(:name, options)
171
+
172
+ assert_equal [:name, options], validation
173
+ end
174
+
175
+ it 'should_pass_block_through_to_class' do
176
+ options = {}
177
+ proxy_block = lambda {}
178
+ validation = @state_context.validate(:name, options, &proxy_block)
179
+
180
+ assert_equal [:name, options, proxy_block], validation
181
+ end
182
+ end
183
+
184
+ context 'ProxyWithoutConditions' do
185
+ before(:each) do
186
+ @klass = Class.new(Validateable)
187
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
188
+ state = machine.state :parked
189
+
190
+ @state_context = StateMachines::StateContext.new(state)
191
+ @object = @klass.new
192
+
193
+ @options = @state_context.validate[0]
194
+ end
195
+
196
+ it 'should_have_options_configuration' do
197
+ assert_instance_of Hash, @options
198
+ end
199
+
200
+ it 'should_have_if_option' do
201
+ assert_not_nil @options[:if]
202
+ end
203
+
204
+ it 'should_be_false_if_state_is_different' do
205
+ @object.state = nil
206
+ assert !@options[:if].call(@object)
207
+ end
208
+
209
+ it 'should_be_true_if_state_matches' do
210
+ assert @options[:if].call(@object)
211
+ end
212
+ end
213
+
214
+ context 'ProxyWithIfCondition' do
215
+ before(:each) do
216
+ @klass = Class.new(Validateable)
217
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
218
+ state = machine.state :parked
219
+
220
+ @state_context = StateMachines::StateContext.new(state)
221
+ @object = @klass.new
222
+
223
+ @condition_result = nil
224
+ @options = @state_context.validate(:if => lambda { @condition_result })[0]
225
+ end
226
+
227
+ it 'should_have_if_option' do
228
+ assert_not_nil @options[:if]
229
+ end
230
+
231
+ it 'should_be_false_if_state_is_different' do
232
+ @object.state = nil
233
+ assert !@options[:if].call(@object)
234
+ end
235
+
236
+ it 'should_be_false_if_original_condition_is_false' do
237
+ @condition_result = false
238
+ assert !@options[:if].call(@object)
239
+ end
240
+
241
+ it 'should_be_true_if_state_matches_and_original_condition_is_true' do
242
+ @condition_result = true
243
+ assert @options[:if].call(@object)
244
+ end
245
+
246
+ it 'should_evaluate_symbol_condition' do
247
+ @klass.class_eval do
248
+ attr_accessor :callback
249
+ end
250
+
251
+ options = @state_context.validate(:if => :callback)[0]
252
+
253
+ object = @klass.new
254
+ object.callback = false
255
+ assert !options[:if].call(object)
256
+
257
+ object.callback = true
258
+ assert options[:if].call(object)
259
+ end
260
+
261
+ it 'should_evaluate_string_condition' do
262
+ @klass.class_eval do
263
+ attr_accessor :callback
264
+ end
265
+
266
+ options = @state_context.validate(:if => '@callback')[0]
267
+
268
+ object = @klass.new
269
+ object.callback = false
270
+ assert !options[:if].call(object)
271
+
272
+ object.callback = true
273
+ assert options[:if].call(object)
274
+ end
275
+ end
276
+
277
+ context 'ProxyWithMultipleIfConditions' do
278
+ before(:each) do
279
+ @klass = Class.new(Validateable)
280
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
281
+ state = machine.state :parked
282
+
283
+ @state_context = StateMachines::StateContext.new(state)
284
+ @object = @klass.new
285
+
286
+ @first_condition_result = nil
287
+ @second_condition_result = nil
288
+ @options = @state_context.validate(:if => [lambda { @first_condition_result }, lambda { @second_condition_result }])[0]
289
+ end
290
+
291
+ it 'should_be_true_if_all_conditions_are_true' do
292
+ @first_condition_result = true
293
+ @second_condition_result = true
294
+ assert @options[:if].call(@object)
295
+ end
296
+
297
+ it 'should_be_false_if_any_condition_is_false' do
298
+ @first_condition_result = true
299
+ @second_condition_result = false
300
+ assert !@options[:if].call(@object)
301
+
302
+ @first_condition_result = false
303
+ @second_condition_result = true
304
+ assert !@options[:if].call(@object)
305
+ end
306
+ end
307
+
308
+ context 'ProxyWithUnlessCondition' do
309
+ before(:each) do
310
+ @klass = Class.new(Validateable)
311
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
312
+ state = machine.state :parked
313
+
314
+ @state_context = StateMachines::StateContext.new(state)
315
+ @object = @klass.new
316
+
317
+ @condition_result = nil
318
+ @options = @state_context.validate(:unless => lambda { @condition_result })[0]
319
+ end
320
+
321
+ it 'should_have_if_option' do
322
+ assert_not_nil @options[:if]
323
+ end
324
+
325
+ it 'should_be_false_if_state_is_different' do
326
+ @object.state = nil
327
+ assert !@options[:if].call(@object)
328
+ end
329
+
330
+ it 'should_be_false_if_original_condition_is_true' do
331
+ @condition_result = true
332
+ assert !@options[:if].call(@object)
333
+ end
334
+
335
+ it 'should_be_true_if_state_matches_and_original_condition_is_false' do
336
+ @condition_result = false
337
+ assert @options[:if].call(@object)
338
+ end
339
+
340
+ it 'should_evaluate_symbol_condition' do
341
+ @klass.class_eval do
342
+ attr_accessor :callback
343
+ end
344
+
345
+ options = @state_context.validate(:unless => :callback)[0]
346
+
347
+ object = @klass.new
348
+ object.callback = true
349
+ assert !options[:if].call(object)
350
+
351
+ object.callback = false
352
+ assert options[:if].call(object)
353
+ end
354
+
355
+ it 'should_evaluate_string_condition' do
356
+ @klass.class_eval do
357
+ attr_accessor :callback
358
+ end
359
+
360
+ options = @state_context.validate(:unless => '@callback')[0]
361
+
362
+ object = @klass.new
363
+ object.callback = true
364
+ assert !options[:if].call(object)
365
+
366
+ object.callback = false
367
+ assert options[:if].call(object)
368
+ end
369
+ end
370
+
371
+ context 'ProxyWithMultipleUnlessConditions' do
372
+ before(:each) do
373
+ @klass = Class.new(Validateable)
374
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
375
+ state = machine.state :parked
376
+
377
+ @state_context = StateMachines::StateContext.new(state)
378
+ @object = @klass.new
379
+
380
+ @first_condition_result = nil
381
+ @second_condition_result = nil
382
+ @options = @state_context.validate(:unless => [lambda { @first_condition_result }, lambda { @second_condition_result }])[0]
383
+ end
384
+
385
+ it 'should_be_true_if_all_conditions_are_false' do
386
+ @first_condition_result = false
387
+ @second_condition_result = false
388
+ assert @options[:if].call(@object)
389
+ end
390
+
391
+ it 'should_be_false_if_any_condition_is_true' do
392
+ @first_condition_result = true
393
+ @second_condition_result = false
394
+ assert !@options[:if].call(@object)
395
+
396
+ @first_condition_result = false
397
+ @second_condition_result = true
398
+ assert !@options[:if].call(@object)
399
+ end
400
+ end
401
+
402
+ context 'ProxyWithIfAndUnlessConditions' do
403
+ before(:each) do
404
+ @klass = Class.new(Validateable)
405
+ machine = StateMachines::Machine.new(@klass, :initial => :parked)
406
+ state = machine.state :parked
407
+
408
+ @state_context = StateMachines::StateContext.new(state)
409
+ @object = @klass.new
410
+
411
+ @if_condition_result = nil
412
+ @unless_condition_result = nil
413
+ @options = @state_context.validate(:if => lambda { @if_condition_result }, :unless => lambda { @unless_condition_result })[0]
414
+ end
415
+
416
+ it 'should_be_false_if_if_condition_is_false' do
417
+ @if_condition_result = false
418
+ @unless_condition_result = false
419
+ assert !@options[:if].call(@object)
420
+
421
+ @if_condition_result = false
422
+ @unless_condition_result = true
423
+ assert !@options[:if].call(@object)
424
+ end
425
+
426
+ it 'should_be_false_if_unless_condition_is_true' do
427
+ @if_condition_result = false
428
+ @unless_condition_result = true
429
+ assert !@options[:if].call(@object)
430
+
431
+ @if_condition_result = true
432
+ @unless_condition_result = true
433
+ assert !@options[:if].call(@object)
434
+ end
435
+
436
+ it 'should_be_true_if_if_condition_is_true_and_unless_condition_is_false' do
437
+ @if_condition_result = true
438
+ @unless_condition_result = false
439
+ assert @options[:if].call(@object)
440
+ end
441
+ end
442
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ describe 'StateMachines' do
3
+ context 'ByDefault' do
4
+ before(:each) do
5
+ @klass = Class.new
6
+ @machine = @klass.state_machine
7
+ end
8
+
9
+ it 'should_use_state_attribute' do
10
+ assert_equal :state, @machine.attribute
11
+ end
12
+ end
13
+
14
+ context '' do
15
+ let(:klass) { Class.new }
16
+
17
+ it 'should_allow_state_machines_on_any_class' do
18
+ expect(klass.respond_to?(:state_machine)).to be_truthy
19
+ end
20
+
21
+ it 'should_evaluate_block_within_machine_context' do
22
+ responded = false
23
+ klass.state_machine(:state) do
24
+ responded = respond_to?(:event)
25
+ end
26
+ expect(responded).to be_truthy
27
+ end
28
+ end
29
+ end