state_machine 0.7.6 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -404,6 +404,52 @@ class GuardWithMultipleExceptOnRequirementsTest < Test::Unit::TestCase
404
404
  end
405
405
  end
406
406
 
407
+ class GuardWithFailuresExcludedTest < Test::Unit::TestCase
408
+ def setup
409
+ @object = Object.new
410
+ @guard = StateMachine::Guard.new(:include_failures => false)
411
+ end
412
+
413
+ def test_should_use_a_blacklist_matcher
414
+ assert_instance_of StateMachine::WhitelistMatcher, @guard.success_requirement
415
+ end
416
+
417
+ def test_should_match_if_not_specified
418
+ assert @guard.matches?(@object)
419
+ end
420
+
421
+ def test_should_match_if_true
422
+ assert @guard.matches?(@object, :success => true)
423
+ end
424
+
425
+ def test_should_not_match_if_false
426
+ assert !@guard.matches?(@object, :success => false)
427
+ end
428
+ end
429
+
430
+ class GuardWithFailuresIncludedTest < Test::Unit::TestCase
431
+ def setup
432
+ @object = Object.new
433
+ @guard = StateMachine::Guard.new(:include_failures => true)
434
+ end
435
+
436
+ def test_should_use_all_matcher
437
+ assert_equal StateMachine::AllMatcher.instance, @guard.success_requirement
438
+ end
439
+
440
+ def test_should_match_if_not_specified
441
+ assert @guard.matches?(@object)
442
+ end
443
+
444
+ def test_should_match_if_true
445
+ assert @guard.matches?(@object, :success => true)
446
+ end
447
+
448
+ def test_should_match_if_false
449
+ assert @guard.matches?(@object, :success => false)
450
+ end
451
+ end
452
+
407
453
  class GuardWithConflictingFromRequirementsTest < Test::Unit::TestCase
408
454
  def test_should_raise_an_exception
409
455
  exception = assert_raise(ArgumentError) { StateMachine::Guard.new(:from => :parked, :except_from => :parked) }
@@ -168,15 +168,14 @@ begin
168
168
 
169
169
  @machine.invalidate(record, :state, :invalid_transition, [[:event, :park]])
170
170
 
171
- assert record.errors.invalid?(:state)
172
- assert_equal 'cannot transition via "park"', record.errors.on(:state)
171
+ assert_equal ['State cannot transition via "park"'], record.errors.full_messages
173
172
  end
174
173
 
175
174
  def test_should_auto_prefix_custom_attributes_on_invalidation
176
175
  record = @model.new
177
176
  @machine.invalidate(record, :event, :invalid)
178
177
 
179
- assert_equal 'is invalid', record.errors.on(:state_event)
178
+ assert_equal ['State event is invalid'], record.errors.full_messages
180
179
  end
181
180
 
182
181
  def test_should_clear_errors_on_reset
@@ -185,7 +184,7 @@ begin
185
184
  record.errors.add(:state, 'is invalid')
186
185
 
187
186
  @machine.reset(record)
188
- assert_nil record.errors.on(:id)
187
+ assert_equal [], record.errors.full_messages
189
188
  end
190
189
 
191
190
  def test_should_not_override_the_column_reader
@@ -229,15 +228,126 @@ begin
229
228
  end
230
229
  end
231
230
 
232
- class MachineWithInitialStateTest < ActiveRecord::TestCase
231
+ class MachineWithStaticInitialStateTest < ActiveRecord::TestCase
233
232
  def setup
234
- @model = new_model
233
+ @model = new_model do
234
+ attr_accessor :value
235
+ end
235
236
  @machine = StateMachine::Machine.new(@model, :initial => :parked)
236
- @record = @model.new
237
237
  end
238
238
 
239
239
  def test_should_set_initial_state_on_created_object
