state_machine 0.7.6 → 0.8.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.
@@ -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