state_machine 0.8.1 → 0.9.0

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 (42) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/LICENSE +1 -1
  3. data/README.rdoc +162 -23
  4. data/Rakefile +3 -18
  5. data/lib/state_machine.rb +3 -4
  6. data/lib/state_machine/callback.rb +65 -13
  7. data/lib/state_machine/eval_helpers.rb +20 -4
  8. data/lib/state_machine/initializers.rb +4 -0
  9. data/lib/state_machine/initializers/merb.rb +1 -0
  10. data/lib/state_machine/initializers/rails.rb +7 -0
  11. data/lib/state_machine/integrations.rb +21 -6
  12. data/lib/state_machine/integrations/active_model.rb +414 -0
  13. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  14. data/lib/state_machine/integrations/{active_record → active_model}/observer.rb +7 -7
  15. data/lib/state_machine/integrations/active_record.rb +65 -129
  16. data/lib/state_machine/integrations/active_record/locale.rb +4 -11
  17. data/lib/state_machine/integrations/data_mapper.rb +24 -6
  18. data/lib/state_machine/integrations/data_mapper/observer.rb +36 -0
  19. data/lib/state_machine/integrations/mongo_mapper.rb +295 -0
  20. data/lib/state_machine/integrations/sequel.rb +33 -7
  21. data/lib/state_machine/machine.rb +121 -23
  22. data/lib/state_machine/machine_collection.rb +12 -103
  23. data/lib/state_machine/transition.rb +125 -164
  24. data/lib/state_machine/transition_collection.rb +244 -0
  25. data/lib/tasks/state_machine.rb +12 -15
  26. data/test/functional/state_machine_test.rb +11 -1
  27. data/test/unit/callback_test.rb +305 -32
  28. data/test/unit/eval_helpers_test.rb +103 -1
  29. data/test/unit/event_test.rb +2 -1
  30. data/test/unit/guard_test.rb +2 -1
  31. data/test/unit/integrations/active_model_test.rb +909 -0
  32. data/test/unit/integrations/active_record_test.rb +1542 -1292
  33. data/test/unit/integrations/data_mapper_test.rb +1369 -1041
  34. data/test/unit/integrations/mongo_mapper_test.rb +1349 -0
  35. data/test/unit/integrations/sequel_test.rb +1214 -985
  36. data/test/unit/integrations_test.rb +8 -0
  37. data/test/unit/machine_collection_test.rb +140 -513
  38. data/test/unit/machine_test.rb +212 -10
  39. data/test/unit/state_test.rb +2 -1
  40. data/test/unit/transition_collection_test.rb +2098 -0
  41. data/test/unit/transition_test.rb +704 -552
  42. metadata +16 -3
@@ -61,6 +61,10 @@ class TransitionTest < Test::Unit::TestCase
61
61
  assert_nil @transition.action
62
62
  end
63
63
 
64
+ def test_should_not_be_transient
65
+ assert_equal false, @transition.transient?
66
+ end
67
+
64
68
  def test_should_generate_attributes
