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,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