state_machine 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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