65
69
  expected = {:object => @object, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
66
70
  assert_equal expected, @transition.attributes
@@ -350,16 +354,9 @@ class TransitionAfterBeingRolledBackTest < Test::Unit::TestCase
350
354
  end
351
355
  end
352
356
 
353
- class TransitionWithCallbacksTest < Test::Unit::TestCase
357
+ class TransitionWithoutCallbacksTest < Test::Unit::TestCase
354
358
  def setup
355
- @klass = Class.new do
356
- attr_reader :saved, :save_state
357
-
358
- def save
359
- @save_state = state
360
- @saved = true
361
- end
362
- end
359
+ @klass = Class.new
363
360
 
364
361
  @machine = StateMachine::Machine.new(@klass)
365
362
  @machine.state :parked, :idling
@@ -370,24 +367,47 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
370
367
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
371
368
  end
372
369
 
373
- def test_should_run_before_callbacks_on_before
374
- @machine.before_transition(lambda {|object| @run = true})
375
- result = @transition.before
370
+ def test_should_succeed
371
+ assert_equal true, @transition.run_callbacks
372
+ end
373
+
374
+ def test_should_succeed_if_after_callbacks_skipped
375
+ assert_equal true, @transition.run_callbacks(:after => false)
376
+ end
377
+
378
+ def test_should_call_block_if_provided
379
+ @transition.run_callbacks { @ran_block = true; {} }
380
+ assert @ran_block
381
+ end
382
+
383
+ def test_should_track_block_result
384
+ @transition.run_callbacks {{:result => 1}}
385
+ assert_equal 1, @transition.result
386
+ end
387
+ end
388
+
389
+ class TransitionWithBeforeCallbacksTest < Test::Unit::TestCase
390
+ def setup
391
+ @klass = Class.new
376
392
 
377
- assert_equal true, result
378
- assert_equal true, @run
393
+ @machine = StateMachine::Machine.new(@klass)
394
+ @machine.state :parked, :idling
395
+ @machine.event :ignite
396
+
397
+ @object = @klass.new
398
+ @object.state = 'parked'
399
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
379
400
  end
380
401
 
381
- def test_should_run_before_callbacks_in_the_order_they_were_defined
382
- @callbacks = []
383
- @machine.before_transition(lambda {@callbacks << 1})
384
- @machine.before_transition(lambda {@callbacks << 2})
385
- @transition.before
402
+ def test_should_run_before_callbacks
403
+ @machine.before_transition {@run = true}
404
+ result = @transition.run_callbacks
386
405
 
387
- assert_equal [1, 2], @callbacks
406
+ assert_equal true, result
407
+ assert_equal true, @run
388
408
  end
389
409
 
390
- def test_should_only_run_before_callbacks_that_match_transition_context
410
+ def test_should_only_run_those_that_match_transition_context
391
411
  @count = 0
392
412
  callback = lambda {@count += 1}
393
413
 
@@ -395,76 +415,142 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
395
415
  @machine.before_transition :from => :parked, :to => :parked, :on => :park, :do => callback
396
416
  @machine.before_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
397
417
  @machine.before_transition :from => :idling, :to => :idling, :on => :park, :do => callback
398
- @transition.before
418
+ @transition.run_callbacks
399
419
 
400
420
  assert_equal 1, @count
401
421
  end
402
422
 
403
- def test_should_pass_transition_to_before_callbacks
404
- @machine.before_transition(lambda {|*args| @args = args})
405
- @transition.before
423
+ def test_should_pass_transition_as_argument
424
+ @machine.before_transition {|*args| @args = args}
425
+ @transition.run_callbacks
406
426
 
407
427
  assert_equal [@object, @transition], @args
408
428
  end
409
429
 
410
- def test_should_catch_halted_before_callbacks
411
- @machine.before_transition(lambda {throw :halt})
430
+ def test_should_catch_halts
431
+ @machine.before_transition {throw :halt}
412
432
 
413
433
  result = nil
414
- assert_nothing_thrown { result = @transition.before }
434
+ assert_nothing_thrown { result = @transition.run_callbacks }
415
435
  assert_equal false, result
416
436
  end
417
437
 
418
- def test_should_run_before_callbacks_on_perform_before_changing_the_state
419
- @machine.before_transition(lambda {|object| @state = object.state})
420
- @transition.perform
421
-
422
- assert_equal 'parked', @state
438
+ def test_should_not_catch_exceptions
439
+ @machine.before_transition {raise ArgumentError}
440
+ assert_raise(ArgumentError) { @transition.run_callbacks }
423
441
  end
424
442
 
425
- def test_should_not_be_able_to_run_before_callbacks_twice
443
+ def test_should_not_be_able_to_run_twice
426
444
  @count = 0
427
- @machine.before_transition(lambda {@count += 1})
428
- @transition.before
429
- @transition.before
445
+ @machine.before_transition {@count += 1}
446
+ @transition.run_callbacks
447
+ @transition.run_callbacks
430
448
  assert_equal 1, @count
431
449
  end
432
450
 
433
- def test_should_be_able_to_run_before_callbacks_again_after_resetting
451
+ def test_should_be_able_to_run_again_after_halt
452
+ @count = 0
453
+ @machine.before_transition {@count += 1; throw :halt}
454
+ @transition.run_callbacks
455
+ @transition.run_callbacks
456
+ assert_equal 2, @count
457
+ end
458
+
459
+ def test_should_be_able_to_run_again_after_resetting
434
460
  @count = 0
435
- @machine.before_transition(lambda {@count += 1})
436
- @transition.before
461
+ @machine.before_transition {@count += 1}
462
+ @transition.run_callbacks
437
463
  @transition.reset
438
- @transition.before
464
+ @transition.run_callbacks
439
465
  assert_equal 2, @count
440
466
  end
441
467
 
442
- def test_should_run_after_callbacks_on_after
443
- @machine.after_transition(lambda {|object| @run = true})
444
- result = @transition.after(true)
445
-
446
- assert_equal true, result
447
- assert_equal true, @run
468
+ def test_should_succeed_if_block_result_is_false
469
+ @machine.before_transition {@run = true}
470
+ assert_equal true, @transition.run_callbacks {{:result => false}}
471
+ assert @run
448
472
  end
449
473
 
450
- def test_should_set_result_on_after
451
- @transition.after
452
- assert_nil @transition.result
474
+ def test_should_succeed_if_block_result_is_true
475
+ @machine.before_transition {@run = true}
476
+ assert_equal true, @transition.run_callbacks {{:result => true}}
477
+ assert @run
478
+ end
479
+
480
+ def test_should_succeed_if_block_success_is_false
481
+ @machine.before_transition {@run = true}
482
+ assert_equal true, @transition.run_callbacks {{:success => false}}
483
+ assert @run
484
+ end
485
+
486
+ def test_should_succeed_if_block_success_is_false
487
+ @machine.before_transition {@run = true}
488
+ assert_equal true, @transition.run_callbacks {{:success => true}}
489
+ assert @run
490
+ end
491
+ end
492
+
493
+ class TransitionWithMultipleBeforeCallbacksTest < Test::Unit::TestCase
494
+ def setup
495
+ @klass = Class.new
453
496
 
454
- @transition.after(1)
455
- assert_equal 1, @transition.result
497
+ @machine = StateMachine::Machine.new(@klass)
498
+ @machine.state :parked, :idling
499
+ @machine.event :ignite
500
+
501
+ @object = @klass.new
502
+ @object.state = 'parked'
503
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
456
504
  end
457
505
 
458
- def test_should_run_after_callbacks_in_the_order_they_were_defined
506
+ def test_should_run_in_the_order_they_were_defined
459
507
  @callbacks = []
460
- @machine.after_transition(lambda {@callbacks << 1})
461
- @machine.after_transition(lambda {@callbacks << 2})
462
- @transition.after(true)
508
+ @machine.before_transition {@callbacks << 1}
509
+ @machine.before_transition {@callbacks << 2}
510
+ @transition.run_callbacks
463
511
 
464
512
  assert_equal [1, 2], @callbacks
465
513
  end
466
514
 
467
- def test_should_only_run_after_callbacks_that_match_transition_context
515
+ def test_should_not_run_further_callbacks_if_halted
516
+ @callbacks = []
517
+ @machine.before_transition {@callbacks << 1; throw :halt}
518
+ @machine.before_transition {@callbacks << 2}
519
+
520
+ assert_equal false, @transition.run_callbacks
521
+ assert_equal [1], @callbacks
522
+ end
523
+
524
+ def test_should_fail_if_any_callback_halted
525
+ @machine.before_transition {true}
526
+ @machine.before_transition {throw :halt}
527
+
528
+ assert_equal false, @transition.run_callbacks
529
+ end
530
+ end
531
+
532
+ class TransitionWithAfterCallbacksTest < Test::Unit::TestCase
533
+ def setup
534
+ @klass = Class.new
535
+
536
+ @machine = StateMachine::Machine.new(@klass)
537
+ @machine.state :parked, :idling
538
+ @machine.event :ignite
539
+
540
+ @object = @klass.new
541
+ @object.state = 'parked'
542
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
543
+ end
544
+
545
+ def test_should_run_after_callbacks
546
+ @machine.after_transition {|object| @run = true}
547
+ result = @transition.run_callbacks
548
+
549
+ assert_equal true, result
550
+ assert_equal true, @run
551
+ end
552
+
553
+ def test_should_only_run_those_that_match_transition_context
468
554
  @count = 0
469
555
  callback = lambda {@count += 1}
470
556
 
@@ -472,119 +558,125 @@ class TransitionWithCallbacksTest < Test::Unit::TestCase
472
558
  @machine.after_transition :from => :parked, :to => :parked, :on => :park, :do => callback
473
559
  @machine.after_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
474
560
  @machine.after_transition :from => :idling, :to => :idling, :on => :park, :do => callback
475
- @transition.after(true)
561
+ @transition.run_callbacks
476
562
 
477
563
  assert_equal 1, @count
478
564
  end
479
565
 
480
- def test_should_not_run_after_callbacks_if_not_successful
481
- @machine.after_transition(lambda {|object| @run = true})
482
- @transition.after(nil, false)
566
+ def test_should_not_run_if_not_successful
567
+ @machine.after_transition {|object| @run = true}
568
+ @transition.run_callbacks {{:success => false}}
483
569
  assert !@run
484
570
  end
485
571
 
486
- def test_should_pass_transition_to_after_callbacks
487
- @machine.after_transition(lambda {|*args| @args = args})
488
-
489
- @transition.after(true)
490
- assert_equal [@object, @transition], @args
491
- assert_equal true, @transition.result
572
+ def test_should_run_if_not_successful_and_includes_failures
573
+ @machine.after_transition(:include_failures => true) {|object| @run = true}
574
+ @transition.run_callbacks {{:success => false}}
575
+ assert @run
576
+ end
577
+
578
+ def test_should_run_if_successful
579
+ @machine.after_transition {|object| @run = true}
580
+ @transition.run_callbacks {{:success => true}}
581
+ assert @run
582
+ end
583
+
584
+ def test_should_run_if_successful_and_includes_failures
585
+ @machine.after_transition(:include_failures => true) {|object| @run = true}
586
+ @transition.run_callbacks {{:success => true}}
587
+ assert @run
588
+ end
589
+
590
+ def test_should_pass_transition_as_argument
591
+ @machine.after_transition {|*args| @args = args}
492
592
 
493
- @transition.after(false)
593
+ @transition.run_callbacks
494
594
  assert_equal [@object, @transition], @args
495
- assert_equal false, @transition.result
496
595
  end
497
596
 
498
- def test_should_catch_halted_after_callbacks
499
- @machine.after_transition(lambda {throw :halt})
597
+ def test_should_catch_halts
598
+ @machine.after_transition {throw :halt}
500
599
 
501
600
  result = nil
502
- assert_nothing_thrown { result = @transition.after(true) }
601
+ assert_nothing_thrown { result = @transition.run_callbacks }
503
602
  assert_equal true, result
504
603
  end
505
604
 
506
- def test_should_run_after_callbacks_on_perform_after_running_the_action
507
- @machine.after_transition(lambda {|object| @state = object.state})
508
- @transition.perform(true)
509
-
510
- assert_equal 'idling', @state
605
+ def test_should_not_catch_exceptions
606
+ @machine.after_transition {raise ArgumentError}
607
+ assert_raise(ArgumentError) { @transition.run_callbacks }
608
+ end
609
+
610
+ def test_should_not_be_able_to_run_twice
611
+ @count = 0
612
+ @machine.after_transition {@count += 1}
613
+ @transition.run_callbacks
614
+ @transition.run_callbacks
615
+ assert_equal 1, @count
511
616
  end
512
617
 
513
- def test_should_not_be_able_to_run_after_callbacks_twice
618
+ def test_should_not_be_able_to_run_twice_if_halted
514
619
  @count = 0
515
- @machine.after_transition(lambda {@count += 1})
516
- @transition.after
517
- @transition.after
620
+ @machine.after_transition {@count += 1; throw :halt}
621
+ @transition.run_callbacks
622
+ @transition.run_callbacks
518
623
  assert_equal 1, @count
519
624
  end
520
625
 
521
- def test_should_be_able_to_run_after_callbacks_again_after_resetting
626
+ def test_should_be_able_to_run_again_after_resetting
522
627
  @count = 0
523
- @machine.after_transition(lambda {@count += 1})
524
- @transition.after
628
+ @machine.after_transition {@count += 1}
629
+ @transition.run_callbacks
525
630
  @transition.reset
526
- @transition.after
631
+ @transition.run_callbacks
527
632
  assert_equal 2, @count
528
633
  end
529
634
  end
530
635
 
531
- class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
636
+ class TransitionWithMultipleAfterCallbacksTest < Test::Unit::TestCase
532
637
  def setup
533
- @klass = Class.new do
534
- attr_reader :saved, :save_state
535
-
536
- def save
537
- @save_state = state
538
- @saved = true
539
- end
540
- end
638
+ @klass = Class.new
541
639
 
542
- @machine = StateMachine::Machine.new(@klass, :action => :save)
640
+ @machine = StateMachine::Machine.new(@klass)
543
641
  @machine.state :parked, :idling
544
642
  @machine.event :ignite
545
643
 
546
644
  @object = @klass.new
547
645
  @object.state = 'parked'
548
646
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
549
- @result = @transition.perform
550
- end
551
-
552
- def test_should_have_empty_args
553
- assert_equal [], @transition.args
554
- end
555
-
556
- def test_should_have_a_result
557
- assert_equal true, @transition.result
558
- end
559
-
560
- def test_should_be_successful
561
- assert_equal true, @result
562
647
  end
563
648
 
564
- def test_should_change_the_current_state
565
- assert_equal 'idling', @object.state
649
+ def test_should_run_in_the_order_they_were_defined
650
+ @callbacks = []
651
+ @machine.after_transition {@callbacks << 1}
652
+ @machine.after_transition {@callbacks << 2}
653
+ @transition.run_callbacks
654
+
655
+ assert_equal [1, 2], @callbacks
566
656
  end
567
657
 
568
- def test_should_run_the_action
569
- assert @object.saved
658
+ def test_should_not_run_further_callbacks_if_halted
659
+ @callbacks = []
660
+ @machine.after_transition {@callbacks << 1; throw :halt}
661
+ @machine.after_transition {@callbacks << 2}
662
+
663
+ assert_equal true, @transition.run_callbacks
664
+ assert_equal [1], @callbacks
570
665
  end
571
666
 
572
- def test_should_run_the_action_after_saving_the_state
573
- assert_equal 'idling', @object.save_state
667
+ def test_should_fail_if_any_callback_halted
668
+ @machine.after_transition {true}
669
+ @machine.after_transition {throw :halt}
670
+
671
+ assert_equal true, @transition.run_callbacks
574
672
  end
575
673
  end
576
674
 
577
- class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
675
+ class TransitionWithAroundCallbacksTest < Test::Unit::TestCase
578
676
  def setup
579
- @klass = Class.new do
580
- attr_reader :saved
581
-
582
- def save
583
- @saved = true
584
- end
585
- end
677
+ @klass = Class.new
586
678
 
587
- @machine = StateMachine::Machine.new(@klass, :action => :save)
679
+ @machine = StateMachine::Machine.new(@klass)
588
680
  @machine.state :parked, :idling
589
681
  @machine.event :ignite
590
682
 
@@ -593,308 +685,551 @@ class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
593
685
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
594
686
  end
595
687
 
596
- def test_should_have_arguments
597
- @transition.perform(1, 2)
688
+ def test_should_run_around_callbacks
689
+ @machine.around_transition {|object, transition, block| @run_before = true; block.call; @run_after = true}
690
+ result = @transition.run_callbacks
598
691
 
599
- assert_equal [1, 2], @transition.args
600
- assert @object.saved
692
+ assert_equal true, result
693
+ assert_equal true, @run_before
694
+ assert_equal true, @run_after
601
695
  end
602
696
 
603
- def test_should_not_include_run_action_in_arguments
604
- @transition.perform(1, 2, false)
697
+ def test_should_only_run_those_that_match_transition_context
698
+ @count = 0
699
+ callback = lambda {|object, transition, block| @count += 1; block.call}
605
700
 
606
- assert_equal [1, 2], @transition.args
607
- assert !@object.saved
701
+ @machine.around_transition :from => :parked, :to => :idling, :on => :park, :do => callback
702
+ @machine.around_transition :from => :parked, :to => :parked, :on => :park, :do => callback
703
+ @machine.around_transition :from => :parked, :to => :idling, :on => :ignite, :do => callback
704
+ @machine.around_transition :from => :idling, :to => :idling, :on => :park, :do => callback
705
+ @transition.run_callbacks
706
+
707
+ assert_equal 1, @count
608
708
  end
609
- end
610
-
611
- class TransitionWithoutRunningActionTest < Test::Unit::TestCase
612
- def setup
613
- @klass = Class.new do
614
- attr_reader :saved
615
-
616
- def save
617
- @saved = true
618
- end
619
- end
709
+
710
+ def test_should_pass_transition_as_argument
711
+ @machine.around_transition {|*args| block = args.pop; @args = args; block.call}
712
+ @transition.run_callbacks
620
713
 
621
- @machine = StateMachine::Machine.new(@klass, :action => :save)
622
- @machine.state :parked, :idling
623
- @machine.event :ignite
624
- @machine.after_transition(lambda {|object| @run_after = true})
714
+ assert_equal [@object, @transition], @args
715
+ end
716
+
717
+ def test_should_run_block_between_callback
718
+ @callbacks = []
719
+ @machine.around_transition {|block| @callbacks << :before; block.call; @callbacks << :after}
720
+ @transition.run_callbacks { @callbacks << :within; {:success => true} }
625
721
 
626
- @object = @klass.new
627
- @object.state = 'parked'
628
- @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
629
- @result = @transition.perform(false)
722
+ assert_equal [:before, :within, :after], @callbacks
630
723
  end
631
724
 
632
- def test_should_have_empty_args
633
- assert_equal [], @transition.args
725
+ def test_should_have_access_to_result_after_yield
726
+ @machine.around_transition {|block| @before_result = @transition.result; block.call; @after_result = @transition.result}
727
+ @transition.run_callbacks {{:result => 1, :success => true}}
728
+
729
+ assert_nil @before_result
730
+ assert_equal 1, @after_result
634
731
  end
635
732
 
636
- def test_should_not_have_a_result
637
- assert_nil @transition.result
733
+ def test_should_catch_before_yield_halts
734
+ @machine.around_transition {throw :halt}
735
+
736
+ result = nil
737
+ assert_nothing_thrown { result = @transition.run_callbacks }
738
+ assert_equal false, result
638
739
  end
639
740
 
640
- def test_should_be_successful
641
- assert_equal true, @result
741
+ def test_should_catch_after_yield_halts
742
+ @machine.around_transition {|block| block.call; throw :halt}
743
+
744
+ result = nil
745
+ assert_nothing_thrown { result = @transition.run_callbacks }
746
+ assert_equal true, result
642
747
  end
643
748
 
644
- def test_should_change_the_current_state
645
- assert_equal 'idling', @object.state
749
+ def test_should_not_catch_before_yield
750
+ @machine.around_transition {raise ArgumentError}
751
+ assert_raise(ArgumentError) { @transition.run_callbacks }
646
752
  end
647
753
 
648
- def test_should_not_run_the_action
649
- assert !@object.saved
754
+ def test_should_not_catch_after_yield
755
+ @machine.around_transition {|block| block.call; raise ArgumentError}
756
+ assert_raise(ArgumentError) { @transition.run_callbacks }
650
757
  end
651
758
 
652
- def test_should_run_after_callbacks
653
- assert @run_after
759
+ def test_should_fail_if_not_yielded
760
+ @machine.around_transition {}
761
+
762
+ result = nil
763
+ assert_nothing_thrown { result = @transition.run_callbacks }
764
+ assert_equal false, result
765
+ end
766
+
767
+ def test_should_not_be_able_to_run_twice
768
+ @before_count = 0
769
+ @after_count = 0
770
+ @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
771
+ @transition.run_callbacks
772
+ @transition.run_callbacks
773
+ assert_equal 1, @before_count
774
+ assert_equal 1, @after_count
775
+ end
776
+
777
+ def test_should_be_able_to_run_again_after_resetting
778
+ @before_count = 0
779
+ @after_count = 0
780
+ @machine.around_transition {|block| @before_count += 1; block.call; @after_count += 1}
781
+ @transition.run_callbacks
782
+ @transition.reset
783
+ @transition.run_callbacks
784
+ assert_equal 2, @before_count
785
+ assert_equal 2, @after_count
786
+ end
787
+
788
+ def test_should_succeed_if_block_result_is_false
789
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
790
+ assert_equal true, @transition.run_callbacks {{:success => true, :result => false}}
791
+ assert @before_run
792
+ assert @after_run
793
+ end
794
+
795
+ def test_should_succeed_if_block_result_is_true
796
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
797
+ assert_equal true, @transition.run_callbacks {{:success => true, :result => true}}
798
+ assert @before_run
799
+ assert @after_run
800
+ end
801
+
802
+ def test_should_only_run_before_if_block_success_is_false
803
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
804
+ assert_equal true, @transition.run_callbacks {{:success => false}}
805
+ assert @before_run
806
+ assert !@after_run
807
+ end
808
+
809
+ def test_should_succeed_if_including_failure_and_block_success_is_false
810
+ @machine.around_transition(:include_failures => true) {|block| @before_run = true; block.call; @after_run = true}
811
+ assert_equal true, @transition.run_callbacks {{:success => false}}
812
+ assert @before_run
813
+ assert @after_run
814
+ end
815
+
816
+ def test_should_succeed_if_block_success_is_false
817
+ @machine.around_transition {|block| @before_run = true; block.call; @after_run = true}
818
+ assert_equal true, @transition.run_callbacks {{:success => true}}
819
+ assert @before_run
820
+ assert @after_run
654
821
  end
655
822
  end
656
823
 
657
- class TransitionWithTransactionsTest < Test::Unit::TestCase
824
+ class TransitionWithMultipleAroundCallbacksTest < Test::Unit::TestCase
658
825
  def setup
659
- @klass = Class.new do
660
- class << self
661
- attr_accessor :running_transaction
662
- end
663
-
664
- attr_accessor :result
665
-
666
- def save
667
- @result = self.class.running_transaction
668
- true
669
- end
670
- end
826
+ @klass = Class.new
671
827
 
672
- @machine = StateMachine::Machine.new(@klass, :action => :save)
828
+ @machine = StateMachine::Machine.new(@klass)
673
829
  @machine.state :parked, :idling
674
830
  @machine.event :ignite
675
831
 
676
832
  @object = @klass.new
677
833
  @object.state = 'parked'
678
834
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
679
-
680
- class << @machine
681
- def within_transaction(object)
682
- owner_class.running_transaction = object
683
- yield
684
- owner_class.running_transaction = false
685
- end
686
- end
687
835
  end
688
836
 
689
- def test_should_run_blocks_within_transaction_for_object
690
- @transition.within_transaction do
691
- @result = @klass.running_transaction
692
- end
837
+ def test_should_before_yield_in_the_order_they_were_defined
838
+ @callbacks = []
839
+ @machine.around_transition {|block| @callbacks << 1; block.call}
840
+ @machine.around_transition {|block| @callbacks << 2; block.call}
841
+ @transition.run_callbacks
693
842
 
694
- assert_equal @object, @result
843
+ assert_equal [1, 2], @callbacks
695
844
  end
696
845
 
697
- def test_should_run_before_callbacks_within_transaction
698
- @machine.before_transition(lambda {|object| @result = @klass.running_transaction})
699
- @transition.perform
846
+ def test_should_before_yield_multiple_methods_in_the_order_they_were_defined
847
+ @callbacks = []
848
+ @machine.around_transition(lambda {|block| @callbacks << 1; block.call}, lambda {|block| @callbacks << 2; block.call})
849
+ @machine.around_transition(lambda {|block| @callbacks << 3; block.call}, lambda {|block| @callbacks << 4; block.call})
850
+ @transition.run_callbacks
700
851
 
701
- assert_equal @object, @result
852
+ assert_equal [1, 2, 3, 4], @callbacks
702
853
  end
703
854
 
704
- def test_should_run_action_within_transaction
705
- @transition.perform
855
+ def test_should_after_yield_in_the_reverse_order_they_were_defined
856
+ @callbacks = []
857
+ @machine.around_transition {|block| block.call; @callbacks << 1}
858
+ @machine.around_transition {|block| block.call; @callbacks << 2}
859
+ @transition.run_callbacks
706
860
 
707
- assert_equal @object, @object.result
861
+ assert_equal [2, 1], @callbacks
708
862
  end
709
863
 
710
- def test_should_run_after_callbacks_within_transaction
711
- @machine.after_transition(lambda {|object| @result = @klass.running_transaction})
712
- @transition.perform
864
+ def test_should_after_yield_multiple_methods_in_the_reverse_order_they_were_defined
865
+ @callbacks = []
866
+ @machine.around_transition(lambda {|block| block.call; @callbacks << 1}) {|block| block.call; @callbacks << 2}
867
+ @machine.around_transition(lambda {|block| block.call; @callbacks << 3}) {|block| block.call; @callbacks << 4}
868
+ @transition.run_callbacks
713
869
 
714
- assert_equal @object, @result
870
+ assert_equal [4, 3, 2, 1], @callbacks
871
+ end
872
+
873
+ def test_should_run_block_between_callback
874
+ @callbacks = []
875
+ @machine.around_transition {|block| @callbacks << :before_1; block.call; @callbacks << :after_1}
876
+ @machine.around_transition {|block| @callbacks << :before_2; block.call; @callbacks << :after_2}
877
+ @transition.run_callbacks { @callbacks << :within; {:success => true} }
878
+
879
+ assert_equal [:before_1, :before_2, :within, :after_2, :after_1], @callbacks
880
+ end
881
+
882
+ def test_should_have_access_to_result_after_yield
883
+ @machine.around_transition {|block| @before_result_1 = @transition.result; block.call; @after_result_1 = @transition.result}
884
+ @machine.around_transition {|block| @before_result_2 = @transition.result; block.call; @after_result_2 = @transition.result}
885
+ @transition.run_callbacks {{:result => 1, :success => true}}
886
+
887
+ assert_nil @before_result_1
888
+ assert_nil @before_result_2
889
+ assert_equal 1, @after_result_1
890
+ assert_equal 1, @after_result_2
891
+ end
892
+
893
+ def test_should_fail_if_any_before_yield_halted
894
+ @machine.around_transition {|block| block.call}
895
+ @machine.around_transition {throw :halt}
896
+
897
+ assert_equal false, @transition.run_callbacks
898
+ end
899
+
900
+ def test_should_not_continue_around_callbacks_if_before_yield_halted
901
+ @callbacks = []
902
+ @machine.around_transition {@callbacks << 1; throw :halt}
903
+ @machine.around_transition {|block| @callbacks << 2; block.call; @callbacks << 3}
904
+
905
+ assert_equal false, @transition.run_callbacks
906
+ assert_equal [1], @callbacks
907
+ end
908
+
909
+ def test_should_not_continue_around_callbacks_if_later_before_yield_halted
910
+ @callbacks = []
911
+ @machine.around_transition {|block| block.call; @callbacks << 1}
912
+ @machine.around_transition {throw :halt}
913
+
914
+ @transition.run_callbacks
915
+ assert_equal [], @callbacks
916
+ end
917
+
918
+ def test_should_not_run_further_callbacks_if_after_yield_halted
919
+ @callbacks = []
920
+ @machine.around_transition {|block| block.call; @callbacks << 1}
921
+ @machine.around_transition {|block| block.call; throw :halt}
922
+
923
+ assert_equal true, @transition.run_callbacks
924
+ assert_equal [], @callbacks
925
+ end
926
+
927
+ def test_should_fail_if_any_fail_to_yield
928
+ @callbacks = []
929
+ @machine.around_transition {@callbacks << 1}
930
+ @machine.around_transition {|block| @callbacks << 2; block.call; @callbacks << 3}
931
+
932
+ assert_equal false, @transition.run_callbacks
933
+ assert_equal [1], @callbacks
715
934
  end
716
935
  end
717
936
 
718
- class TransitionHaltedDuringBeforeCallbacksTest < Test::Unit::TestCase
937
+ class TransitionWithMixedCallbacksTest < Test::Unit::TestCase
719
938
  def setup
720
- @klass = Class.new do
721
- class << self; attr_accessor :cancelled_transaction; end
722
- attr_reader :saved
723
-
724
- def save
725
- @saved = true
726
- end
727
- end
728
- @before_count = 0
729
- @after_count = 0
939
+ @klass = Class.new
730
940
 
731
- @machine = StateMachine::Machine.new(@klass, :action => :save)
941
+ @machine = StateMachine::Machine.new(@klass)
732
942
  @machine.state :parked, :idling
733
943
  @machine.event :ignite
734
- class << @machine
735
- def within_transaction(object)
736
- owner_class.cancelled_transaction = yield == false
737
- end
738
- end
739
-
740
- @machine.before_transition lambda {@before_count += 1; throw :halt}
741
- @machine.before_transition lambda {@before_count += 1}
742
- @machine.after_transition lambda {@after_count += 1}
743
944
 
744
945
  @object = @klass.new
745
946
  @object.state = 'parked'
746
947
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
747
- @result = @transition.perform
748
948
  end
749
949
 
750
- def test_should_not_be_successful
751
- assert_equal false, @result
950
+ def test_should_before_and_around_callbacks_in_order_defined
951
+ @callbacks = []
952
+ @machine.before_transition {@callbacks << :before_1}
953
+ @machine.around_transition {|block| @callbacks << :around; block.call}
954
+ @machine.before_transition {@callbacks << :before_2}
955
+
956
+ assert_equal true, @transition.run_callbacks
957
+ assert_equal [:before_1, :around, :before_2], @callbacks
752
958
  end
753
959
 
754
- def test_should_not_change_current_state
755
- assert_equal 'parked', @object.state
960
+ def test_should_run_around_callbacks_before_after_callbacks
961
+ @callbacks = []
962
+ @machine.after_transition {@callbacks << :after_1}
963
+ @machine.around_transition {|block| block.call; @callbacks << :after_2}
964
+ @machine.after_transition {@callbacks << :after_3}
965
+
966
+ assert_equal true, @transition.run_callbacks
967
+ assert_equal [:after_2, :after_1, :after_3], @callbacks
756
968
  end
757
969
 
758
- def test_should_not_run_action
759
- assert !@object.saved
970
+ def test_should_have_access_to_result_for_both_after_and_around_callbacks
971
+ @machine.after_transition {@after_result = @transition.result}
972
+ @machine.around_transition {|block| block.call; @around_result = @transition.result}
973
+
974
+ @transition.run_callbacks {{:result => 1, :success => true}}
975
+ assert_equal 1, @after_result
976
+ assert_equal 1, @around_result
760
977
  end
761
978
 
762
- def test_should_not_run_further_before_callbacks
763
- assert_equal 1, @before_count
979
+ def test_should_not_run_further_callbacks_if_before_callback_halts
980
+ @callbacks = []
981
+ @machine.before_transition {@callbacks << :before_1}
982
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
983
+ @machine.before_transition {@callbacks << :before_2; throw :halt}
984
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
985
+ @machine.after_transition {@callbacks << :after}
986
+
987
+ assert_equal false, @transition.run_callbacks
988
+ assert_equal [:before_1, :before_around_1, :before_2], @callbacks
764
989
  end
765
990
 
766
- def test_should_not_run_after_callbacks
767
- assert_equal 0, @after_count
991
+ def test_should_not_run_further_callbacks_if_before_yield_halts
992
+ @callbacks = []
993
+ @machine.before_transition {@callbacks << :before_1}
994
+ @machine.around_transition {|block| @callbacks << :before_around_1; throw :halt}
995
+ @machine.before_transition {@callbacks << :before_2; throw :halt}
996
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
997
+ @machine.after_transition {@callbacks << :after}
998
+
999
+ assert_equal false, @transition.run_callbacks
1000
+ assert_equal [:before_1, :before_around_1], @callbacks
1001
+ end
1002
+
1003
+ def test_should_not_run_further_callbacks_if_around_callback_fails_to_yield
1004
+ @callbacks = []
1005
+ @machine.before_transition {@callbacks << :before_1}
1006
+ @machine.around_transition {|block| @callbacks << :before_around_1}
1007
+ @machine.before_transition {@callbacks << :before_2; throw :halt}
1008
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1009
+ @machine.after_transition {@callbacks << :after}
1010
+
1011
+ assert_equal false, @transition.run_callbacks
1012
+ assert_equal [:before_1, :before_around_1], @callbacks
1013
+ end
1014
+
1015
+ def test_should_not_run_further_callbacks_if_after_yield_halts
1016
+ @callbacks = []
1017
+ @machine.before_transition {@callbacks << :before_1}
1018
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1; throw :halt}
1019
+ @machine.before_transition {@callbacks << :before_2}
1020
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1021
+ @machine.after_transition {@callbacks << :after}
1022
+
1023
+ assert_equal true, @transition.run_callbacks
1024
+ assert_equal [:before_1, :before_around_1, :before_2, :before_around_2, :after_around_2, :after_around_1], @callbacks
1025
+ end
1026
+
1027
+ def test_should_not_run_further_callbacks_if_after_callback_halts
1028
+ @callbacks = []
1029
+ @machine.before_transition {@callbacks << :before_1}
1030
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1031
+ @machine.before_transition {@callbacks << :before_2}
1032
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1033
+ @machine.after_transition {@callbacks << :after_1; throw :halt}
1034
+ @machine.after_transition {@callbacks << :after_2}
1035
+
1036
+ assert_equal true, @transition.run_callbacks
1037
+ assert_equal [:before_1, :before_around_1, :before_2, :before_around_2, :after_around_2, :after_around_1, :after_1], @callbacks
768
1038
  end
