state_machine 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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")
|