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,970 @@
1
+ require 'spec_helper'
2
+ describe StateMachines::State do
3
+ context 'Drawing' do
4
+ it 'should raise NotImplementedError' do
5
+ machine = StateMachines::Machine.new(Class.new)
6
+ state = StateMachines::State.new(machine, :parked)
7
+ expect { state.draw(:foo) }.to raise_error(NotImplementedError)
8
+ end
9
+ end
10
+
11
+ context 'ByDefault' do
12
+ before(:each) do
13
+ @machine = StateMachines::Machine.new(Class.new)
14
+ @machine.states << @state = StateMachines::State.new(@machine, :parked)
15
+ end
16
+
17
+ it 'should_have_a_machine' do
18
+ assert_equal @machine, @state.machine
19
+ end
20
+
21
+ it 'should_have_a_name' do
22
+ assert_equal :parked, @state.name
23
+ end
24
+
25
+ it 'should_have_a_qualified_name' do
26
+ assert_equal :parked, @state.qualified_name
27
+ end
28
+
29
+ it 'should_have_a_human_name' do
30
+ assert_equal 'parked', @state.human_name
31
+ end
32
+
33
+ it 'should_use_stringify_the_name_as_the_value' do
34
+ assert_equal 'parked', @state.value
35
+ end
36
+
37
+ it 'should_not_be_initial' do
38
+ assert !@state.initial
39
+ end
40
+
41
+ it 'should_not_have_a_matcher' do
42
+ assert_nil @state.matcher
43
+ end
44
+
45
+ it 'should_not_have_any_methods' do
46
+ expected = {}
47
+ assert_equal expected, @state.context_methods
48
+ end
49
+ end
50
+
51
+ context '' do
52
+ before(:each) do
53
+ @machine = StateMachines::Machine.new(Class.new)
54
+ @machine.states << @state = StateMachines::State.new(@machine, :parked)
55
+ end
56
+
57
+ it 'should_raise_exception_if_invalid_option_specified' do
58
+ assert_raise(ArgumentError) { StateMachines::State.new(@machine, :parked, :invalid => true) }
59
+ # FIXME
60
+ # assert_equal 'Invalid key(s): invalid', exception.message
61
+ end
62
+
63
+ it 'should_allow_changing_machine' do
64
+ new_machine = StateMachines::Machine.new(Class.new)
65
+ @state.machine = new_machine
66
+ assert_equal new_machine, @state.machine
67
+ end
68
+
69
+ it 'should_allow_changing_value' do
70
+ @state.value = 1
71
+ assert_equal 1, @state.value
72
+ end
73
+
74
+ it 'should_allow_changing_initial' do
75
+ @state.initial = true
76
+ assert @state.initial
77
+ end
78
+
79
+ it 'should_allow_changing_matcher' do
80
+ matcher = lambda {}
81
+ @state.matcher = matcher
82
+ assert_equal matcher, @state.matcher
83
+ end
84
+
85
+ it 'should_allow_changing_human_name' do
86
+ @state.human_name = 'stopped'
87
+ assert_equal 'stopped', @state.human_name
88
+ end
89
+
90
+ it 'should_use_pretty_inspect' do
91
+ assert_equal '#<StateMachines::State name=:parked value="parked" initial=false>', @state.inspect
92
+ end
93
+ end
94
+
95
+ context 'WithoutName' do
96
+ before(:each) do
97
+ @klass = Class.new
98
+ @machine = StateMachines::Machine.new(@klass)
99
+ @machine.states << @state = StateMachines::State.new(@machine, nil)
100
+ end
101
+
102
+ it 'should_have_a_nil_name' do
103
+ assert_nil @state.name
104
+ end
105
+
106
+ it 'should_have_a_nil_qualified_name' do
107
+ assert_nil @state.qualified_name
108
+ end
109
+
110
+ it 'should_have_an_empty_human_name' do
111
+ assert_equal 'nil', @state.human_name
112
+ end
113
+
114
+ it 'should_have_a_nil_value' do
115
+ assert_nil @state.value
116
+ end
117
+
118
+ it 'should_not_redefine_nil_predicate' do
119
+ object = @klass.new
120
+ assert !object.nil?
121
+ assert !object.respond_to?('?')
122
+ end
123
+
124
+ it 'should_have_a_description' do
125
+ assert_equal 'nil', @state.description
126
+ end
127
+
128
+ it 'should_have_a_description_using_human_name' do
129
+ assert_equal 'nil', @state.description(:human_name => true)
130
+ end
131
+ end
132
+
133
+ context 'WithName' do
134
+ before(:each) do
135
+ @klass = Class.new
136
+ @machine = StateMachines::Machine.new(@klass)
137
+ @machine.states << @state = StateMachines::State.new(@machine, :parked)
138
+ end
139
+
140
+ it 'should_have_a_name' do
141
+ assert_equal :parked, @state.name
142
+ end
143
+
144
+ it 'should_have_a_qualified_name' do
145
+ assert_equal :parked, @state.name
146
+ end
147
+
148
+ it 'should_have_a_human_name' do
149
+ assert_equal 'parked', @state.human_name
150
+ end
151
+
152
+ it 'should_use_stringify_the_name_as_the_value' do
153
+ assert_equal 'parked', @state.value
154
+ end
155
+
156
+ it 'should_match_stringified_name' do
157
+ assert @state.matches?('parked')
158
+ assert !@state.matches?('idling')
159
+ end
160
+
161
+ it 'should_not_include_value_in_description' do
162
+ assert_equal 'parked', @state.description
163
+ end
164
+
165
+ it 'should_allow_using_human_name_in_description' do
166
+ @state.human_name = 'Parked'
167
+ assert_equal 'Parked', @state.description(:human_name => true)
168
+ end
169
+
170
+ it 'should_define_predicate' do
171
+ assert @klass.new.respond_to?(:parked?)
172
+ end
173
+ end
174
+
175
+ context 'WithNilValue' do
176
+ before(:each) do
177
+ @klass = Class.new
178
+ @machine = StateMachines::Machine.new(@klass)
179
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :value => nil)
180
+ end
181
+
182
+ it 'should_have_a_name' do
183
+ assert_equal :parked, @state.name
184
+ end
185
+
186
+ it 'should_have_a_nil_value' do
187
+ assert_nil @state.value
188
+ end
189
+
190
+ it 'should_match_nil_values' do
191
+ assert @state.matches?(nil)
192
+ end
193
+
194
+ it 'should_have_a_description' do
195
+ assert_equal 'parked (nil)', @state.description
196
+ end
197
+
198
+ it 'should_have_a_description_with_human_name' do
199
+ @state.human_name = 'Parked'
200
+ assert_equal 'Parked (nil)', @state.description(:human_name => true)
201
+ end
202
+
203
+ it 'should_define_predicate' do
204
+ object = @klass.new
205
+ assert object.respond_to?(:parked?)
206
+ end
207
+ end
208
+
209
+ context 'WithSymbolicValue' do
210
+ before(:each) do
211
+ @klass = Class.new
212
+ @machine = StateMachines::Machine.new(@klass)
213
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :value => :parked)
214
+ end
215
+
216
+ it 'should_use_custom_value' do
217
+ assert_equal :parked, @state.value
218
+ end
219
+
220
+ it 'should_not_include_value_in_description' do
221
+ assert_equal 'parked', @state.description
222
+ end
223
+
224
+ it 'should_allow_human_name_in_description' do
225
+ @state.human_name = 'Parked'
226
+ assert_equal 'Parked', @state.description(:human_name => true)
227
+ end
228
+
229
+ it 'should_match_symbolic_value' do
230
+ assert @state.matches?(:parked)
231
+ assert !@state.matches?('parked')
232
+ end
233
+
234
+ it 'should_define_predicate' do
235
+ object = @klass.new
236
+ assert object.respond_to?(:parked?)
237
+ end
238
+ end
239
+
240
+ context 'WithIntegerValue' do
241
+ before(:each) do
242
+ @klass = Class.new
243
+ @machine = StateMachines::Machine.new(@klass)
244
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :value => 1)
245
+ end
246
+
247
+ it 'should_use_custom_value' do
248
+ assert_equal 1, @state.value
249
+ end
250
+
251
+ it 'should_include_value_in_description' do
252
+ assert_equal 'parked (1)', @state.description
253
+ end
254
+
255
+ it 'should_allow_human_name_in_description' do
256
+ @state.human_name = 'Parked'
257
+ assert_equal 'Parked (1)', @state.description(:human_name => true)
258
+ end
259
+
260
+ it 'should_match_integer_value' do
261
+ assert @state.matches?(1)
262
+ assert !@state.matches?(2)
263
+ end
264
+
265
+ it 'should_define_predicate' do
266
+ object = @klass.new
267
+ assert object.respond_to?(:parked?)
268
+ end
269
+ end
270
+
271
+ context 'WithLambdaValue' do
272
+ before(:each) do
273
+ @klass = Class.new
274
+ @args = nil
275
+ @machine = StateMachines::Machine.new(@klass)
276
+ @value = lambda { |*args| @args = args; :parked }
277
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :value => @value)
278
+ end
279
+
280
+ it 'should_use_evaluated_value_by_default' do
281
+ assert_equal :parked, @state.value
282
+ end
283
+
284
+ it 'should_allow_access_to_original_value' do
285
+ assert_equal @value, @state.value(false)
286
+ end
287
+
288
+ it 'should_include_masked_value_in_description' do
289
+ assert_equal 'parked (*)', @state.description
290
+ end
291
+
292
+ it 'should_not_pass_in_any_arguments' do
293
+ @state.value
294
+ assert_equal [], @args
295
+ end
296
+
297
+ it 'should_define_predicate' do
298
+ object = @klass.new
299
+ assert object.respond_to?(:parked?)
300
+ end
301
+
302
+ it 'should_match_evaluated_value' do
303
+ assert @state.matches?(:parked)
304
+ end
305
+ end
306
+
307
+ context 'WithCachedLambdaValue' do
308
+ before(:each) do
309
+ @klass = Class.new
310
+ @machine = StateMachines::Machine.new(@klass)
311
+ @dynamic_value = lambda { 'value' }
312
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :value => @dynamic_value, :cache => true)
313
+ end
314
+
315
+ it 'should_be_caching' do
316
+ assert @state.cache
317
+ end
318
+
319
+ it 'should_evaluate_value' do
320
+ assert_equal 'value', @state.value
321
+ end
322
+
323
+ it 'should_only_evaluate_value_once' do
324
+ value = @state.value
325
+ assert_same value, @state.value
326
+ end
327
+
328
+ it 'should_update_value_index_for_state_collection' do
329
+ @state.value
330
+ assert_equal @state, @machine.states['value', :value]
331
+ assert_nil @machine.states[@dynamic_value, :value]
332
+ end
333
+ end
334
+
335
+ context 'WithoutCachedLambdaValue' do
336
+ before(:each) do
337
+ @klass = Class.new
338
+ @machine = StateMachines::Machine.new(@klass)
339
+ @dynamic_value = lambda { 'value' }
340
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :value => @dynamic_value)
341
+ end
342
+
343
+ it 'should_not_be_caching' do
344
+ assert !@state.cache
345
+ end
346
+
347
+ it 'should_evaluate_value_each_time' do
348
+ value = @state.value
349
+ assert_not_same value, @state.value
350
+ end
351
+
352
+ it 'should_not_update_value_index_for_state_collection' do
353
+ @state.value
354
+ assert_nil @machine.states['value', :value]
355
+ assert_equal @state, @machine.states[@dynamic_value, :value]
356
+ end
357
+ end
358
+
359
+ context 'WithMatcher' do
360
+ before(:each) do
361
+ @klass = Class.new
362
+ @args = nil
363
+ @machine = StateMachines::Machine.new(@klass)
364
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :if => lambda { |value| value == 1 })
365
+ end
366
+
367
+ it 'should_not_match_actual_value' do
368
+ assert !@state.matches?('parked')
369
+ end
370
+
371
+ it 'should_match_evaluated_block' do
372
+ assert @state.matches?(1)
373
+ end
374
+ end
375
+
376
+ context 'WithHumanName' do
377
+ before(:each) do
378
+ @klass = Class.new
379
+ @machine = StateMachines::Machine.new(@klass)
380
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :human_name => 'stopped')
381
+ end
382
+
383
+ it 'should_use_custom_human_name' do
384
+ assert_equal 'stopped', @state.human_name
385
+ end
386
+ end
387
+
388
+ context 'WithDynamicHumanName' do
389
+ before(:each) do
390
+ @klass = Class.new
391
+ @machine = StateMachines::Machine.new(@klass)
392
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :human_name => lambda { |state, object| ['stopped', object] })
393
+ end
394
+
395
+ it 'should_use_custom_human_name' do
396
+ human_name, klass = @state.human_name
397
+ assert_equal 'stopped', human_name
398
+ assert_equal @klass, klass
399
+ end
400
+
401
+ it 'should_allow_custom_class_to_be_passed_through' do
402
+ human_name, klass = @state.human_name(1)
403
+ assert_equal 'stopped', human_name
404
+ assert_equal 1, klass
405
+ end
406
+
407
+ it 'should_not_cache_value' do
408
+ assert_not_same @state.human_name, @state.human_name
409
+ end
410
+ end
411
+
412
+ context 'Initial' do
413
+ before(:each) do
414
+ @machine = StateMachines::Machine.new(Class.new)
415
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :initial => true)
416
+ end
417
+
418
+ it 'should_be_initial' do
419
+ assert @state.initial
420
+ assert @state.initial?
421
+ end
422
+ end
423
+
424
+ context 'NotInitial' do
425
+ before(:each) do
426
+ @machine = StateMachines::Machine.new(Class.new)
427
+ @machine.states << @state = StateMachines::State.new(@machine, :parked, :initial => false)
428
+ end
429
+
430
+ it 'should_not_be_initial' do
431
+ assert !@state.initial
432
+ assert !@state.initial?
433
+ end
434
+ end
435
+
436
+ context 'Final' do
437
+ before(:each) do
438
+ @machine = StateMachines::Machine.new(Class.new)
439
+ @machine.states << @state = StateMachines::State.new(@machine, :parked)
440
+ end
441
+
442
+ it 'should_be_final_without_input_transitions' do
443
+ assert @state.final?
444
+ end
445
+
446
+ it 'should_be_final_with_input_transitions' do
447
+ @machine.event :park do
448
+ transition :idling => :parked
449
+ end
450
+
451
+ assert @state.final?
452
+ end
453
+
454
+ it 'should_be_final_with_loopback' do
455
+ @machine.event :ignite do
456
+ transition :parked => same
457
+ end
458
+
459
+ assert @state.final?
460
+ end
461
+ end
462
+
463
+ context 'NotFinal' do
464
+ before(:each) do
465
+ @machine = StateMachines::Machine.new(Class.new)
466
+ @machine.states << @state = StateMachines::State.new(@machine, :parked)
467
+ end
468
+
469
+ it 'should_not_be_final_with_outgoing_whitelist_transitions' do
470
+ @machine.event :ignite do
471
+ transition :parked => :idling
472
+ end
473
+
474
+ assert !@state.final?
475
+ end
476
+
477
+ it 'should_not_be_final_with_outgoing_all_transitions' do
478
+ @machine.event :ignite do
479
+ transition all => :idling
480
+ end
481
+
482
+ assert !@state.final?
483
+ end
484
+
485
+ it 'should_not_be_final_with_outgoing_blacklist_transitions' do
486
+ @machine.event :ignite do
487
+ transition all - :first_gear => :idling
488
+ end
489
+
490
+ assert !@state.final?
491
+ end
492
+ end
493
+
494
+ context 'WithConflictingHelpersBeforeDefinition' do
495
+ before(:each) do
496
+ require 'stringio'
497
+ @original_stderr, $stderr = $stderr, StringIO.new
498
+
499
+ @superclass = Class.new do
500
+ def parked?
501
+ 0
502
+ end
503
+ end
504
+ @klass = Class.new(@superclass)
505
+ @machine = StateMachines::Machine.new(@klass)
506
+ @machine.state :parked
507
+ @object = @klass.new
508
+ end
509
+
510
+ it 'should_not_override_state_predicate' do
511
+ assert_equal 0, @object.parked?
512
+ end
513
+
514
+ it 'should_output_warning' do
515
+ assert_equal "Instance method \"parked?\" is already defined in #{@superclass.to_s}, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true.\n", $stderr.string
516
+ end
517
+
518
+ def teardown
519
+ $stderr = @original_stderr
520
+ end
521
+ end
522
+
523
+ context 'WithConflictingHelpersAfterDefinition' do
524
+ before(:each) do
525
+ require 'stringio'
526
+ @original_stderr, $stderr = $stderr, StringIO.new
527
+
528
+ @klass = Class.new do
529
+ def parked?
530
+ 0
531
+ end
532
+ end
533
+ @machine = StateMachines::Machine.new(@klass)
534
+ @machine.state :parked
535
+ @object = @klass.new
536
+ end
537
+
538
+ it 'should_not_override_state_predicate' do
539
+ assert_equal 0, @object.parked?
540
+ end
541
+
542
+ it 'should_still_allow_super_chaining' do
543
+ @klass.class_eval do
544
+ def parked?
545
+ super
546
+ end
547
+ end
548
+
549
+ assert_equal false, @object.parked?
550
+ end
551
+
552
+ it 'should_not_output_warning' do
553
+ assert_equal '', $stderr.string
554
+ end
555
+
556
+ def teardown
557
+ $stderr = @original_stderr
558
+ end
559
+ end
560
+
561
+ context 'WithConflictingMachine' do
562
+ before(:each) do
563
+ require 'stringio'
564
+ @original_stderr, $stderr = $stderr, StringIO.new
565
+
566
+ @klass = Class.new
567
+ @state_machine = StateMachines::Machine.new(@klass, :state)
568
+ @state_machine.states << @state = StateMachines::State.new(@state_machine, :parked)
569
+ end
570
+
571
+ it 'should_output_warning_if_using_different_attribute' do
572
+ @status_machine = StateMachines::Machine.new(@klass, :status)
573
+ @status_machine.states << @state = StateMachines::State.new(@status_machine, :parked)
574
+
575
+ assert_equal "State :parked for :status is already defined in :state\n", $stderr.string
576
+ end
577
+
578
+ it 'should_not_output_warning_if_using_same_attribute' do
579
+ @status_machine = StateMachines::Machine.new(@klass, :status, :attribute => :state)
580
+ @status_machine.states << @state = StateMachines::State.new(@status_machine, :parked)
581
+
582
+ assert_equal '', $stderr.string
583
+ end
584
+
585
+ it 'should_not_output_warning_if_using_different_namespace' do
586
+ @status_machine = StateMachines::Machine.new(@klass, :status, :namespace => 'alarm')
587
+ @status_machine.states << @state = StateMachines::State.new(@status_machine, :parked)
588
+
589
+ assert_equal '', $stderr.string
590
+ end
591
+
592
+ def teardown
593
+ $stderr = @original_stderr
594
+ end
595
+ end
596
+
597
+ context 'WithConflictingMachineName' do
598
+ before(:each) do
599
+ require 'stringio'
600
+ @original_stderr, $stderr = $stderr, StringIO.new
601
+
602
+ @klass = Class.new
603
+ @state_machine = StateMachines::Machine.new(@klass, :state)
604
+ end
605
+
606
+ it 'should_output_warning_if_name_conflicts' do
607
+ StateMachines::State.new(@state_machine, :state)
608
+ assert_equal "Instance method \"state?\" is already defined in #{@klass} :state instance helpers, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true.\n", $stderr.string
609
+ end
610
+
611
+ def teardown
612
+ $stderr = @original_stderr
613
+ end
614
+ end
615
+
616
+ context 'WithNamespace' do
617
+ before(:each) do
618
+ @klass = Class.new
619
+ @machine = StateMachines::Machine.new(@klass, :namespace => 'alarm')
620
+ @machine.states << @state = StateMachines::State.new(@machine, :active)
621
+ @object = @klass.new
622
+ end
623
+
624
+ it 'should_have_a_name' do
625
+ assert_equal :active, @state.name
626
+ end
627
+
628
+ it 'should_have_a_qualified_name' do
629
+ assert_equal :alarm_active, @state.qualified_name
630
+ end
631
+
632
+ it 'should_namespace_predicate' do
633
+ assert @object.respond_to?(:alarm_active?)
634
+ end
635
+ end
636
+
637
+ context 'AfterBeingCopied' do
638
+ before(:each) do
639
+ @machine = StateMachines::Machine.new(Class.new)
640
+ @machine.states << @state = StateMachines::State.new(@machine, :parked)
641
+ @copied_state = @state.dup
642
+ end
643
+
644
+ it 'should_not_have_the_context' do
645
+ state_context = nil
646
+ @state.context { state_context = self }
647
+
648
+ copied_state_context = nil
649
+ @copied_state.context { copied_state_context = self }
650
+
651
+ assert_not_same state_context, copied_state_context
652
+ end
653
+ end
654
+
655
+ context 'WithContext' do
656
+ before(:each) do
657
+ @klass = Class.new
658
+ @machine = StateMachines::Machine.new(@klass)
659
+ @ancestors = @klass.ancestors
660
+ @machine.states << @state = StateMachines::State.new(@machine, :idling)
661
+
662
+ context = nil
663
+ speed_method = nil
664
+ rpm_method = nil
665
+ @result = @state.context do
666
+ context = self
667
+
668
+ def speed
669
+ 0
670
+ end
671
+
672
+ speed_method = instance_method(:speed)
673
+
674
+ def rpm
675
+ 1000
676
+ end
677
+
678
+ rpm_method = instance_method(:rpm)
679
+ end
680
+
681
+ @context = context
682
+ @speed_method = speed_method
683
+ @rpm_method = rpm_method
684
+ end
685
+
686
+ it 'should_return_true' do
687
+ assert_equal true, @result
688
+ end
689
+
690
+ it 'should_include_new_module_in_owner_class' do
691
+ assert_not_equal @ancestors, @klass.ancestors
692
+ assert_equal [@context], @klass.ancestors - @ancestors
693
+ end
694
+
695
+ it 'should_define_each_context_method_in_owner_class' do
696
+ %w(speed rpm).each { |method| assert @klass.method_defined?(method) }
697
+ end
698
+
699
+ it 'should_define_aliased_context_method_in_owner_class' do
700
+ %w(speed rpm).each { |method| assert @klass.method_defined?("__state_idling_#{method}_#{@context.object_id}__") }
701
+ end
702
+
703
+ it 'should_not_use_context_methods_as_owner_class_methods' do
704
+ assert_not_equal @speed_method, @state.context_methods[:speed]
705
+ assert_not_equal @rpm_method, @state.context_methods[:rpm]
706
+ end
707
+
708
+ it 'should_use_context_methods_as_aliased_owner_class_methods' do
709
+ assert_equal @speed_method, @state.context_methods[:"__state_idling_speed_#{@context.object_id}__"]
710
+ assert_equal @rpm_method, @state.context_methods[:"__state_idling_rpm_#{@context.object_id}__"]
711
+ end
712
+ end
713
+
714
+ context 'WithMultipleContexts' do
715
+ before(:each) do
716
+ @klass = Class.new
717
+ @machine = StateMachines::Machine.new(@klass)
718
+ @ancestors = @klass.ancestors
719
+ @machine.states << @state = StateMachines::State.new(@machine, :idling)
720
+
721
+ context = nil
722
+ speed_method = nil
723
+ @state.context do
724
+ context = self
725
+
726
+ def speed
727
+ 0
728
+ end
729
+
730
+ speed_method = instance_method(:speed)
731
+ end
732
+ @context = context
733
+ @speed_method = speed_method
734
+
735
+ rpm_method = nil
736
+ @state.context do
737
+ def rpm
738
+ 1000
739
+ end
740
+
741
+ rpm_method = instance_method(:rpm)
742
+ end
743
+ @rpm_method = rpm_method
744
+ end
745
+
746
+ it 'should_include_new_module_in_owner_class' do
747
+ assert_not_equal @ancestors, @klass.ancestors
748
+ assert_equal [@context], @klass.ancestors - @ancestors
749
+ end
750
+
751
+ it 'should_define_each_context_method_in_owner_class' do
752
+ %w(speed rpm).each { |method| assert @klass.method_defined?(method) }
753
+ end
754
+
755
+ it 'should_define_aliased_context_method_in_owner_class' do
756
+ %w(speed rpm).each { |method| assert @klass.method_defined?("__state_idling_#{method}_#{@context.object_id}__") }
757
+ end
758
+
759
+ it 'should_not_use_context_methods_as_owner_class_methods' do
760
+ assert_not_equal @speed_method, @state.context_methods[:speed]
761
+ assert_not_equal @rpm_method, @state.context_methods[:rpm]
762
+ end
763
+
764
+ it 'should_use_context_methods_as_aliased_owner_class_methods' do
765
+ assert_equal @speed_method, @state.context_methods[:"__state_idling_speed_#{@context.object_id}__"]
766
+ assert_equal @rpm_method, @state.context_methods[:"__state_idling_rpm_#{@context.object_id}__"]
767
+ end
768
+ end
769
+
770
+ context 'WithExistingContextMethod' do
771
+ before(:each) do
772
+ @klass = Class.new do
773
+ def speed
774
+ 60
775
+ end
776
+ end
777
+ @original_speed_method = @klass.instance_method(:speed)
778
+
779
+ @machine = StateMachines::Machine.new(@klass)
780
+ @machine.states << @state = StateMachines::State.new(@machine, :idling)
781
+ @state.context do
782
+ def speed
783
+ 0
784
+ end
785
+ end
786
+ end
787
+
788
+ it 'should_not_override_method' do
789
+ assert_equal @original_speed_method, @klass.instance_method(:speed)
790
+ end
791
+ end
792
+
793
+ context 'WithRedefinedContextMethod' do
794
+ before(:each) do
795
+ @klass = Class.new
796
+ @machine = StateMachines::Machine.new(@klass)
797
+ @machine.states << @state = StateMachines::State.new(@machine, 'on')
798
+
799
+ old_context = nil
800
+ old_speed_method = nil
801
+ @state.context do
802
+ old_context = self
803
+
804
+ def speed
805
+ 0
806
+ end
807
+
808
+ old_speed_method = instance_method(:speed)
809
+ end
810
+ @old_context = old_context
811
+ @old_speed_method = old_speed_method
812
+
813
+ current_context = nil
814
+ current_speed_method = nil
815
+ @state.context do
816
+ current_context = self
817
+
818
+ def speed
819
+ 'green'
820
+ end
821
+
822
+ current_speed_method = instance_method(:speed)
823
+ end
824
+ @current_context = current_context
825
+ @current_speed_method = current_speed_method
826
+ end
827
+
828
+ it 'should_track_latest_defined_method' do
829
+ assert_equal @current_speed_method, @state.context_methods[:"__state_on_speed_#{@current_context.object_id}__"]
830
+ end
831
+
832
+ it 'should_have_the_same_context' do
833
+ assert_equal @current_context, @old_context
834
+ end
835
+ end
836
+
837
+ context 'WithInvalidMethodCall' do
838
+ before(:each) do
839
+ @klass = Class.new
840
+ @machine = StateMachines::Machine.new(@klass)
841
+ @ancestors = @klass.ancestors
842
+ @machine.states << @state = StateMachines::State.new(@machine, :idling)
843
+ @state.context do
844
+ def speed
845
+ 0
846
+ end
847
+ end
848
+
849
+ @object = @klass.new
850
+ end
851
+
852
+ it 'should_call_method_missing_arg' do
853
+ assert_equal 1, @state.call(@object, :invalid, :method_missing => lambda { 1 })
854
+ end
855
+ end
856
+
857
+ context 'WithValidMethodCallForDifferentState' do
858
+ before(:each) do
859
+ @klass = Class.new
860
+ @machine = StateMachines::Machine.new(@klass)
861
+ @ancestors = @klass.ancestors
862
+ @machine.states << @state = StateMachines::State.new(@machine, :idling)
863
+ @state.context do
864
+ def speed
865
+ 0
866
+ end
867
+ end
868
+
869
+ @object = @klass.new
870
+ end
871
+
872
+ it 'should_call_method_missing_arg' do
873
+ assert_equal 1, @state.call(@object, :speed, :method_missing => lambda { 1 })
874
+ end
875
+
876
+ it 'should_raise_invalid_context_on_no_method_error' do
877
+ assert_raise(StateMachines::InvalidContext) do
878
+ @state.call(@object, :speed, :method_missing => lambda { raise NoMethodError.new('Invalid', :speed, []) })
879
+ end
880
+ # FIXME
881
+ # assert_equal @object, exception.object
882
+ # assert_equal 'State nil for :state is not a valid context for calling #speed', exception.message
883
+ end
884
+
885
+ it 'should_raise_original_error_on_no_method_error_with_different_arguments' do
886
+ assert_raise(NoMethodError) do
887
+ @state.call(@object, :speed, :method_missing => lambda { raise NoMethodError.new('Invalid', :speed, [1]) })
888
+ end
889
+ end
890
+
891
+ it 'should_raise_original_error_on_no_method_error_for_different_method' do
892
+ assert_raise(NoMethodError) do
893
+ @state.call(@object, :speed, :method_missing => lambda { raise NoMethodError.new('Invalid', :rpm, []) })
894
+ end
895
+ end
896
+ end
897
+
898
+ context 'WithValidMethodCallForCurrentState' do
899
+ before(:each) do
900
+ @klass = Class.new
901
+ @machine = StateMachines::Machine.new(@klass, :initial => :idling)
902
+ @ancestors = @klass.ancestors
903
+ @state = @machine.state(:idling)
904
+ @state.context do
905
+ def speed(arg = nil)
906
+ block_given? ? [arg, yield] : arg
907
+ end
908
+ end
909
+
910
+ @object = @klass.new
911
+ end
912
+
913
+ it 'should_not_raise_an_exception' do
914
+ assert_nothing_raised { @state.call(@object, :speed, :method_missing => lambda { raise }) }
915
+ end
916
+
917
+ it 'should_pass_arguments_through' do
918
+ assert_equal 1, @state.call(@object, :speed, 1, :method_missing => lambda {})
919
+ end
920
+
921
+ it 'should_pass_blocks_through' do
922
+ assert_equal [nil, 1], @state.call(@object, :speed) { 1 }
923
+ end
924
+
925
+ it 'should_pass_both_arguments_and_blocks_through' do
926
+ assert_equal [1, 2], @state.call(@object, :speed, 1, :method_missing => lambda {}) { 2 }
927
+ end
928
+ end
929
+
930
+ if RUBY_VERSION > '1.8.7'
931
+ context 'WithValidInheritedMethodCallForCurrentState' do
932
+ before(:each) do
933
+ @superclass = Class.new do
934
+ def speed(arg = nil)
935
+ [arg]
936
+ end
937
+ end
938
+ @klass = Class.new(@superclass)
939
+ @machine = StateMachines::Machine.new(@klass, :initial => :idling)
940
+ @ancestors = @klass.ancestors
941
+ @state = @machine.state(:idling)
942
+ @state.context do
943
+ def speed(arg = nil)
944
+ [arg] + super(2)
945
+ end
946
+ end
947
+
948
+ @object = @klass.new
949
+ end
950
+
951
+ it 'should_not_raise_an_exception' do
952
+ assert_nothing_raised { @state.call(@object, :speed, :method_missing => lambda { raise }) }
953
+ end
954
+
955
+ it 'should_be_able_to_call_super' do
956
+ assert_equal [1, 2], @state.call(@object, :speed, 1)
957
+ end
958
+
959
+ it 'should_allow_redefinition' do
960
+ @state.context do
961
+ def speed(arg = nil)
962
+ [arg] + super(3)
963
+ end
964
+ end
965
+
966
+ assert_equal [1, 3], @state.call(@object, :speed, 1)
967
+ end
968
+ end
969
+ end
970
+ end