769
1039
 
770
- def test_should_cancel_the_transaction
771
- assert @klass.cancelled_transaction
1040
+ def test_should_run_any_callbacks_that_include_failures_if_block_success_is_false
1041
+ @callbacks = []
1042
+ @machine.around_transition {|block| block.call; @callbacks << :after_around_1}
1043
+ @machine.around_transition(:include_failures => true) {|block| block.call; @callbacks << :after_around_2}
1044
+ @machine.after_transition {@callbacks << :after_1}
1045
+ @machine.after_transition(:include_failures => true) {@callbacks << :after_2}
1046
+
1047
+ assert_equal true, @transition.run_callbacks {{:success => false}}
1048
+ assert_equal [:after_around_2, :after_2], @callbacks
772
1049
  end
773
1050
  end
774
1051
 
775
- class TransitionHaltedAfterCallbackTest < Test::Unit::TestCase
1052
+ class TransitionWithAfterCallbacksSkippedTest < Test::Unit::TestCase
776
1053
  def setup
777
- @klass = Class.new do
778
- class << self; attr_accessor :cancelled_transaction; end
779
- attr_reader :saved
780
-
781
- def save
782
- @saved = true
783
- end
784
- end
785
- @before_count = 0
786
- @after_count = 0
1054
+ @klass = Class.new
787
1055
 