240
- assert_equal 'parked', @record.state
240
+ record = @model.new
241
+ assert_equal 'parked', record.state
242
+ end
243
+
244
+ def test_should_still_set_attributes
245
+ record = @model.new(:value => 1)
246
+ assert_equal 1, record.value
247
+ end
248
+
249
+ def test_should_still_allow_initialize_blocks
250
+ block_args = nil
251
+ record = @model.new do |*args|
252
+ block_args = args
253
+ end
254
+
255
+ assert_equal [record], block_args
256
+ end
257
+
258
+ def test_should_set_attributes_prior_to_after_initialize_hook
259
+ state = nil
260
+ @model.class_eval do
261
+ define_method(:after_initialize) do
262
+ state = self.state
263
+ end
264
+ end
265
+ @model.new
266
+ assert_equal 'parked', state
267
+ end
268
+
269
+ def test_should_set_initial_state_before_setting_attributes
270
+ @model.class_eval do
271
+ attr_accessor :state_during_setter
272
+
273
+ define_method(:value=) do |value|
274
+ self.state_during_setter = state
275
+ end
276
+ end
277
+
278
+ record = @model.new(:value => 1)
279
+ assert_equal 'parked', record.state_during_setter
280
+ end
281
+
282
+ def test_should_not_set_initial_state_after_already_initialized
283
+ record = @model.new(:value => 1)
284
+ assert_equal 'parked', record.state
285
+
286
+ record.state = 'idling'
287
+ record.attributes = {}
288
+ assert_equal 'idling', record.state
289
+ end
290
+ end
291
+
292
+ class MachineWithDynamicInitialStateTest < ActiveRecord::TestCase
293
+ def setup
294
+ @model = new_model do
295
+ attr_accessor :value
296
+ end
297
+ @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
298
+ @machine.state :parked
299
+ end
300
+
301
+ def test_should_set_initial_state_on_created_object
302
+ record = @model.new
303
+ assert_equal 'parked', record.state
304
+ end
305
+
306
+ def test_should_still_set_attributes
307
+ record = @model.new(:value => 1)
308
+ assert_equal 1, record.value
309
+ end
310
+
311
+ def test_should_still_allow_initialize_blocks
312
+ block_args = nil
313
+ record = @model.new do |*args|
314
+ block_args = args
315
+ end
316
+
317
+ assert_equal [record], block_args
318
+ end
319
+
320
+ def test_should_set_attributes_prior_to_after_initialize_hook
321
+ state = nil
322
+ @model.class_eval do
323
+ define_method(:after_initialize) do
324
+ state = self.state
325
+ end
326
+ end
327
+ @model.new
328
+ assert_equal 'parked', state
329
+ end
330
+
331
+ def test_should_set_initial_state_after_setting_attributes
332
+ @model.class_eval do
333
+ attr_accessor :state_during_setter
334
+
335
+ define_method(:value=) do |value|
336
+ self.state_during_setter = state || 'nil'
337
+ end
338
+ end
339
+
340
+ record = @model.new(:value => 1)
341
+ assert_equal 'nil', record.state_during_setter
342
+ end
343
+
344
+ def test_should_not_set_initial_state_after_already_initialized
345
+ record = @model.new(:value => 1)
346
+ assert_equal 'parked', record.state
347
+
348
+ record.state = 'idling'
349
+ record.attributes = {}
350
+ assert_equal 'idling', record.state
241
351
  end
242
352
  end
243
353
 
@@ -395,28 +505,31 @@ begin
395
505
 
396
506
  class MachineWithCustomAttributeTest < ActiveRecord::TestCase
397
507
  def setup
398
- @model = new_model
399
- @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
508
+ @model = new_model do
509
+ alias_attribute :vehicle_status, :state
510
+ end
511
+
512
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
400
513
  @machine.state :parked
401
514
 
402
515
  @record = @model.new
403
516
  end
404
517
 
405
518
  def test_should_add_validation_errors_to_custom_attribute
406
- @record.state = 'invalid'
519
+ @record.vehicle_status = 'invalid'
407
520
 
408
521
  assert !@record.valid?
409
- assert_equal ['State is invalid'], @record.errors.full_messages
522
+ assert_equal ['Vehicle status is invalid'], @record.errors.full_messages
410
523
 
411
- @record.state = 'parked'
524
+ @record.vehicle_status = 'parked'
412
525
  assert @record.valid?
413
526
  end
414
527
 
415
528
  def test_should_check_custom_attribute_for_predicate
416
- @record.state = nil
529
+ @record.vehicle_status = nil
417
530
  assert !@record.status?(:parked)
