state_machine 0.9.4 → 0.10.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.
- data/CHANGELOG.rdoc +20 -0
- data/LICENSE +1 -1
- data/README.rdoc +74 -4
- data/Rakefile +3 -3
- data/lib/state_machine.rb +51 -24
- data/lib/state_machine/{guard.rb → branch.rb} +34 -40
- data/lib/state_machine/callback.rb +13 -18
- data/lib/state_machine/error.rb +13 -0
- data/lib/state_machine/eval_helpers.rb +3 -0
- data/lib/state_machine/event.rb +67 -30
- data/lib/state_machine/event_collection.rb +20 -3
- data/lib/state_machine/extensions.rb +3 -3
- data/lib/state_machine/integrations.rb +7 -0
- data/lib/state_machine/integrations/active_model.rb +149 -59
- data/lib/state_machine/integrations/active_model/versions.rb +30 -0
- data/lib/state_machine/integrations/active_record.rb +74 -148
- data/lib/state_machine/integrations/active_record/locale.rb +0 -7
- data/lib/state_machine/integrations/active_record/versions.rb +149 -0
- data/lib/state_machine/integrations/base.rb +64 -0
- data/lib/state_machine/integrations/data_mapper.rb +50 -39
- data/lib/state_machine/integrations/data_mapper/observer.rb +47 -12
- data/lib/state_machine/integrations/data_mapper/versions.rb +62 -0
- data/lib/state_machine/integrations/mongo_mapper.rb +37 -64
- data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
- data/lib/state_machine/integrations/mongo_mapper/versions.rb +102 -0
- data/lib/state_machine/integrations/mongoid.rb +297 -0
- data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
- data/lib/state_machine/integrations/mongoid/versions.rb +18 -0
- data/lib/state_machine/integrations/sequel.rb +99 -55
- data/lib/state_machine/integrations/sequel/versions.rb +40 -0
- data/lib/state_machine/machine.rb +273 -136
- data/lib/state_machine/machine_collection.rb +21 -13
- data/lib/state_machine/node_collection.rb +6 -1
- data/lib/state_machine/path.rb +120 -0
- data/lib/state_machine/path_collection.rb +90 -0
- data/lib/state_machine/state.rb +28 -9
- data/lib/state_machine/state_collection.rb +1 -1
- data/lib/state_machine/transition.rb +65 -6
- data/lib/state_machine/transition_collection.rb +1 -1
- data/test/files/en.yml +8 -0
- data/test/functional/state_machine_test.rb +15 -2
- data/test/unit/branch_test.rb +890 -0
- data/test/unit/callback_test.rb +9 -36
- data/test/unit/error_test.rb +43 -0
- data/test/unit/event_collection_test.rb +67 -33
- data/test/unit/event_test.rb +165 -38
- data/test/unit/integrations/active_model_test.rb +103 -3
- data/test/unit/integrations/active_record_test.rb +90 -43
- data/test/unit/integrations/base_test.rb +87 -0
- data/test/unit/integrations/data_mapper_test.rb +105 -44
- data/test/unit/integrations/mongo_mapper_test.rb +261 -64
- data/test/unit/integrations/mongoid_test.rb +1529 -0
- data/test/unit/integrations/sequel_test.rb +33 -49
- data/test/unit/integrations_test.rb +4 -0
- data/test/unit/invalid_event_test.rb +15 -2
- data/test/unit/invalid_parallel_transition_test.rb +18 -0
- data/test/unit/invalid_transition_test.rb +72 -2
- data/test/unit/machine_collection_test.rb +55 -61
- data/test/unit/machine_test.rb +388 -26
- data/test/unit/node_collection_test.rb +14 -4
- data/test/unit/path_collection_test.rb +266 -0
- data/test/unit/path_test.rb +485 -0
- data/test/unit/state_collection_test.rb +30 -0
- data/test/unit/state_test.rb +82 -35
- data/test/unit/transition_collection_test.rb +48 -44
- data/test/unit/transition_test.rb +198 -41
- metadata +111 -74
- data/test/unit/guard_test.rb +0 -909
data/test/unit/machine_test.rb
CHANGED
@@ -43,6 +43,10 @@ class MachineByDefaultTest < Test::Unit::TestCase
|
|
43
43
|
assert @machine.callbacks[:after].empty?
|
44
44
|
end
|
45
45
|
|
46
|
+
def test_should_not_have_any_failure_callbacks
|
47
|
+
assert @machine.callbacks[:failure].empty?
|
48
|
+
end
|
49
|
+
|
46
50
|
def test_should_not_have_an_action
|
47
51
|
assert_nil @machine.action
|
48
52
|
end
|
@@ -69,6 +73,10 @@ class MachineByDefaultTest < Test::Unit::TestCase
|
|
69
73
|
assert_equal 'cannot transition via "park"', @machine.generate_message(:invalid_transition, [[:event, :park]])
|
70
74
|
end
|
71
75
|
|
76
|
+
def test_should_not_be_extended_by_the_base_integration
|
77
|
+
assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::Base)
|
78
|
+
end
|
79
|
+
|
72
80
|
def test_should_not_be_extended_by_the_active_model_integration
|
73
81
|
assert !(class << @machine; ancestors; end).include?(StateMachine::Integrations::ActiveModel)
|
74
82
|
end
|
@@ -113,6 +121,10 @@ class MachineByDefaultTest < Test::Unit::TestCase
|
|
113
121
|
assert @object.respond_to?(:state_transitions)
|
114
122
|
end
|
115
123
|
|
124
|
+
def test_should_define_a_path_reader_for_the_attribute
|
125
|
+
assert @object.respond_to?(:state_paths)
|
126
|
+
end
|
127
|
+
|
116
128
|
def test_should_not_define_an_event_attribute_reader
|
117
129
|
assert !@object.respond_to?(:state_event)
|
118
130
|
end
|
@@ -219,6 +231,37 @@ class MachineWithCustomNameTest < Test::Unit::TestCase
|
|
219
231
|
end
|
220
232
|
end
|
221
233
|
|
234
|
+
class MachineWithoutInitializationTest < Test::Unit::TestCase
|
235
|
+
def setup
|
236
|
+
@klass = Class.new do
|
237
|
+
def initialize(attributes = {})
|
238
|
+
attributes.each {|attr, value| send("#{attr}=", value)}
|
239
|
+
super()
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked, :initialize => false)
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_should_not_have_an_initial_state
|
247
|
+
object = @klass.new
|
248
|
+
assert_nil object.state
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_should_still_allow_manual_initialization
|
252
|
+
@klass.class_eval do
|
253
|
+
def initialize(attributes = {})
|
254
|
+
attributes.each {|attr, value| send("#{attr}=", value)}
|
255
|
+
super()
|
256
|
+
initialize_state_machines
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
object = @klass.new
|
261
|
+
assert_equal 'parked', object.state
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
222
265
|
class MachineWithStaticInitialStateTest < Test::Unit::TestCase
|
223
266
|
def setup
|
224
267
|
@klass = Class.new do
|
@@ -337,6 +380,42 @@ class MachineWithDynamicInitialStateTest < Test::Unit::TestCase
|
|
337
380
|
end
|
338
381
|
end
|
339
382
|
|
383
|
+
class MachineStateInitializationTest < Test::Unit::TestCase
|
384
|
+
def setup
|
385
|
+
@klass = Class.new
|
386
|
+
@machine = StateMachine::Machine.new(@klass, :state, :initial => :parked)
|
387
|
+
|
388
|
+
# Prevent the auto-initialization hook from firing
|
389
|
+
@klass.class_eval do
|
390
|
+
def initialize
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
@object = @klass.new
|
395
|
+
@object.state = nil
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_should_set_states_if_nil
|
399
|
+
@machine.initialize_state(@object)
|
400
|
+
|
401
|
+
assert_equal 'parked', @object.state
|
402
|
+
end
|
403
|
+
|
404
|
+
def test_should_set_states_if_empty
|
405
|
+
@object.state = ''
|
406
|
+
@machine.initialize_state(@object)
|
407
|
+
|
408
|
+
assert_equal 'parked', @object.state
|
409
|
+
end
|
410
|
+
|
411
|
+
def test_should_not_set_states_if_not_empty
|
412
|
+
@object.state = 'idling'
|
413
|
+
@machine.initialize_state(@object)
|
414
|
+
|
415
|
+
assert_equal 'idling', @object.state
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
340
419
|
class MachineWithCustomActionTest < Test::Unit::TestCase
|
341
420
|
def setup
|
342
421
|
@machine = StateMachine::Machine.new(Class.new, :action => :save)
|
@@ -350,7 +429,8 @@ end
|
|
350
429
|
class MachineWithNilActionTest < Test::Unit::TestCase
|
351
430
|
def setup
|
352
431
|
integration = Module.new do
|
353
|
-
|
432
|
+
include StateMachine::Integrations::Base
|
433
|
+
|
354
434
|
@defaults = {:action => :save}
|
355
435
|
end
|
356
436
|
StateMachine::Integrations.const_set('Custom', integration)
|
@@ -394,6 +474,8 @@ end
|
|
394
474
|
class MachineWithCustomIntegrationTest < Test::Unit::TestCase
|
395
475
|
def setup
|
396
476
|
integration = Module.new do
|
477
|
+
include StateMachine::Integrations::Base
|
478
|
+
|
397
479
|
def self.matches?(klass)
|
398
480
|
true
|
399
481
|
end
|
@@ -430,7 +512,8 @@ end
|
|
430
512
|
class MachineWithIntegrationTest < Test::Unit::TestCase
|
431
513
|
def setup
|
432
514
|
StateMachine::Integrations.const_set('Custom', Module.new do
|
433
|
-
|
515
|
+
include StateMachine::Integrations::Base
|
516
|
+
|
434
517
|
@defaults = {:action => :save, :use_transactions => false}
|
435
518
|
|
436
519
|
attr_reader :initialized, :with_scopes, :without_scopes, :ran_transaction
|
@@ -520,8 +603,8 @@ class MachineWithActionUndefinedTest < Test::Unit::TestCase
|
|
520
603
|
assert !@object.respond_to?(:save)
|
521
604
|
end
|
522
605
|
|
523
|
-
def
|
524
|
-
assert !@machine.
|
606
|
+
def test_should_not_mark_action_hook_as_defined
|
607
|
+
assert !@machine.action_hook?
|
525
608
|
end
|
526
609
|
end
|
527
610
|
|
@@ -556,8 +639,8 @@ class MachineWithActionDefinedInClassTest < Test::Unit::TestCase
|
|
556
639
|
assert !@klass.ancestors.any? {|ancestor| ancestor != @klass && ancestor.method_defined?(:save)}
|
557
640
|
end
|
558
641
|
|
559
|
-
def
|
560
|
-
assert !@machine.
|
642
|
+
def test_should_not_mark_action_hook_as_defined
|
643
|
+
assert !@machine.action_hook?
|
561
644
|
end
|
562
645
|
end
|
563
646
|
|
@@ -600,8 +683,8 @@ class MachineWithActionDefinedInIncludedModuleTest < Test::Unit::TestCase
|
|
600
683
|
assert @klass.public_method_defined?(:save)
|
601
684
|
end
|
602
685
|
|
603
|
-
def
|
604
|
-
assert @machine.
|
686
|
+
def test_should_mark_action_hook_as_defined
|
687
|
+
assert @machine.action_hook?
|
605
688
|
end
|
606
689
|
end
|
607
690
|
|
@@ -641,8 +724,8 @@ class MachineWithActionDefinedInSuperclassTest < Test::Unit::TestCase
|
|
641
724
|
assert @klass.public_method_defined?(:save)
|
642
725
|
end
|
643
726
|
|
644
|
-
def
|
645
|
-
assert @machine.
|
727
|
+
def test_should_mark_action_hook_as_defined
|
728
|
+
assert @machine.action_hook?
|
646
729
|
end
|
647
730
|
end
|
648
731
|
|
@@ -683,8 +766,8 @@ class MachineWithPrivateActionTest < Test::Unit::TestCase
|
|
683
766
|
assert @klass.private_method_defined?(:save)
|
684
767
|
end
|
685
768
|
|
686
|
-
def
|
687
|
-
assert @machine.
|
769
|
+
def test_should_mark_action_hook_as_defined
|
770
|
+
assert @machine.action_hook?
|
688
771
|
end
|
689
772
|
end
|
690
773
|
|
@@ -705,14 +788,16 @@ class MachineWithActionAlreadyOverriddenTest < Test::Unit::TestCase
|
|
705
788
|
assert_equal 1, @klass.ancestors.select {|ancestor| ![@klass, @superclass].include?(ancestor) && ancestor.method_defined?(:save)}.length
|
706
789
|
end
|
707
790
|
|
708
|
-
def
|
709
|
-
assert @machine.
|
791
|
+
def test_should_mark_action_hook_as_defined
|
792
|
+
assert @machine.action_hook?
|
710
793
|
end
|
711
794
|
end
|
712
795
|
|
713
796
|
class MachineWithCustomPluralTest < Test::Unit::TestCase
|
714
797
|
def setup
|
715
798
|
@integration = Module.new do
|
799
|
+
include StateMachine::Integrations::Base
|
800
|
+
|
716
801
|
class << self; attr_accessor :with_scopes, :without_scopes; end
|
717
802
|
@with_scopes = []
|
718
803
|
@without_scopes = []
|
@@ -748,6 +833,8 @@ end
|
|
748
833
|
class MachineWithCustomInvalidationTest < Test::Unit::TestCase
|
749
834
|
def setup
|
750
835
|
@integration = Module.new do
|
836
|
+
include StateMachine::Integrations::Base
|
837
|
+
|
751
838
|
def invalidate(object, attribute, message, values = [])
|
752
839
|
object.error = generate_message(message, values)
|
753
840
|
end
|
@@ -815,6 +902,7 @@ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
|
|
815
902
|
@machine.before_transition(lambda {})
|
816
903
|
@machine.after_transition(lambda {})
|
817
904
|
@machine.around_transition(lambda {})
|
905
|
+
@machine.after_failure(lambda {})
|
818
906
|
|
819
907
|
@copied_machine = @machine.clone
|
820
908
|
end
|
@@ -862,6 +950,10 @@ class MachineAfterBeingCopiedTest < Test::Unit::TestCase
|
|
862
950
|
def test_should_not_have_the_same_after_callbacks
|
863
951
|
assert_not_same @copied_machine.callbacks[:after], @machine.callbacks[:after]
|
864
952
|
end
|
953
|
+
|
954
|
+
def test_should_not_have_the_same_failure_callbacks
|
955
|
+
assert_not_same @copied_machine.callbacks[:failure], @machine.callbacks[:failure]
|
956
|
+
end
|
865
957
|
end
|
866
958
|
|
867
959
|
class MachineAfterChangingOwnerClassTest < Test::Unit::TestCase
|
@@ -919,6 +1011,23 @@ class MachineAfterChangingInitialState < Test::Unit::TestCase
|
|
919
1011
|
end
|
920
1012
|
end
|
921
1013
|
|
1014
|
+
class MachineWithHelpersTest < Test::Unit::TestCase
|
1015
|
+
def setup
|
1016
|
+
@klass = Class.new
|
1017
|
+
@machine = StateMachine::Machine.new(@klass)
|
1018
|
+
@object = @klass.new
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def test_should_throw_exception_with_invalid_scope
|
1022
|
+
assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @machine.define_helper(:invalid, :state) {} }
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def test_should_throw_exception_if_calling_helper_directly_with_invalid_scope
|
1026
|
+
@machine.define_helper(:instance, :state) {}
|
1027
|
+
assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @machine.call_helper(:invalid, :state, lambda {}, @object) }
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
|
922
1031
|
class MachineWithInstanceHelpersTest < Test::Unit::TestCase
|
923
1032
|
def setup
|
924
1033
|
@klass = Class.new
|
@@ -933,7 +1042,7 @@ class MachineWithInstanceHelpersTest < Test::Unit::TestCase
|
|
933
1042
|
end
|
934
1043
|
end
|
935
1044
|
|
936
|
-
@machine.
|
1045
|
+
@machine.define_helper(:instance, :state) {}
|
937
1046
|
assert_equal 'parked', @object.state
|
938
1047
|
end
|
939
1048
|
|
@@ -945,7 +1054,7 @@ class MachineWithInstanceHelpersTest < Test::Unit::TestCase
|
|
945
1054
|
end
|
946
1055
|
end
|
947
1056
|
|
948
|
-
@machine.
|
1057
|
+
@machine.define_helper(:instance, :state) {}
|
949
1058
|
assert_equal 'parked', @object.send(:state)
|
950
1059
|
end
|
951
1060
|
|
@@ -957,14 +1066,75 @@ class MachineWithInstanceHelpersTest < Test::Unit::TestCase
|
|
957
1066
|
end
|
958
1067
|
end
|
959
1068
|
|
960
|
-
@machine.
|
1069
|
+
@machine.define_helper(:instance, :state) {}
|
961
1070
|
assert_equal 'parked', @object.send(:state)
|
962
1071
|
end
|
963
1072
|
|
964
1073
|
def test_should_define_nonexistent_methods
|
965
|
-
@machine.
|
1074
|
+
@machine.define_helper(:instance, :state) {'parked'}
|
966
1075
|
assert_equal 'parked', @object.state
|
967
1076
|
end
|
1077
|
+
|
1078
|
+
def test_should_pass_context_as_arguments
|
1079
|
+
helper_args = nil
|
1080
|
+
@machine.define_helper(:instance, :state) {|*args| helper_args = args}
|
1081
|
+
@object.state
|
1082
|
+
assert_equal 3, helper_args.length
|
1083
|
+
assert_equal [@machine, @object], helper_args[0..1]
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
def test_should_pass_method_arguments_through
|
1087
|
+
helper_args = nil
|
1088
|
+
@machine.define_helper(:instance, :state) {|*args| helper_args = args}
|
1089
|
+
@object.state(1, 2, 3)
|
1090
|
+
assert_equal 6, helper_args.length
|
1091
|
+
assert_equal [@machine, @object], helper_args[0..1]
|
1092
|
+
assert_equal [1, 2, 3], helper_args[3..5]
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
def test_should_allow_super_calls
|
1096
|
+
@klass = Class.new
|
1097
|
+
@klass.class_eval do
|
1098
|
+
include(Module.new {
|
1099
|
+
def state
|
1100
|
+
'original'
|
1101
|
+
end
|
1102
|
+
})
|
1103
|
+
end
|
1104
|
+
@machine = StateMachine::Machine.new(@klass)
|
1105
|
+
@object = @klass.new
|
1106
|
+
|
1107
|
+
@machine.define_helper(:instance, :state) {|machine, object, _super| _super.call}
|
1108
|
+
assert_equal 'original', @object.state
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
def test_should_allow_rewrite_of_super_args
|
1112
|
+
@klass = Class.new
|
1113
|
+
@klass.class_eval do
|
1114
|
+
include(Module.new {
|
1115
|
+
def state(value)
|
1116
|
+
value
|
1117
|
+
end
|
1118
|
+
})
|
1119
|
+
end
|
1120
|
+
@machine = StateMachine::Machine.new(@klass)
|
1121
|
+
@object = @klass.new
|
1122
|
+
|
1123
|
+
@machine.define_helper(:instance, :state) {|machine, object, _super, *args| _super.call('override')}
|
1124
|
+
assert_equal 'override', @object.state(1)
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
def test_should_throw_exception_if_calling_helper_directly_with_invalid_method
|
1128
|
+
assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @machine.call_helper(:instance, :invalid, @object, lambda {}) }
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
def test_should_be_able_to_call_helper_directly
|
1132
|
+
helper_args = nil
|
1133
|
+
@machine.define_helper(:instance, :state) {|*args| helper_args = args}
|
1134
|
+
|
1135
|
+
@machine.call_helper(:instance, :state, @object, _super = lambda {}, 1, 2, 3)
|
1136
|
+
assert_equal [@machine, @object, _super, 1, 2, 3], helper_args
|
1137
|
+
end
|
968
1138
|
end
|
969
1139
|
|
970
1140
|
class MachineWithClassHelpersTest < Test::Unit::TestCase
|
@@ -980,7 +1150,7 @@ class MachineWithClassHelpersTest < Test::Unit::TestCase
|
|
980
1150
|
end
|
981
1151
|
end
|
982
1152
|
|
983
|
-
@machine.
|
1153
|
+
@machine.define_helper(:class, :states) {}
|
984
1154
|
assert_equal [], @klass.states
|
985
1155
|
end
|
986
1156
|
|
@@ -992,7 +1162,7 @@ class MachineWithClassHelpersTest < Test::Unit::TestCase
|
|
992
1162
|
end
|
993
1163
|
end
|
994
1164
|
|
995
|
-
@machine.
|
1165
|
+
@machine.define_helper(:class, :states) {}
|
996
1166
|
assert_equal [], @klass.send(:states)
|
997
1167
|
end
|
998
1168
|
|
@@ -1004,14 +1174,73 @@ class MachineWithClassHelpersTest < Test::Unit::TestCase
|
|
1004
1174
|
end
|
1005
1175
|
end
|
1006
1176
|
|
1007
|
-
@machine.
|
1177
|
+
@machine.define_helper(:class, :states) {}
|
1008
1178
|
assert_equal [], @klass.send(:states)
|
1009
1179
|
end
|
1010
1180
|
|
1011
1181
|
def test_should_define_nonexistent_methods
|
1012
|
-
@machine.
|
1182
|
+
@machine.define_helper(:class, :states) {[]}
|
1013
1183
|
assert_equal [], @klass.states
|
1014
1184
|
end
|
1185
|
+
|
1186
|
+
def test_should_pass_context_as_arguments
|
1187
|
+
helper_args = nil
|
1188
|
+
@machine.define_helper(:class, :states) {|*args| helper_args = args}
|
1189
|
+
@klass.states
|
1190
|
+
assert_equal 3, helper_args.length
|
1191
|
+
assert_equal [@machine, @klass], helper_args[0..1]
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
def test_should_pass_method_arguments_through
|
1195
|
+
helper_args = nil
|
1196
|
+
@machine.define_helper(:class, :states) {|*args| helper_args = args}
|
1197
|
+
@klass.states(1, 2, 3)
|
1198
|
+
assert_equal 6, helper_args.length
|
1199
|
+
assert_equal [@machine, @klass], helper_args[0..1]
|
1200
|
+
assert_equal [1, 2, 3], helper_args[3..5]
|
1201
|
+
end
|
1202
|
+
|
1203
|
+
def test_should_allow_super_calls
|
1204
|
+
@klass = Class.new
|
1205
|
+
@klass.class_eval do
|
1206
|
+
extend(Module.new {
|
1207
|
+
def states
|
1208
|
+
'original'
|
1209
|
+
end
|
1210
|
+
})
|
1211
|
+
end
|
1212
|
+
@machine = StateMachine::Machine.new(@klass)
|
1213
|
+
|
1214
|
+
@machine.define_helper(:class, :states) {|machine, klass, _super| _super.call}
|
1215
|
+
assert_equal 'original', @klass.states
|
1216
|
+
end
|
1217
|
+
|
1218
|
+
def test_should_allow_rewrite_of_super_args
|
1219
|
+
@klass = Class.new
|
1220
|
+
@klass.class_eval do
|
1221
|
+
extend(Module.new {
|
1222
|
+
def states(value)
|
1223
|
+
value
|
1224
|
+
end
|
1225
|
+
})
|
1226
|
+
end
|
1227
|
+
@machine = StateMachine::Machine.new(@klass)
|
1228
|
+
|
1229
|
+
@machine.define_helper(:class, :states) {|machine, klass, _super, *args| _super.call('override')}
|
1230
|
+
assert_equal 'override', @klass.states(1)
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
def test_should_throw_exception_if_calling_helper_directly_with_invalid_method
|
1234
|
+
assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @machine.call_helper(:class, :invalid, @klass, lambda {}) }
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
def test_should_be_able_to_call_helper_directly
|
1238
|
+
helper_args = nil
|
1239
|
+
@machine.define_helper(:class, :states) {|*args| helper_args = args}
|
1240
|
+
|
1241
|
+
@machine.call_helper(:class, :states, @klass, _super = lambda {}, 1, 2, 3)
|
1242
|
+
assert_equal [@machine, @klass, _super, 1, 2, 3], helper_args
|
1243
|
+
end
|
1015
1244
|
end
|
1016
1245
|
|
1017
1246
|
class MachineWithConflictingHelpersTest < Test::Unit::TestCase
|
@@ -1070,9 +1299,15 @@ class MachineWithConflictingHelpersTest < Test::Unit::TestCase
|
|
1070
1299
|
def state_transitions
|
1071
1300
|
[{:parked => :idling}]
|
1072
1301
|
end
|
1302
|
+
|
1303
|
+
def state_paths
|
1304
|
+
[[{:parked => :idling}]]
|
1305
|
+
end
|
1073
1306
|
end
|
1074
1307
|
|
1075
1308
|
StateMachine::Integrations.const_set('Custom', Module.new do
|
1309
|
+
include StateMachine::Integrations::Base
|
1310
|
+
|
1076
1311
|
def create_with_scope(name)
|
1077
1312
|
lambda {|klass, values| []}
|
1078
1313
|
end
|
@@ -1141,6 +1376,10 @@ class MachineWithConflictingHelpersTest < Test::Unit::TestCase
|
|
1141
1376
|
assert_equal [{:parked => :idling}], @object.state_transitions
|
1142
1377
|
end
|
1143
1378
|
|
1379
|
+
def test_should_not_redefine_attribute_paths_reader
|
1380
|
+
assert_equal [[{:parked => :idling}]], @object.state_paths
|
1381
|
+
end
|
1382
|
+
|
1144
1383
|
def test_should_allow_super_chaining
|
1145
1384
|
@klass.class_eval do
|
1146
1385
|
def self.with_state(*states)
|
@@ -1197,6 +1436,10 @@ class MachineWithConflictingHelpersTest < Test::Unit::TestCase
|
|
1197
1436
|
def state_transitions
|
1198
1437
|
super == []
|
1199
1438
|
end
|
1439
|
+
|
1440
|
+
def state_paths
|
1441
|
+
super == []
|
1442
|
+
end
|
1200
1443
|
end
|
1201
1444
|
|
1202
1445
|
assert_equal true, @klass.with_state
|
@@ -1214,6 +1457,7 @@ class MachineWithConflictingHelpersTest < Test::Unit::TestCase
|
|
1214
1457
|
assert_equal 0, @object.human_state_name
|
1215
1458
|
assert_equal true, @object.state_events
|
1216
1459
|
assert_equal true, @object.state_transitions
|
1460
|
+
assert_equal true, @object.state_paths
|
1217
1461
|
end
|
1218
1462
|
|
1219
1463
|
def teardown
|
@@ -1348,6 +1592,16 @@ class MachinePersistenceTest < Test::Unit::TestCase
|
|
1348
1592
|
@machine.write(@object, :event, 'ignite')
|
1349
1593
|
assert_equal 'ignite', @object.state_event
|
1350
1594
|
end
|
1595
|
+
|
1596
|
+
def test_should_allow_writing_custom_instance_variables
|
1597
|
+
@klass.class_eval do
|
1598
|
+
attr_reader :state_value
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
assert_raise(NoMethodError) { @machine.write(@object, :value, 1) }
|
1602
|
+
assert_equal 1, @machine.write(@object, :value, 1, true)
|
1603
|
+
assert_equal 1, @object.state_value
|
1604
|
+
end
|
1351
1605
|
end
|
1352
1606
|
|
1353
1607
|
|
@@ -1675,7 +1929,7 @@ class MachineWithMultipleEventsTest < Test::Unit::TestCase
|
|
1675
1929
|
end
|
1676
1930
|
|
1677
1931
|
def test_should_define_transitions_for_each_event
|
1678
|
-
[@park, @shift_down].each {|event| assert_equal 1, event.
|
1932
|
+
[@park, @shift_down].each {|event| assert_equal 1, event.branches.size}
|
1679
1933
|
end
|
1680
1934
|
|
1681
1935
|
def test_should_transition_the_same_for_each_event
|
@@ -1834,6 +2088,76 @@ class MachineWithTransitionCallbacksTest < Test::Unit::TestCase
|
|
1834
2088
|
end
|
1835
2089
|
end
|
1836
2090
|
|
2091
|
+
class MachineWithFailureCallbacksTest < Test::Unit::TestCase
|
2092
|
+
def setup
|
2093
|
+
@klass = Class.new do
|
2094
|
+
attr_accessor :callbacks
|
2095
|
+
end
|
2096
|
+
|
2097
|
+
@machine = StateMachine::Machine.new(@klass, :initial => :parked)
|
2098
|
+
@event = @machine.event :ignite
|
2099
|
+
|
2100
|
+
@object = @klass.new
|
2101
|
+
@object.callbacks = []
|
2102
|
+
end
|
2103
|
+
|
2104
|
+
def test_should_raise_exception_if_implicit_option_specified
|
2105
|
+
exception = assert_raise(ArgumentError) {@machine.after_failure :invalid => :valid, :do => lambda {}}
|
2106
|
+
assert_equal 'Invalid key(s): invalid', exception.message
|
2107
|
+
end
|
2108
|
+
|
2109
|
+
def test_should_raise_exception_if_method_not_specified
|
2110
|
+
exception = assert_raise(ArgumentError) {@machine.after_failure :on => :ignite}
|
2111
|
+
assert_equal 'Method(s) for callback must be specified', exception.message
|
2112
|
+
end
|
2113
|
+
|
2114
|
+
def test_should_invoke_callbacks_during_failed_transition
|
2115
|
+
@machine.after_failure lambda {|object| object.callbacks << 'failure'}
|
2116
|
+
|
2117
|
+
@event.fire(@object)
|
2118
|
+
assert_equal %w(failure), @object.callbacks
|
2119
|
+
end
|
2120
|
+
|
2121
|
+
def test_should_allow_multiple_callbacks
|
2122
|
+
@machine.after_failure lambda {|object| object.callbacks << 'failure1'}, lambda {|object| object.callbacks << 'failure2'}
|
2123
|
+
|
2124
|
+
@event.fire(@object)
|
2125
|
+
assert_equal %w(failure1 failure2), @object.callbacks
|
2126
|
+
end
|
2127
|
+
|
2128
|
+
def test_should_allow_multiple_callbacks_with_requirements
|
2129
|
+
@machine.after_failure lambda {|object| object.callbacks << 'failure_ignite1'}, lambda {|object| object.callbacks << 'failure_ignite2'}, :on => :ignite
|
2130
|
+
@machine.after_failure lambda {|object| object.callbacks << 'failure_park1'}, lambda {|object| object.callbacks << 'failure_park2'}, :on => :park
|
2131
|
+
|
2132
|
+
@event.fire(@object)
|
2133
|
+
assert_equal %w(failure_ignite1 failure_ignite2), @object.callbacks
|
2134
|
+
end
|
2135
|
+
end
|
2136
|
+
|
2137
|
+
class MachineWithPathsTest < Test::Unit::TestCase
|
2138
|
+
def setup
|
2139
|
+
@klass = Class.new
|
2140
|
+
@machine = StateMachine::Machine.new(@klass)
|
2141
|
+
@machine.event :ignite do
|
2142
|
+
transition :parked => :idling
|
2143
|
+
end
|
2144
|
+
@machine.event :shift_up do
|
2145
|
+
transition :first_gear => :second_gear
|
2146
|
+
end
|
2147
|
+
|
2148
|
+
@object = @klass.new
|
2149
|
+
@object.state = 'parked'
|
2150
|
+
end
|
2151
|
+
|
2152
|
+
def test_should_have_paths
|
2153
|
+
assert_equal [[StateMachine::Transition.new(@object, @machine, :ignite, :parked, :idling)]], @machine.paths_for(@object)
|
2154
|
+
end
|
2155
|
+
|
2156
|
+
def test_should_allow_requirement_configuration
|
2157
|
+
assert_equal [[StateMachine::Transition.new(@object, @machine, :shift_up, :first_gear, :second_gear)]], @machine.paths_for(@object, :from => :first_gear)
|
2158
|
+
end
|
2159
|
+
end
|
2160
|
+
|
1837
2161
|
class MachineWithOwnerSubclassTest < Test::Unit::TestCase
|
1838
2162
|
def setup
|
1839
2163
|
@klass = Class.new
|
@@ -1850,6 +2174,37 @@ class MachineWithOwnerSubclassTest < Test::Unit::TestCase
|
|
1850
2174
|
end
|
1851
2175
|
end
|
1852
2176
|
|
2177
|
+
class MachineWithOwnerSubclassHelpersTest < Test::Unit::TestCase
|
2178
|
+
def setup
|
2179
|
+
@base = Class.new
|
2180
|
+
@base_machine = StateMachine::Machine.new(@base)
|
2181
|
+
@base_machine.define_helper(:instance, :transition) { :base }
|
2182
|
+
|
2183
|
+
@subclass = Class.new(@base)
|
2184
|
+
@subclass_machine = @subclass.state_machine {}
|
2185
|
+
@subclass_machine.define_helper(:instance, :run) { :subclass }
|
2186
|
+
|
2187
|
+
@base_object = @base.new
|
2188
|
+
@subclass_object = @subclass.new
|
2189
|
+
end
|
2190
|
+
|
2191
|
+
def test_should_be_able_to_call_base_helper_on_base
|
2192
|
+
assert_equal :base, @base_machine.call_helper(:instance, :transition, @base_object, lambda {})
|
2193
|
+
end
|
2194
|
+
|
2195
|
+
def test_should_be_able_to_call_base_helper_on_subclass
|
2196
|
+
assert_equal :base, @subclass_machine.call_helper(:instance, :transition, @subclass_object, lambda {})
|
2197
|
+
end
|
2198
|
+
|
2199
|
+
def test_should_not_be_able_to_call_subclass_helper_on_base
|
2200
|
+
assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) { @base_machine.call_helper(:instance, :run, @base_object, lambda {}) }
|
2201
|
+
end
|
2202
|
+
|
2203
|
+
def test_should_be_able_to_call_subclass_helper_on_base
|
2204
|
+
assert_equal :subclass, @subclass_machine.call_helper(:instance, :run, @subclass_object, lambda {})
|
2205
|
+
end
|
2206
|
+
end
|
2207
|
+
|
1853
2208
|
class MachineWithExistingMachinesOnOwnerClassTest < Test::Unit::TestCase
|
1854
2209
|
def setup
|
1855
2210
|
@klass = Class.new
|
@@ -1952,7 +2307,8 @@ end
|
|
1952
2307
|
class MachineWithCustomAttributeTest < Test::Unit::TestCase
|
1953
2308
|
def setup
|
1954
2309
|
StateMachine::Integrations.const_set('Custom', Module.new do
|
1955
|
-
|
2310
|
+
include StateMachine::Integrations::Base
|
2311
|
+
|
1956
2312
|
@defaults = {:action => :save, :use_transactions => false}
|
1957
2313
|
|
1958
2314
|
def create_with_scope(name)
|
@@ -2001,6 +2357,10 @@ class MachineWithCustomAttributeTest < Test::Unit::TestCase
|
|
2001
2357
|
assert @object.respond_to?(:state_transitions)
|
2002
2358
|
end
|
2003
2359
|
|
2360
|
+
def test_should_define_a_path_reader_for_the_attribute
|
2361
|
+
assert @object.respond_to?(:state_paths)
|
2362
|
+
end
|
2363
|
+
|
2004
2364
|
def test_should_define_a_human_attribute_name_reader
|
2005
2365
|
assert @klass.respond_to?(:human_state_name)
|
2006
2366
|
end
|
@@ -2083,6 +2443,8 @@ end
|
|
2083
2443
|
class MachineFinderWithExistingMachineOnSuperclassTest < Test::Unit::TestCase
|
2084
2444
|
def setup
|
2085
2445
|
integration = Module.new do
|
2446
|
+
include StateMachine::Integrations::Base
|
2447
|
+
|
2086
2448
|
def self.matches?(klass)
|
2087
2449
|
false
|
2088
2450
|
end
|
@@ -2331,14 +2693,14 @@ begin
|
|
2331
2693
|
end
|
2332
2694
|
|
2333
2695
|
def test_should_load_files
|
2334
|
-
StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../files/switch.rb")
|
2696
|
+
StateMachine::Machine.draw('Switch', :file => File.expand_path("#{File.dirname(__FILE__)}/../files/switch.rb"))
|
2335
2697
|
assert defined?(::Switch)
|
2336
2698
|
ensure
|
2337
2699
|
FileUtils.rm('./Switch_state.png')
|
2338
2700
|
end
|
2339
2701
|
|
2340
2702
|
def test_should_allow_path_and_format_to_be_customized
|
2341
|
-
StateMachine::Machine.draw('Switch', :file => "#{File.dirname(__FILE__)}/../files/switch.rb", :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
|
2703
|
+
StateMachine::Machine.draw('Switch', :file => File.expand_path("#{File.dirname(__FILE__)}/../files/switch.rb"), :path => "#{File.dirname(__FILE__)}/", :format => 'jpg')
|
2342
2704
|
assert File.exist?("#{File.dirname(__FILE__)}/Switch_state.jpg")
|
2343
2705
|
ensure
|
2344
2706
|
FileUtils.rm("#{File.dirname(__FILE__)}/Switch_state.jpg")
|