788
- @machine = StateMachine::Machine.new(@klass, :action => :save)
1056
+ @machine = StateMachine::Machine.new(@klass)
789
1057
  @machine.state :parked, :idling
790
1058
  @machine.event :ignite
791
- class << @machine
792
- def within_transaction(object)
793
- owner_class.cancelled_transaction = yield == false
794
- end
795
- end
796
-
797
- @machine.before_transition lambda {@before_count += 1}
798
- @machine.after_transition lambda {@after_count += 1; throw :halt}
799
- @machine.after_transition lambda {@after_count += 1}
800
1059
 
801
1060
  @object = @klass.new
802
1061
  @object.state = 'parked'
803
1062
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
804
- @result = @transition.perform
805
1063
  end
806
1064
 
807
- def test_should_be_successful
808
- assert_equal true, @result
1065
+ def test_should_run_before_callbacks
1066
+ @machine.before_transition {@run = true}
1067
+
1068
+ assert_equal true, @transition.run_callbacks(:after => false)
1069
+ assert @run
809
1070
  end
810
1071
 
811
- def test_should_change_current_state
812
- assert_equal 'idling', @object.state
1072
+ def test_should_run_around_callbacks_before_yield
1073
+ @machine.around_transition {|block| @run = true; block.call}
1074
+
1075
+ assert_equal true, @transition.run_callbacks(:after => false)
1076
+ assert @run
813
1077
  end