418
531
 
419
- @record.state = 'parked'
532
+ @record.vehicle_status = 'parked'
420
533
  assert @record.status?(:parked)
421
534
  end
422
535
  end
@@ -528,9 +641,46 @@ begin
528
641
  end
529
642
  end
530
643
 
644
+ class MachineWithLoopbackTest < ActiveRecord::TestCase
645
+ def setup
646
+ changed_attrs = nil
647
+
648
+ @model = new_model do
649
+ connection.change_table(:foo) {|t| t.datetime(:updated_at)}
650
+
651
+ define_method(:before_update) do
652
+ changed_attrs = changed_attributes.dup
653
+ end
654
+ end
655
+
656
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
657
+ @machine.event :park
658
+
659
+ @record = @model.create(:updated_at => Time.now - 1)
660
+ @timestamp = @record.updated_at
661
+
662
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
663
+ @transition.perform
664
+
665
+ @changed_attrs = changed_attrs
666
+ end
667
+
668
+ def test_should_include_state_in_changed_attributes
669
+ @changed_attrs.delete('updated_at')
670
+
671
+ expected = {'state' => 'parked'}
672
+ assert_equal expected, @changed_attrs
673
+ end
674
+
675
+ def test_should_update_record
676
+ assert_not_equal @timestamp, @record.updated_at
677
+ end
678
+ end
679
+
531
680
  class MachineWithFailedBeforeCallbacksTest < ActiveRecord::TestCase
532
681
  def setup
533
682
  @before_count = 0
683
+ @after_count = 0
534
684
 
535
685
  @model = new_model
536
686
  @machine = StateMachine::Machine.new(@model)
@@ -538,6 +688,7 @@ begin
538
688
  @machine.event :ignite
539
689
  @machine.before_transition(lambda {@before_count += 1; false})
540
690
  @machine.before_transition(lambda {@before_count += 1})
691
+ @machine.after_transition(lambda {@after_count += 1})
541
692
 
542
693
  @record = @model.new(:state => 'parked')
543
694
  @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
@@ -559,6 +710,10 @@ begin
559
710
  def test_should_not_run_further_before_callbacks
560
711
  assert_equal 1, @before_count
561
712
  end
713
+
714
+ def test_should_not_run_after_callbacks
715
+ assert_equal 0, @after_count
716
+ end
562
717
  end
563
718
 
564
719
  class MachineWithFailedActionTest < ActiveRecord::TestCase
@@ -570,6 +725,14 @@ begin
570
725
  @machine = StateMachine::Machine.new(@model)
571
726
  @machine.state :parked, :idling
572
727
  @machine.event :ignite
728
+
729
+ @before_transition_called = false
730
+ @after_transition_called = false
731
+ @after_transition_with_failures_called = false
732
+ @machine.before_transition(lambda {@before_transition_called = true})
733
+ @machine.after_transition(lambda {@after_transition_called = true})
734
+ @machine.after_transition(lambda {@after_transition_with_failures_called = true}, :include_failures => true)
735
+
573
736
  @record = @model.new(:state => 'parked')
574
737
  @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
575
738
  @result = @transition.perform
@@ -586,6 +749,18 @@ begin
586
749
  def test_should_not_save_record
587
750
  assert @record.new_record?
588
751
  end
752
+
753
+ def test_should_run_before_callback
754
+ assert @before_transition_called
755
+ end
756
+
757
+ def test_should_not_run_after_callback_if_not_including_failures
758
+ assert !@after_transition_called
759
+ end
760
+
761
+ def test_should_run_after_callback_if_including_failures
762
+ assert @after_transition_with_failures_called
763
+ end
589
764
  end
590
765
 
591
766
  class MachineWithValidationsTest < ActiveRecord::TestCase
@@ -722,6 +897,32 @@ begin
722
897
  @record.valid?
723
898
  assert !ran_callback
724
899
  end