814
1078
 
815
- def test_should_run_before_callbacks
816
- assert_equal 1, @before_count
1079
+ def test_should_not_run_after_callbacks
1080
+ @machine.after_transition {@run = true}
1081
+
1082
+ assert_equal true, @transition.run_callbacks(:after => false)
1083
+ assert !@run
817
1084
  end
818
1085
 
819
- def test_should_not_run_further_after_callbacks
820
- assert_equal 1, @after_count
1086
+ def test_should_not_run_around_callbacks_after_yield
1087
+ @machine.around_transition {|block| block.call; @run = true}
1088
+
1089
+ assert_equal true, @transition.run_callbacks(:after => false)
1090
+ assert !@run
1091
+ end
1092
+
1093
+ def test_should_run_after_callbacks_that_include_failures_if_block_success_is_false
1094
+ @machine.after_transition(:include_failures => true) {@run = true}
1095
+
1096
+ assert_equal true, @transition.run_callbacks(:after => false) {{:success => false}}
1097
+ assert @run
1098
+ end
1099
+
1100
+ def test_should_run_around_callbacks_that_include_failures_if_block_success_is_false
1101
+ @machine.around_transition(:include_failures => true) {|block| block.call; @run = true}
1102
+
1103
+ assert_equal true, @transition.run_callbacks(:after => false) {{:success => false}}
1104
+ assert @run
1105
+ end
1106
+
1107
+ def test_should_continue_around_transition_execution_on_second_call
1108
+ @callbacks = []
1109
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1110
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2}
1111
+ @machine.after_transition {@callbacks << :after}
1112
+
1113
+ assert_equal true, @transition.run_callbacks(:after => false)
1114
+ assert_equal [:before_around_1, :before_around_2], @callbacks
1115
+
1116
+ assert_equal true, @transition.run_callbacks
1117
+ assert_equal [:before_around_1, :before_around_2, :after_around_2, :after_around_1, :after], @callbacks
1118
+ end
1119
+
1120
+ def test_should_not_run_further_callbacks_if_halted_during_continue_around_transition
1121
+ @callbacks = []
1122
+ @machine.around_transition {|block| @callbacks << :before_around_1; block.call; @callbacks << :after_around_1}
1123
+ @machine.around_transition {|block| @callbacks << :before_around_2; block.call; @callbacks << :after_around_2; throw :halt}
1124
+ @machine.after_transition {@callbacks << :after}
1125
+
1126
+ assert_equal true, @transition.run_callbacks(:after => false)
1127
+ assert_equal [:before_around_1, :before_around_2], @callbacks
1128
+
1129
+ assert_equal true, @transition.run_callbacks
1130
+ assert_equal [:before_around_1, :before_around_2, :after_around_2], @callbacks
1131
+ end
1132
+
1133
+ def test_should_not_be_able_to_continue_twice
1134
+ @count = 0
1135
+ @machine.around_transition {|block| block.call; @count += 1}
1136
+ @machine.after_transition {@count += 1}
1137
+
1138
+ @transition.run_callbacks(:after => false)
1139
+
1140
+ 2.times do
1141
+ assert_equal true, @transition.run_callbacks
1142
+ assert_equal 2, @count
1143
+ end
1144
+ end
1145
+
1146
+ def test_should_not_be_able_to_continue_again_after_halted
1147
+ @count = 0
1148
+ @machine.around_transition {|block| block.call; @count += 1; throw :halt}
1149
+ @machine.after_transition {@count += 1}
1150
+
1151
+ @transition.run_callbacks(:after => false)
1152
+
1153
+ 2.times do
1154
+ assert_equal true, @transition.run_callbacks
1155
+ assert_equal 1, @count
1156
+ end
1157
+ end
1158
+
1159
+ def test_should_have_access_to_result_after_continued
1160
+ @machine.around_transition {|block| @around_before_result = @transition.result; block.call; @around_after_result = @transition.result}
1161
+ @machine.after_transition {@after_result = @transition.result}
1162
+
1163
+ @transition.run_callbacks(:after => false)
1164
+ @transition.run_callbacks {{:result => 1}}
1165
+
1166
+ assert_nil @around_before_result
1167
+ assert_equal 1, @around_after_result
1168
+ assert_equal 1, @after_result
821
1169
  end
822
1170
 
823
- def test_should_not_cancel_the_transaction
824
- assert !@klass.cancelled_transaction
1171
+ def test_should_raise_exceptions_during_around_callbacks_after_yield_in_second_execution
1172
+ @machine.around_transition {|block| block.call; raise ArgumentError}
1173
+
1174
+ assert_nothing_raised { @transition.run_callbacks(:after => false) }
1175
+ assert_raise(ArgumentError) { @transition.run_callbacks }
825
1176
  end
826
1177
  end
827
1178
 
828
- class TransitionWithActionFailedTest < Test::Unit::TestCase
1179
+ class TransitionAfterBeingPerformedTest < Test::Unit::TestCase
829
1180
  def setup
830
1181
  @klass = Class.new do
831
- class << self; attr_accessor :cancelled_transaction; end
832
- attr_reader :saved
1182
+ attr_reader :saved, :save_state
833
1183
 
834
1184
  def save
835
- false
1185
+ @save_state = state
1186
+ @saved = true
1187
+ 1
836
1188
  end
837
1189
  end
838
- @before_count = 0
839
- @after_count = 0
840
1190
 
841
1191
  @machine = StateMachine::Machine.new(@klass, :action => :save)
842
1192
  @machine.state :parked, :idling
843
1193
  @machine.event :ignite
844
- class << @machine
845
- def within_transaction(object)
846
- owner_class.cancelled_transaction = yield == false
847
- end
848
- end
849
-
850
- @machine.before_transition lambda {@before_count += 1}
851
- @machine.after_transition lambda {@after_count += 1}
852
- @machine.after_transition lambda {@after_count += 1}, :include_failures => true
853
1194
 