900
+
901
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
902
+ @model.class_eval do
903
+ attr_accessor :seatbelt
904
+ validates_presence_of :seatbelt
905
+ end
906
+
907
+ ran_callback = false
908
+ @machine.after_transition { ran_callback = true }
909
+
910
+ @record.valid?
911
+ assert !ran_callback
912
+ end
913
+
914
+ def test_should_run_after_callbacks_with_failures_enabled_if_validation_fails
915
+ @model.class_eval do
916
+ attr_accessor :seatbelt
917
+ validates_presence_of :seatbelt
918
+ end
919
+
920
+ ran_callback = false
921
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
922
+
923
+ @record.valid?
924
+ assert ran_callback
925
+ end
725
926
  end
726
927
 
727
928
  class MachineWithEventAttributesOnSaveBangTest < ActiveRecord::TestCase
@@ -759,6 +960,14 @@ begin
759
960
  assert ran_callback
760
961
  end
761
962
 
963
+ def test_should_run_before_callbacks_once
964
+ before_count = 0
965
+ @machine.before_transition { before_count += 1 }
966
+
967
+ @record.save!
968
+ assert_equal 1, before_count
969
+ end
970
+
762
971
  def test_should_persist_new_state
763
972
  @record.save!
764
973
  assert_equal 'idling', @record.state
@@ -771,6 +980,26 @@ begin
771
980
  @record.save!
772
981
  assert ran_callback
773
982
  end
983
+
984
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
985
+ @model.before_create {|record| false}
986
+
987
+ ran_callback = false
988
+ @machine.after_transition { ran_callback = true }
989
+
990
+ begin; @record.save!; rescue; end
991
+ assert !ran_callback
992
+ end
993
+
994
+ def test_should_run_after_callbacks_with_failures_enabled_if_fails
995
+ @model.before_create {|record| false}
996
+
997
+ ran_callback = false
998
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
999
+
1000
+ begin; @record.save!; rescue; end
1001
+ assert ran_callback
1002
+ end
774
1003
  end
775
1004
 
776
1005
  class MachineWithEventAttributesOnCustomActionTest < ActiveRecord::TestCase
@@ -1004,7 +1233,7 @@ begin
1004
1233
  record = @model.new(:state => 'idling')
1005
1234
 
1006
1235
  machine.invalidate(record, :state, :invalid_transition, [[:event, :ignite]])
1007
- assert_equal 'cannot ignite', record.errors.on(:state)
1236
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1008
1237
  end
1009
1238
 
1010
1239
  def test_should_invalidate_using_customized_i18n_key_if_specified
@@ -1024,7 +1253,7 @@ begin
1024
1253
  record = @model.new(:state => 'idling')
1025
1254
 
1026
1255
  machine.invalidate(record, :state, :invalid_transition, [[:event, :ignite]])
1027
- assert_equal 'cannot ignite', record.errors.on(:state)
1256
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1028
1257
  end
1029
1258
 
1030
1259
  def test_should_invalidate_using_customized_i18n_string_if_specified
@@ -1034,7 +1263,7 @@ begin
1034
1263
  record = @model.new(:state => 'idling')
1035
1264
 
1036
1265
  machine.invalidate(record, :state, :invalid_transition, [[:event, :ignite]])
1037
- assert_equal 'cannot ignite', record.errors.on(:state)
1266
+ assert_equal ['State cannot ignite'], record.errors.full_messages
1038
1267
  end
1039
1268
 
1040
1269
  def test_should_only_add_locale_once_in_load_path
@@ -25,7 +25,7 @@ begin
25
25
  storage_names[:default] = 'foo'
26
26
  def self.name; 'DataMapperTest::Foo'; end
27
27
 
28
- property :id, Integer, :serial => true
28
+ property :id, DataMapper::Types::Serial
29
29
  property :state, String
30
30
 
31
31
  auto_migrate! if auto_migrate
@@ -375,6 +375,45 @@ begin
375
375
  end
376
376
  end
377
377
 