854
1195
  @object = @klass.new
1196
+ @object.state = 'parked'
855
1197
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1198
+ @result = @transition.perform
856
1199
  end
857
1200
 
858
- def test_should_not_be_successful
859
- assert_equal false, @transition.perform
1201
+ def test_should_have_empty_args
1202
+ assert_equal [], @transition.args
860
1203
  end
861
1204
 
862
- def test_should_not_change_current_state
863
- @transition.perform
864
- assert_nil @object.state
1205
+ def test_should_have_a_result
1206
+ assert_equal 1, @transition.result
865
1207
  end
866
1208
 
867
- def test_should_run_before_callbacks
868
- @transition.perform
869
- assert_equal 1, @before_count
1209
+ def test_should_be_successful
1210
+ assert_equal true, @result
870
1211
  end
871
1212
 
872
- def test_should_only_run_after_callbacks_that_include_failures
873
- @transition.perform
874
- assert_equal 1, @after_count
1213
+ def test_should_change_the_current_state
1214
+ assert_equal 'idling', @object.state
875
1215
  end
876
1216
 
877
- def test_should_cancel_the_transaction
878
- @transition.perform
879
- assert @klass.cancelled_transaction
1217
+ def test_should_run_the_action
1218
+ assert @object.saved
880
1219
  end
881
1220
 
882
- def test_should_interpret_nil_as_failure
883
- @klass.class_eval do
884
- def save
885
- nil
886
- end
887
- end
888
-
889
- assert_equal false, @transition.perform
1221
+ def test_should_run_the_action_after_saving_the_state
1222
+ assert_equal 'idling', @object.save_state
890
1223
  end
891
1224
  end
892
1225
 
893
- class TransitionWithActionErrorTest < Test::Unit::TestCase
1226
+ class TransitionWithPerformArgumentsTest < Test::Unit::TestCase
894
1227
  def setup
895
1228
  @klass = Class.new do
1229
+ attr_reader :saved
1230
+
896
1231
  def save
897
- raise ArgumentError
1232
+ @saved = true
898
1233
  end
899
1234
  end
900
1235
 
@@ -905,308 +1240,125 @@ class TransitionWithActionErrorTest < Test::Unit::TestCase
905
1240
  @object = @klass.new
906
1241
  @object.state = 'parked'
907
1242
  @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
908
-
909
- @raised = true
910
- begin
911
- @transition.perform
912
- @raised = false
913
- rescue ArgumentError
914
- end
915
1243
  end
916
1244
 
917
- def test_should_not_catch_exception
918
- assert @raised
1245
+ def test_should_have_arguments
1246
+ @transition.perform(1, 2)
1247
+
1248
+ assert_equal [1, 2], @transition.args
1249
+ assert @object.saved
919
1250
  end
920
1251
 
921
- def test_should_not_change_current_state
922
- assert_equal 'parked', @object.state
1252
+ def test_should_not_include_run_action_in_arguments
1253
+ @transition.perform(1, 2, false)
1254
+
1255
+ assert_equal [1, 2], @transition.args
1256
+ assert !@object.saved
923
1257
  end
924
1258
  end
925
1259
 
926
- class TransitionsInParallelTest < Test::Unit::TestCase
1260
+ class TransitionWithoutRunningActionTest < Test::Unit::TestCase
927
1261
  def setup
928
1262
  @klass = Class.new do
929
- attr_reader :persisted
930
-
931
- def initialize
932
- @persisted = []
933
- @state = 'parked'
934
- @status = 'first_gear'
935
- super
936
- end
937
-
938
- def state=(value)
939
- @persisted << value
940
- @state = value
941
- end
1263
+ attr_reader :saved
942
1264
 
943
- def status=(value)
944
- @persisted << value
945
- @status = value
1265
+ def save
1266
+ @saved = true
946
1267
  end
947
1268
  end
948
1269
 
949
- @state = StateMachine::Machine.new(@klass, :state)
950
- @state.event :ignite
951
- @state.state :parked, :idling
952
-
953
- @status = StateMachine::Machine.new(@klass, :status)
954
- @status.event :shift_up
955
- @status.state :first_gear, :second_gear
956
-
957
- @object = @klass.new
958
- @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
959
- @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
960
-
961
- @result = StateMachine::Transition.perform([@state_transition, @status_transition])
962
- end
963
-
964
- def test_should_raise_exception_if_attempted_on_the_same_state_machine
965
- exception = assert_raise(ArgumentError) { StateMachine::Transition.perform([@state_transition, @state_transition]) }
966
- assert_equal 'Cannot perform multiple transitions in parallel for the same state machine attribute', exception.message
967
- end
968
-
969
- def test_should_perform
970
- assert_equal true, @result
971
- end
972
-
973
- def test_should_persist_first_state
974
- assert_equal 'idling', @object.state
975
- end
976
-
977
- def test_should_persist_second_state
978
- assert_equal 'second_gear', @object.status
979
- end
980
-
981
- def test_should_persist_in_order
982
- assert_equal ['idling', 'second_gear'], @object.persisted
983
- end
984
-
985
- def test_should_have_args_in_transitions
986
- assert_equal [], @state_transition.args
987
- assert_equal [], @status_transition.args
988
- end
989
- end
990
-
991
- class TransitionsInParallelWithCallbacksTest < Test::Unit::TestCase
992
- def setup
993
- @klass = Class.new
994
-
995
- @before_callbacks = []
996
- @after_callbacks = []
997
-
998
- @state = StateMachine::Machine.new(@klass, :state, :initial => :parked)
999
- @state.event :ignite
1000
- @state.state :parked, :idling
1001
- @state.before_transition lambda {@before_callbacks << :state}
1002
- @state.after_transition lambda {@after_callbacks << :state}
1003
-
1004
- @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
1005
- @status.event :shift_up
1006
- @status.state :first_gear, :second_gear
1007
- @status.before_transition lambda {@before_callbacks << :status}
1008
- @status.after_transition lambda {@after_callbacks << :status}
1270
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
1271
+ @machine.state :parked, :idling
1272
+ @machine.event :ignite
1273
+ @machine.after_transition {|object| @run_after = true}
1009
1274
 
1010
1275
  @object = @klass.new
1011
- @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
1012
- @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1013
- end
1014
-
1015
- def test_should_run_before_callbacks_in_order
1016
- perform
1017
- assert_equal [:state, :status], @before_callbacks
1018
- end
1019
-
1020
- def test_should_run_after_callbacks_in_order
1021
- perform
1022
- assert_equal [:state, :status], @after_callbacks
1276
+ @object.state = 'parked'
1277
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1278
+ @result = @transition.perform(false)
1023
1279
  end
1024
1280
 
1025
- def test_should_not_run_after_callbacks_if_disabled
1026
- perform(:after => false)
1027
- assert_equal [], @after_callbacks
1281
+ def test_should_have_empty_args
1282
+ assert_equal [], @transition.args
1028
1283
  end
1029
1284
 
1030
- def test_should_halt_if_before_callback_halted_for_first_transition
1031
- @state.before_transition lambda {throw :halt}
1032
-
1033
- assert_equal false, perform
1034
- assert_equal [:state], @before_callbacks
1035
- assert_equal 'parked', @object.state
1036
- assert_equal 'first_gear', @object.status
1037
- assert_equal [], @after_callbacks
1285
+ def test_should_not_have_a_result
1286
+ assert_nil @transition.result
1038
1287
  end
1039
1288
 
1040
- def test_should_halt_if_before_callback_halted_for_second_transition
1041
- @status.before_transition lambda {throw :halt}
1042
-
1043
- assert_equal false, perform
1044
- assert_equal [:state, :status], @before_callbacks
1045
- assert_equal 'parked', @object.state
1046
- assert_equal 'first_gear', @object.status
1047
- assert_equal [], @after_callbacks
1289
+ def test_should_be_successful
1290
+ assert_equal true, @result
1048
1291
  end