378
+ class MachineWithLoopbackTest < BaseTestCase
379
+ def setup
380
+ dirty_attributes = nil
381
+
382
+ @resource = new_resource do
383
+ property :updated_at, DateTime
384
+ auto_migrate!
385
+
386
+ # Simulate dm-timestamps
387
+ before :update do
388
+ dirty_attributes = self.dirty_attributes.dup
389
+
390
+ return unless dirty?
391
+ self.updated_at = DateTime.now
392
+ end
393
+ end
394
+
395
+ @machine = StateMachine::Machine.new(@resource, :initial => :parked)
396
+ @machine.event :park
397
+
398
+ @record = @resource.create(:updated_at => Time.now - 1)
399
+ @timestamp = @record.updated_at
400
+
401
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
402
+ @transition.perform
403
+
404
+ @dirty_attributes = dirty_attributes
405
+ end
406
+
407
+ def test_should_include_state_in_dirty_attributes
408
+ expected = {@resource.properties[:state] => 'parked'}
409
+ assert_equal expected, @dirty_attributes
410
+ end
411
+
412
+ def test_should_update_record
413
+ assert_not_equal @timestamp, @record.updated_at
414
+ end
415
+ end
416
+
378
417
  begin
379
418
  gem 'dm-observer', ENV['DM_VERSION'] ? "=#{ENV['DM_VERSION']}" : '>=0.9.4'
380
419
  require 'dm-observer'
@@ -721,6 +760,109 @@ begin
721
760
  @record.valid?
722
761
  assert !ran_callback
723
762
  end
763
+
764
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
765
+ @resource.class_eval do
766
+ attr_accessor :seatbelt
767
+ validates_present :seatbelt
768
+ end
769
+
770
+ ran_callback = false
771
+ @machine.after_transition { ran_callback = true }
772
+
773
+ @record.valid?
774
+ assert !ran_callback
775
+ end
776
+
777
+ def test_should_run_after_callbacks_with_failures_enabled_if_validation_fails
778
+ @resource.class_eval do
779
+ attr_accessor :seatbelt
780
+ validates_present :seatbelt
781
+ end
782
+
783
+ ran_callback = false
784
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
785
+
786
+ @record.valid?
787
+ assert ran_callback
788
+ end
789
+ end
790
+
791
+ class MachineWithEventAttributesOnSaveTest < BaseTestCase
792
+ def setup
793
+ @resource = new_resource
794
+ @machine = StateMachine::Machine.new(@resource)
795
+ @machine.event :ignite do
796
+ transition :parked => :idling
797
+ end
798
+
799
+ @record = @resource.new
800
+ @record.state = 'parked'
801
+ @record.state_event = 'ignite'
802
+ end
803
+
804
+ def test_should_fail_if_event_is_invalid
805
+ @record.state_event = 'invalid'
806
+ assert !@record.save
807
+ end
808
+
809
+ def test_should_fail_if_event_has_no_transition
810
+ @record.state = 'idling'
811
+ assert !@record.save
812
+ end
813
+
814
+ def test_should_be_successful_if_event_has_transition
815
+ assert_equal true, @record.save
816
+ end
817
+
818
+ def test_should_run_before_callbacks
819
+ ran_callback = false
820
+ @machine.before_transition { ran_callback = true }
821
+
822
+ @record.save
823
+ assert ran_callback
824
+ end
825
+
826
+ def test_should_run_before_callbacks_once
827
+ before_count = 0
828
+ @machine.before_transition { before_count += 1 }
829
+
830
+ @record.save
831
+ assert_equal 1, before_count
832
+ end
833
+
834
+ def test_should_persist_new_state
835
+ @record.save
836
+ assert_equal 'idling', @record.state
837
+ end
838
+
839
+ def test_should_run_after_callbacks
840
+ ran_callback = false
841
+ @machine.after_transition { ran_callback = true }
842
+
843
+ @record.save
844
+ assert ran_callback
845
+ end
846
+
847
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
848
+ @resource.before(:create) { throw :halt }
849
+
850
+ ran_callback = false
851
+ @machine.after_transition { ran_callback = true }
852
+
853
+ @record.save
854
+ assert !ran_callback
855
+ end
856
+
857
+ def test_should_run_after_callbacks_with_failures_enabled_if_fails
858
+ @resource.before(:create) { throw :halt }
859
+
860
+ ran_callback = false
861
+ @machine.after_transition(:include_failures => true) { ran_callback = true }
862
+
863
+ @record.save
864
+ assert ran_callback
865
+ end
724
866
  end
725
867
 
726
868
  class MachineWithEventAttributesOnCustomActionTest < BaseTestCase