1049
1292
 
1050
- def test_should_perform_if_after_callback_halted_for_first_transition
1051
- @state.after_transition lambda {throw :halt}
1052
- @state.after_transition lambda {@after_callbacks << :invalid}
1053
-
1054
- assert_equal true, perform
1055
- assert_equal [:state, :status], @before_callbacks
1293
+ def test_should_change_the_current_state
1056
1294
  assert_equal 'idling', @object.state
1057
- assert_equal 'second_gear', @object.status
1058
- assert_equal [:state, :status], @after_callbacks
1059
1295
  end
1060
1296
 
1061
- def test_should_perform_if_after_callback_halted_for_second_transition
1062
- @status.after_transition lambda {throw :halt}
1063
- @status.after_transition lambda {@after_callbacks << :invalid}
1064
-
1065
- assert_equal true, perform
1066
- assert_equal [:state, :status], @before_callbacks
1067
- assert_equal 'idling', @object.state
1068
- assert_equal 'second_gear', @object.status
1069
- assert_equal [:state, :status], @after_callbacks
1297
+ def test_should_not_run_the_action
1298
+ assert !@object.saved
1070
1299
  end
1071
1300
 
1072
- private
1073
- def perform(options = {})
1074
- StateMachine::Transition.perform([@state_transition, @status_transition], options)
1075
- end
1301
+ def test_should_run_after_callbacks
1302
+ assert @run_after
1303
+ end
1076
1304
  end
1077
1305
 
1078
- class TransitionsInParallelWithActionsTest < Test::Unit::TestCase
1306
+ class TransitionWithTransactionsTest < Test::Unit::TestCase
1079
1307
  def setup
1080
1308
  @klass = Class.new do
1081
- attr_reader :actions
1082
-
1083
- def save_state
1084
- (@actions ||= []) << :save_state
1085
- :save_state
1309
+ class << self
1310
+ attr_accessor :running_transaction
1086
1311
  end
1087
1312
 
1088
- def save_status
1089
- (@actions ||= []) << :save_status
1090
- :save_status
1313
+ attr_accessor :result
1314
+
1315
+ def save
1316
+ @result = self.class.running_transaction
1317
+ true
1091
1318
  end
1092
1319
  end
1093
1320
 
1094
- @state = StateMachine::Machine.new(@klass, :state, :initial => :parked, :action => :save_state)
1095
- @state.event :ignite
1096
- @state.state :parked, :idling
1097
-
1098
- @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear, :action => :save_status)
1099
- @status.event :shift_up
1100
- @status.state :first_gear, :second_gear
1321
+ @machine = StateMachine::Machine.new(@klass, :action => :save)
1322
+ @machine.state :parked, :idling
1323
+ @machine.event :ignite
1101
1324
 
1102
1325
  @object = @klass.new
1103
- @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
1104
- @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1105
- end
1106
-
1107
- def test_should_run_actions_in_order
1108
- perform
1109
- assert_equal [:save_state, :save_status], @object.actions
1110
- end
1111
-
1112
- def test_should_store_action_specific_results
1113
- perform
1114
- assert_equal :save_state, @state_transition.result
1115
- assert_equal :save_status, @status_transition.result
1116
- end
1117
-
1118
- def test_should_not_perform_if_action_fails_for_first_transition
1119
- @klass.class_eval do
1120
- def save_state
1121
- false
1122
- end
1123
- end
1124
-
1125
- assert_equal false, perform
1126
- assert_equal 'parked', @object.state
1127
- assert_equal 'first_gear', @object.status
1128
- end
1129
-
1130
- def test_should_not_perform_if_action_fails_for_second_transition
1131
- @klass.class_eval do
1132
- def save_status
1133
- false
1134
- end
1135
- end
1326
+ @object.state = 'parked'
1327
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1136
1328
 
1137
- assert_equal false, perform
1138
- assert_equal 'parked', @object.state
1139
- assert_equal 'first_gear', @object.status
1140
- end
1141
-
1142
- def test_should_rollback_if_action_errors_for_first_transition
1143
- @klass.class_eval do
1144
- def save_state
1145
- raise ArgumentError
1329
+ class << @machine
1330
+ def within_transaction(object)
1331
+ owner_class.running_transaction = object
1332
+ yield
1333
+ owner_class.running_transaction = false
1146
1334
  end
1147
1335
  end
1148
-
1149
- begin; perform; rescue; end
1150
- assert_equal 'parked', @object.state
1151
- assert_equal 'first_gear', @object.status
1152
1336
  end
1153
1337
 
1154
- def test_should_rollback_if_action_errors_for_second_transition
1155
- @klass.class_eval do
1156
- def save_status
1157
- raise ArgumentError
1158
- end
1338
+ def test_should_run_blocks_within_transaction_for_object
1339
+ @transition.within_transaction do
1340
+ @result = @klass.running_transaction
1159
1341
  end
1160
1342
 
1161
- begin; perform; rescue; end
1162
- assert_equal 'parked', @object.state
1163
- assert_equal 'first_gear', @object.status
1343
+ assert_equal @object, @result
1164
1344
  end
1165
-
1166
- private
1167
- def perform(options = {})
1168
- StateMachine::Transition.perform([@state_transition, @status_transition], options)
1169
- end
1170
1345
  end
1171
1346
 
1172
- class TransitionsWithPerformBlockTest < Test::Unit::TestCase
1347
+ class TransitionTransientTest < Test::Unit::TestCase
1173
1348
  def setup
1174
1349
  @klass = Class.new
1175
1350
 
1176
- @state = StateMachine::Machine.new(@klass, :state, :initial => :parked)
1177
- @state.event :ignite
1178
- @state.state :parked, :idling
1179
-
1180
- @status = StateMachine::Machine.new(@klass, :status, :initial => :first_gear)
1181
- @status.event :shift_up
1182
- @status.state :first_gear, :second_gear
1351
+ @machine = StateMachine::Machine.new(@klass)
1352
+ @machine.state :parked, :idling
1353
+ @machine.event :ignite
1183
1354
 
1184
1355
  @object = @klass.new
1185
- @state_transition = StateMachine::Transition.new(@object, @state, :ignite, :parked, :idling)
1186
- @status_transition = StateMachine::Transition.new(@object, @status, :shift_up, :first_gear, :second_gear)
1187
- end
1188
-
1189
- def test_should_be_perform_if_result_is_not_false
1190
- assert_equal true, StateMachine::Transition.perform([@state_transition, @status_transition]) { true }
1191
- assert_equal 'idling', @object.state
1192
- assert_equal 'second_gear', @object.status
1193
- end
1194
-
1195
- def test_should_not_perform_if_result_is_false
1196
- assert_equal false, StateMachine::Transition.perform([@state_transition, @status_transition]) { false }
1197
- assert_equal 'parked', @object.state
1198
- assert_equal 'first_gear', @object.status
1199
- end
1200
-
1201
- def test_should_not_perform_if_result_is_nil
1202
- assert_equal false, StateMachine::Transition.perform([@state_transition, @status_transition]) { nil }
1203
- assert_equal 'parked', @object.state
1204
- assert_equal 'first_gear', @object.status
1356
+ @object.state = 'parked'
1357
+ @transition = StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)
1358
+ @transition.transient = true
1205
1359
  end
1206
1360
 
1207
- def test_should_use_result_as_transition_result
1208
- StateMachine::Transition.perform([@state_transition, @status_transition]) { 1 }
1209
- assert_equal 1, @state_transition.result
1210
- assert_equal 1, @status_transition.result
1361
+ def test_should_be_transient
1362
+ assert @transition.transient?
1211
1363
  end
1212
1364
  end