telvue_state_machine 1.2.1
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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +72 -0
- data/.yardopts +5 -0
- data/Appraisals +491 -0
- data/CHANGELOG.md +502 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +1263 -0
- data/Rakefile +41 -0
- data/examples/AutoShop_state.png +0 -0
- data/examples/Car_state.png +0 -0
- data/examples/Gemfile +5 -0
- data/examples/Gemfile.lock +14 -0
- data/examples/TrafficLight_state.png +0 -0
- data/examples/Vehicle_state.png +0 -0
- data/examples/auto_shop.rb +13 -0
- data/examples/car.rb +21 -0
- data/examples/doc/AutoShop.html +2856 -0
- data/examples/doc/AutoShop_state.png +0 -0
- data/examples/doc/Car.html +919 -0
- data/examples/doc/Car_state.png +0 -0
- data/examples/doc/TrafficLight.html +2230 -0
- data/examples/doc/TrafficLight_state.png +0 -0
- data/examples/doc/Vehicle.html +7921 -0
- data/examples/doc/Vehicle_state.png +0 -0
- data/examples/doc/_index.html +136 -0
- data/examples/doc/class_list.html +47 -0
- data/examples/doc/css/common.css +1 -0
- data/examples/doc/css/full_list.css +55 -0
- data/examples/doc/css/style.css +322 -0
- data/examples/doc/file_list.html +46 -0
- data/examples/doc/frames.html +13 -0
- data/examples/doc/index.html +136 -0
- data/examples/doc/js/app.js +205 -0
- data/examples/doc/js/full_list.js +173 -0
- data/examples/doc/js/jquery.js +16 -0
- data/examples/doc/method_list.html +734 -0
- data/examples/doc/top-level-namespace.html +105 -0
- data/examples/merb-rest/controller.rb +51 -0
- data/examples/merb-rest/model.rb +28 -0
- data/examples/merb-rest/view_edit.html.erb +24 -0
- data/examples/merb-rest/view_index.html.erb +23 -0
- data/examples/merb-rest/view_new.html.erb +13 -0
- data/examples/merb-rest/view_show.html.erb +17 -0
- data/examples/rails-rest/controller.rb +43 -0
- data/examples/rails-rest/migration.rb +7 -0
- data/examples/rails-rest/model.rb +23 -0
- data/examples/rails-rest/view__form.html.erb +34 -0
- data/examples/rails-rest/view_edit.html.erb +6 -0
- data/examples/rails-rest/view_index.html.erb +25 -0
- data/examples/rails-rest/view_new.html.erb +5 -0
- data/examples/rails-rest/view_show.html.erb +19 -0
- data/examples/traffic_light.rb +9 -0
- data/examples/vehicle.rb +33 -0
- data/gemfiles/active_model_3.0.0.gemfile +7 -0
- data/gemfiles/active_model_3.0.0.gemfile.lock +35 -0
- data/gemfiles/active_model_3.0.5.gemfile +7 -0
- data/gemfiles/active_model_3.0.5.gemfile.lock +35 -0
- data/gemfiles/active_model_3.1.1.gemfile +7 -0
- data/gemfiles/active_model_3.1.1.gemfile.lock +36 -0
- data/gemfiles/active_model_3.2.1.gemfile +7 -0
- data/gemfiles/active_model_3.2.12.gemfile +7 -0
- data/gemfiles/active_model_3.2.12.gemfile.lock +36 -0
- data/gemfiles/active_model_3.2.13.rc1.gemfile +7 -0
- data/gemfiles/active_model_3.2.13.rc1.gemfile.lock +36 -0
- data/gemfiles/active_model_4.0.0.gemfile +9 -0
- data/gemfiles/active_model_4.0.0.gemfile.lock +78 -0
- data/gemfiles/active_record_2.0.0.gemfile +9 -0
- data/gemfiles/active_record_2.0.0.gemfile.lock +39 -0
- data/gemfiles/active_record_2.0.5.gemfile +9 -0
- data/gemfiles/active_record_2.0.5.gemfile.lock +39 -0
- data/gemfiles/active_record_2.1.0.gemfile +9 -0
- data/gemfiles/active_record_2.1.0.gemfile.lock +39 -0
- data/gemfiles/active_record_2.1.2.gemfile +9 -0
- data/gemfiles/active_record_2.1.2.gemfile.lock +39 -0
- data/gemfiles/active_record_2.2.3.gemfile +9 -0
- data/gemfiles/active_record_2.2.3.gemfile.lock +39 -0
- data/gemfiles/active_record_2.3.12.gemfile +9 -0
- data/gemfiles/active_record_2.3.12.gemfile.lock +39 -0
- data/gemfiles/active_record_2.3.5.gemfile +9 -0
- data/gemfiles/active_record_2.3.5.gemfile.lock +39 -0
- data/gemfiles/active_record_3.0.0.gemfile +9 -0
- data/gemfiles/active_record_3.0.0.gemfile.lock +51 -0
- data/gemfiles/active_record_3.0.5.gemfile +9 -0
- data/gemfiles/active_record_3.0.5.gemfile.lock +50 -0
- data/gemfiles/active_record_3.1.1.gemfile +9 -0
- data/gemfiles/active_record_3.1.1.gemfile.lock +51 -0
- data/gemfiles/active_record_3.2.12.gemfile +9 -0
- data/gemfiles/active_record_3.2.12.gemfile.lock +51 -0
- data/gemfiles/active_record_3.2.13.rc1.gemfile +9 -0
- data/gemfiles/active_record_3.2.13.rc1.gemfile.lock +51 -0
- data/gemfiles/active_record_4.0.0.gemfile +11 -0
- data/gemfiles/active_record_4.0.0.gemfile.lock +83 -0
- data/gemfiles/data_mapper_0.10.2.gemfile +13 -0
- data/gemfiles/data_mapper_0.10.2.gemfile.lock +56 -0
- data/gemfiles/data_mapper_0.9.11.gemfile +13 -0
- data/gemfiles/data_mapper_0.9.11.gemfile.lock +71 -0
- data/gemfiles/data_mapper_0.9.4.gemfile +12 -0
- data/gemfiles/data_mapper_0.9.4.gemfile.lock +70 -0
- data/gemfiles/data_mapper_0.9.7.gemfile +13 -0
- data/gemfiles/data_mapper_0.9.7.gemfile.lock +67 -0
- data/gemfiles/data_mapper_1.0.0.gemfile +12 -0
- data/gemfiles/data_mapper_1.0.0.gemfile.lock +63 -0
- data/gemfiles/data_mapper_1.0.1.gemfile +12 -0
- data/gemfiles/data_mapper_1.0.1.gemfile.lock +63 -0
- data/gemfiles/data_mapper_1.0.2.gemfile +12 -0
- data/gemfiles/data_mapper_1.0.2.gemfile.lock +63 -0
- data/gemfiles/data_mapper_1.1.0.gemfile +12 -0
- data/gemfiles/data_mapper_1.1.0.gemfile.lock +61 -0
- data/gemfiles/data_mapper_1.2.0.gemfile +12 -0
- data/gemfiles/data_mapper_1.2.0.gemfile.lock +61 -0
- data/gemfiles/default.gemfile +7 -0
- data/gemfiles/default.gemfile.lock +27 -0
- data/gemfiles/graphviz_0.9.17.gemfile +7 -0
- data/gemfiles/graphviz_0.9.17.gemfile.lock +29 -0
- data/gemfiles/graphviz_0.9.21.gemfile +7 -0
- data/gemfiles/graphviz_0.9.21.gemfile.lock +29 -0
- data/gemfiles/graphviz_1.0.0.gemfile +7 -0
- data/gemfiles/graphviz_1.0.0.gemfile.lock +29 -0
- data/gemfiles/graphviz_1.0.3.gemfile +7 -0
- data/gemfiles/graphviz_1.0.3.gemfile.lock +29 -0
- data/gemfiles/graphviz_1.0.8.gemfile +7 -0
- data/gemfiles/graphviz_1.0.8.gemfile.lock +29 -0
- data/gemfiles/mongo_mapper_0.10.0.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.10.0.gemfile.lock +47 -0
- data/gemfiles/mongo_mapper_0.11.2.gemfile +9 -0
- data/gemfiles/mongo_mapper_0.11.2.gemfile.lock +48 -0
- data/gemfiles/mongo_mapper_0.12.0.gemfile +9 -0
- data/gemfiles/mongo_mapper_0.12.0.gemfile.lock +48 -0
- data/gemfiles/mongo_mapper_0.5.5.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.5.5.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.5.8.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.5.8.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.6.0.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.6.0.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.6.10.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.6.10.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.7.0.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.7.0.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.7.5.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.7.5.gemfile.lock +39 -0
- data/gemfiles/mongo_mapper_0.8.0.gemfile +10 -0
- data/gemfiles/mongo_mapper_0.8.0.gemfile.lock +43 -0
- data/gemfiles/mongo_mapper_0.8.3.gemfile +10 -0
- data/gemfiles/mongo_mapper_0.8.3.gemfile.lock +43 -0
- data/gemfiles/mongo_mapper_0.8.4.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.8.4.gemfile.lock +42 -0
- data/gemfiles/mongo_mapper_0.8.6.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.8.6.gemfile.lock +42 -0
- data/gemfiles/mongo_mapper_0.9.0.gemfile +7 -0
- data/gemfiles/mongo_mapper_0.9.0.gemfile.lock +45 -0
- data/gemfiles/mongoid_2.0.0.gemfile +9 -0
- data/gemfiles/mongoid_2.0.0.gemfile.lock +49 -0
- data/gemfiles/mongoid_2.1.4.gemfile +9 -0
- data/gemfiles/mongoid_2.1.4.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.2.4.gemfile +9 -0
- data/gemfiles/mongoid_2.2.4.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.3.3.gemfile +9 -0
- data/gemfiles/mongoid_2.3.3.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.4.0.gemfile +9 -0
- data/gemfiles/mongoid_2.4.0.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.4.10.gemfile +9 -0
- data/gemfiles/mongoid_2.4.10.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.5.2.gemfile +9 -0
- data/gemfiles/mongoid_2.5.2.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.6.0.gemfile +9 -0
- data/gemfiles/mongoid_2.6.0.gemfile.lock +47 -0
- data/gemfiles/mongoid_3.0.0.gemfile +8 -0
- data/gemfiles/mongoid_3.0.0.gemfile.lock +45 -0
- data/gemfiles/mongoid_3.0.22.gemfile +8 -0
- data/gemfiles/mongoid_3.0.22.gemfile.lock +45 -0
- data/gemfiles/mongoid_3.1.0.gemfile +8 -0
- data/gemfiles/mongoid_3.1.0.gemfile.lock +45 -0
- data/gemfiles/sequel_2.11.0.gemfile +9 -0
- data/gemfiles/sequel_2.11.0.gemfile.lock +33 -0
- data/gemfiles/sequel_2.12.0.gemfile +9 -0
- data/gemfiles/sequel_2.12.0.gemfile.lock +33 -0
- data/gemfiles/sequel_2.8.0.gemfile +9 -0
- data/gemfiles/sequel_2.8.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.0.0.gemfile +9 -0
- data/gemfiles/sequel_3.0.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.10.0.gemfile +9 -0
- data/gemfiles/sequel_3.10.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.13.0.gemfile +9 -0
- data/gemfiles/sequel_3.13.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.14.0.gemfile +9 -0
- data/gemfiles/sequel_3.14.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.23.0.gemfile +9 -0
- data/gemfiles/sequel_3.23.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.24.0.gemfile +9 -0
- data/gemfiles/sequel_3.24.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.29.0.gemfile +9 -0
- data/gemfiles/sequel_3.29.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.34.0.gemfile +9 -0
- data/gemfiles/sequel_3.34.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.35.0.gemfile +9 -0
- data/gemfiles/sequel_3.35.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.4.0.gemfile +9 -0
- data/gemfiles/sequel_3.4.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.44.0.gemfile +9 -0
- data/gemfiles/sequel_3.44.0.gemfile.lock +33 -0
- data/init.rb +1 -0
- data/lib/state_machine.rb +8 -0
- data/lib/state_machine/assertions.rb +36 -0
- data/lib/state_machine/branch.rb +225 -0
- data/lib/state_machine/callback.rb +236 -0
- data/lib/state_machine/core.rb +12 -0
- data/lib/state_machine/core_ext.rb +2 -0
- data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
- data/lib/state_machine/error.rb +13 -0
- data/lib/state_machine/eval_helpers.rb +87 -0
- data/lib/state_machine/event.rb +257 -0
- data/lib/state_machine/event_collection.rb +141 -0
- data/lib/state_machine/extensions.rb +149 -0
- data/lib/state_machine/graph.rb +92 -0
- data/lib/state_machine/helper_module.rb +17 -0
- data/lib/state_machine/initializers.rb +4 -0
- data/lib/state_machine/initializers/merb.rb +1 -0
- data/lib/state_machine/initializers/rails.rb +25 -0
- data/lib/state_machine/integrations.rb +121 -0
- data/lib/state_machine/integrations/active_model.rb +585 -0
- data/lib/state_machine/integrations/active_model/locale.rb +11 -0
- data/lib/state_machine/integrations/active_model/observer.rb +33 -0
- data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
- data/lib/state_machine/integrations/active_model/versions.rb +31 -0
- data/lib/state_machine/integrations/active_record.rb +552 -0
- data/lib/state_machine/integrations/active_record/locale.rb +20 -0
- data/lib/state_machine/integrations/active_record/versions.rb +123 -0
- data/lib/state_machine/integrations/base.rb +100 -0
- data/lib/state_machine/integrations/data_mapper.rb +511 -0
- data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
- data/lib/state_machine/integrations/data_mapper/versions.rb +85 -0
- data/lib/state_machine/integrations/mongo_mapper.rb +389 -0
- data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
- data/lib/state_machine/integrations/mongo_mapper/versions.rb +89 -0
- data/lib/state_machine/integrations/mongoid.rb +465 -0
- data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
- data/lib/state_machine/integrations/mongoid/versions.rb +81 -0
- data/lib/state_machine/integrations/sequel.rb +486 -0
- data/lib/state_machine/integrations/sequel/versions.rb +95 -0
- data/lib/state_machine/machine.rb +2292 -0
- data/lib/state_machine/machine_collection.rb +86 -0
- data/lib/state_machine/macro_methods.rb +522 -0
- data/lib/state_machine/matcher.rb +123 -0
- data/lib/state_machine/matcher_helpers.rb +54 -0
- data/lib/state_machine/node_collection.rb +222 -0
- data/lib/state_machine/path.rb +120 -0
- data/lib/state_machine/path_collection.rb +90 -0
- data/lib/state_machine/state.rb +297 -0
- data/lib/state_machine/state_collection.rb +112 -0
- data/lib/state_machine/state_context.rb +138 -0
- data/lib/state_machine/transition.rb +470 -0
- data/lib/state_machine/transition_collection.rb +245 -0
- data/lib/state_machine/version.rb +3 -0
- data/lib/state_machine/yard.rb +8 -0
- data/lib/state_machine/yard/handlers.rb +12 -0
- data/lib/state_machine/yard/handlers/base.rb +32 -0
- data/lib/state_machine/yard/handlers/event.rb +25 -0
- data/lib/state_machine/yard/handlers/machine.rb +344 -0
- data/lib/state_machine/yard/handlers/state.rb +25 -0
- data/lib/state_machine/yard/handlers/transition.rb +47 -0
- data/lib/state_machine/yard/templates.rb +3 -0
- data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
- data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
- data/lib/tasks/state_machine.rake +1 -0
- data/lib/tasks/state_machine.rb +30 -0
- data/lib/yard-state_machine.rb +2 -0
- data/state_machine.gemspec +22 -0
- data/test/files/en.yml +17 -0
- data/test/files/switch.rb +15 -0
- data/test/functional/state_machine_test.rb +1066 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/assertions_test.rb +40 -0
- data/test/unit/branch_test.rb +969 -0
- data/test/unit/callback_test.rb +704 -0
- data/test/unit/error_test.rb +43 -0
- data/test/unit/eval_helpers_test.rb +270 -0
- data/test/unit/event_collection_test.rb +398 -0
- data/test/unit/event_test.rb +1196 -0
- data/test/unit/graph_test.rb +98 -0
- data/test/unit/helper_module_test.rb +17 -0
- data/test/unit/integrations/active_model_test.rb +1245 -0
- data/test/unit/integrations/active_record_test.rb +2551 -0
- data/test/unit/integrations/base_test.rb +104 -0
- data/test/unit/integrations/data_mapper_test.rb +2194 -0
- data/test/unit/integrations/mongo_mapper_test.rb +2026 -0
- data/test/unit/integrations/mongoid_test.rb +2309 -0
- data/test/unit/integrations/sequel_test.rb +1896 -0
- data/test/unit/integrations_test.rb +83 -0
- data/test/unit/invalid_event_test.rb +20 -0
- data/test/unit/invalid_parallel_transition_test.rb +18 -0
- data/test/unit/invalid_transition_test.rb +115 -0
- data/test/unit/machine_collection_test.rb +603 -0
- data/test/unit/machine_test.rb +3431 -0
- data/test/unit/matcher_helpers_test.rb +37 -0
- data/test/unit/matcher_test.rb +155 -0
- data/test/unit/node_collection_test.rb +362 -0
- data/test/unit/path_collection_test.rb +266 -0
- data/test/unit/path_test.rb +485 -0
- data/test/unit/state_collection_test.rb +352 -0
- data/test/unit/state_context_test.rb +441 -0
- data/test/unit/state_machine_test.rb +31 -0
- data/test/unit/state_test.rb +1101 -0
- data/test/unit/transition_collection_test.rb +2168 -0
- data/test/unit/transition_test.rb +1558 -0
- metadata +435 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
|
2
|
+
|
3
|
+
# Load library
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
module BaseTest
|
7
|
+
class IntegrationTest < Test::Unit::TestCase
|
8
|
+
def test_should_have_an_integration_name
|
9
|
+
assert_equal :base, StateMachine::Integrations::Base.integration_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_should_not_be_available
|
13
|
+
assert !StateMachine::Integrations::Base.available?
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_should_not_have_any_matching_ancestors
|
17
|
+
assert_equal [], StateMachine::Integrations::Base.matching_ancestors
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_not_match_any_classes
|
21
|
+
assert !StateMachine::Integrations::Base.matches?(Class.new)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_not_have_a_locale_path
|
25
|
+
assert_nil StateMachine::Integrations::Base.locale_path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class IncludedTest < Test::Unit::TestCase
|
30
|
+
def setup
|
31
|
+
@integration = Module.new
|
32
|
+
StateMachine::Integrations.const_set('Custom', @integration)
|
33
|
+
|
34
|
+
@integration.class_eval do
|
35
|
+
include StateMachine::Integrations::Base
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_not_have_any_defaults
|
40
|
+
assert_nil @integration.defaults
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_not_have_any_versions
|
44
|
+
assert_equal [], @integration.versions
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_track_version
|
48
|
+
version1 = @integration.version '1.0' do
|
49
|
+
def self.active?
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
version2 = @integration.version '2.0' do
|
55
|
+
def self.active?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_equal [version1, version2], @integration.versions
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_should_allow_active_versions_to_override_default_behavior
|
64
|
+
@integration.class_eval do
|
65
|
+
def version1_included?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
def version2_included?
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@integration.version '1.0' do
|
75
|
+
def self.active?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def version1_included?
|
80
|
+
true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
@integration.version '2.0' do
|
85
|
+
def self.active?
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
def version2_included?
|
90
|
+
true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
@machine = StateMachine::Machine.new(Class.new, :integration => :custom)
|
95
|
+
assert @machine.version1_included?
|
96
|
+
assert !@machine.version2_included?
|
97
|
+
end
|
98
|
+
|
99
|
+
def teardown
|
100
|
+
StateMachine::Integrations.send(:remove_const, 'Custom')
|
101
|
+
super
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,2194 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
|
2
|
+
|
3
|
+
require 'dm-core'
|
4
|
+
require 'dm-core/version' unless defined?(DataMapper::VERSION)
|
5
|
+
require 'dm-observer'
|
6
|
+
|
7
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.3')
|
8
|
+
require 'dm-migrations'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Establish database connection
|
12
|
+
DataMapper.setup(:default, 'sqlite3::memory:')
|
13
|
+
DataObjects::Sqlite3.logger = DataObjects::Logger.new("#{File.dirname(__FILE__)}/../../data_mapper.log", :debug)
|
14
|
+
|
15
|
+
module DataMapperTest
|
16
|
+
class BaseTestCase < Test::Unit::TestCase
|
17
|
+
def default_test
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
super
|
22
|
+
@resources.uniq.each {|resource| DataMapperTest.send(:remove_const, resource)} if instance_variable_defined?('@resources')
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
# Creates a new DataMapper resource (and the associated table)
|
27
|
+
def new_resource(create_table = :foo, &block)
|
28
|
+
base_table_name = create_table || :foo
|
29
|
+
name = base_table_name.to_s.capitalize
|
30
|
+
table_name = "#{base_table_name}_#{rand(1000000)}"
|
31
|
+
|
32
|
+
resource = Class.new
|
33
|
+
DataMapperTest.send(:remove_const, name) if DataMapperTest.const_defined?(name)
|
34
|
+
DataMapperTest.const_set(name, resource)
|
35
|
+
(@resources ||= []) << name
|
36
|
+
|
37
|
+
resource.class_eval do
|
38
|
+
include DataMapper::Resource
|
39
|
+
|
40
|
+
storage_names[:default] = table_name.to_s
|
41
|
+
|
42
|
+
property :id, resource.class_eval('Serial')
|
43
|
+
property :state, String
|
44
|
+
end
|
45
|
+
resource.class_eval(&block) if block_given?
|
46
|
+
resource.auto_migrate! if create_table
|
47
|
+
resource
|
48
|
+
end
|
49
|
+
|
50
|
+
# Creates a new DataMapper observer
|
51
|
+
def new_observer(resource, &block)
|
52
|
+
observer = Class.new do
|
53
|
+
include DataMapper::Observer
|
54
|
+
end
|
55
|
+
observer.observe(resource)
|
56
|
+
observer.class_eval(&block) if block_given?
|
57
|
+
observer
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class IntegrationTest < BaseTestCase
|
62
|
+
def test_should_have_an_integration_name
|
63
|
+
assert_equal :data_mapper, StateMachine::Integrations::DataMapper.integration_name
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_should_be_available
|
67
|
+
assert StateMachine::Integrations::DataMapper.available?
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_should_match_if_class_includes_data_mapper
|
71
|
+
assert StateMachine::Integrations::DataMapper.matches?(new_resource)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_should_not_match_if_class_does_not_include_data_mapper
|
75
|
+
assert !StateMachine::Integrations::DataMapper.matches?(Class.new)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_should_have_defaults
|
79
|
+
assert_equal({:action => :save, :use_transactions => false}, StateMachine::Integrations::DataMapper.defaults)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_should_not_have_a_locale_path
|
83
|
+
assert_nil StateMachine::Integrations::DataMapper.locale_path
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class MachineWithoutDatabaseTest < BaseTestCase
|
88
|
+
def setup
|
89
|
+
@resource = new_resource(false) do
|
90
|
+
# Simulate the database not being available entirely
|
91
|
+
def self.repository
|
92
|
+
raise DataObjects::SyntaxError
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_should_allow_machine_creation
|
98
|
+
assert_nothing_raised { StateMachine::Machine.new(@resource) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class MachineUnmigratedTest < BaseTestCase
|
103
|
+
def setup
|
104
|
+
@resource = new_resource(false)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_should_allow_machine_creation
|
108
|
+
assert_nothing_raised { StateMachine::Machine.new(@resource) }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class MachineWithoutPropertyTest < BaseTestCase
|
113
|
+
def setup
|
114
|
+
@resource = new_resource
|
115
|
+
StateMachine::Machine.new(@resource, :status)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_should_define_field_with_string_type
|
119
|
+
property = @resource.properties.detect {|p| p.name == :status}
|
120
|
+
assert_not_nil property
|
121
|
+
|
122
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('1.0.0')
|
123
|
+
assert_instance_of DataMapper::Property::String, property
|
124
|
+
else
|
125
|
+
assert_equal String, property.type
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class MachineWithPropertyTest < BaseTestCase
|
131
|
+
def setup
|
132
|
+
@resource = new_resource do
|
133
|
+
property :status, Integer
|
134
|
+
end
|
135
|
+
StateMachine::Machine.new(@resource, :status)
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_should_not_redefine_field
|
139
|
+
property = @resource.properties.detect {|p| p.name == :status}
|
140
|
+
assert_not_nil property
|
141
|
+
|
142
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('1.0.0')
|
143
|
+
assert_instance_of DataMapper::Property::Integer, property
|
144
|
+
else
|
145
|
+
assert_equal Integer, property.type
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class MachineByDefaultTest < BaseTestCase
|
151
|
+
def setup
|
152
|
+
@resource = new_resource
|
153
|
+
@machine = StateMachine::Machine.new(@resource)
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_should_use_save_as_action
|
157
|
+
assert_equal :save, @machine.action
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_should_not_use_transactions
|
161
|
+
assert_equal false, @machine.use_transactions
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_should_not_have_any_before_callbacks
|
165
|
+
assert_equal 0, @machine.callbacks[:before].size
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_should_not_have_any_after_callbacks
|
169
|
+
assert_equal 0, @machine.callbacks[:after].size
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class MachineWithStatesTest < BaseTestCase
|
174
|
+
def setup
|
175
|
+
@resource = new_resource
|
176
|
+
@machine = StateMachine::Machine.new(@resource)
|
177
|
+
@machine.state :first_gear
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_should_humanize_name
|
181
|
+
assert_equal 'first gear', @machine.state(:first_gear).human_name
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class MachineWithStaticInitialStateTest < BaseTestCase
|
186
|
+
def setup
|
187
|
+
@resource = new_resource(:vehicle) do
|
188
|
+
attr_accessor :value
|
189
|
+
end
|
190
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_should_set_initial_state_on_created_object
|
194
|
+
record = @resource.new
|
195
|
+
assert_equal 'parked', record.state
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_should_set_initial_state_with_nil_attributes
|
199
|
+
@resource.class_eval do
|
200
|
+
def attributes=(attributes)
|
201
|
+
super(attributes || {})
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
record = @resource.new(nil)
|
206
|
+
assert_equal 'parked', record.state
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_should_still_set_attributes
|
210
|
+
record = @resource.new(:value => 1)
|
211
|
+
assert_equal 1, record.value
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_should_not_allow_initialize_blocks
|
215
|
+
block_args = nil
|
216
|
+
@resource.new do |*args|
|
217
|
+
block_args = args
|
218
|
+
end
|
219
|
+
|
220
|
+
assert_nil block_args
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_should_set_initial_state_before_setting_attributes
|
224
|
+
@resource.class_eval do
|
225
|
+
attr_accessor :state_during_setter
|
226
|
+
|
227
|
+
remove_method :value=
|
228
|
+
define_method(:value=) do |value|
|
229
|
+
self.state_during_setter = state
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
record = @resource.new(:value => 1)
|
234
|
+
assert_equal 'parked', record.state_during_setter
|
235
|
+
end
|
236
|
+
|
237
|
+
def test_should_not_set_initial_state_after_already_initialized
|
238
|
+
record = @resource.new(:value => 1)
|
239
|
+
assert_equal 'parked', record.state
|
240
|
+
|
241
|
+
record.state = 'idling'
|
242
|
+
record.attributes = {}
|
243
|
+
assert_equal 'idling', record.state
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_should_persist_initial_state
|
247
|
+
record = @resource.new
|
248
|
+
record.save
|
249
|
+
record.reload
|
250
|
+
assert_equal 'parked', record.state
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_should_persist_initial_state_on_dup
|
254
|
+
record = @resource.create.dup
|
255
|
+
record.save
|
256
|
+
record.reload
|
257
|
+
assert_equal 'parked', record.state
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_should_use_stored_values_when_loading_from_database
|
261
|
+
@machine.state :idling
|
262
|
+
|
263
|
+
record = @resource.get(@resource.create(:state => 'idling').id)
|
264
|
+
assert_equal 'idling', record.state
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_should_use_stored_values_when_loading_from_database_with_nil_state
|
268
|
+
@machine.state nil
|
269
|
+
|
270
|
+
record = @resource.get(@resource.create(:state => nil).id)
|
271
|
+
assert_nil record.state
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_should_use_stored_values_when_loading_for_many_association
|
275
|
+
@machine.state :idling
|
276
|
+
|
277
|
+
@resource.property :owner_id, Integer
|
278
|
+
@resource.auto_migrate!
|
279
|
+
|
280
|
+
owner_resource = new_resource(:owner) do
|
281
|
+
has n, :vehicles
|
282
|
+
end
|
283
|
+
|
284
|
+
owner = owner_resource.create
|
285
|
+
record = @resource.new(:state => 'idling')
|
286
|
+
record.owner_id = owner.id
|
287
|
+
record.save
|
288
|
+
assert_equal 'idling', owner.vehicles[0].state
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_should_use_stored_values_when_loading_for_one_association
|
292
|
+
@machine.state :idling
|
293
|
+
|
294
|
+
@resource.property :owner_id, Integer
|
295
|
+
@resource.auto_migrate!
|
296
|
+
|
297
|
+
owner_resource = new_resource(:owner) do
|
298
|
+
has 1, :vehicle
|
299
|
+
end
|
300
|
+
|
301
|
+
owner = owner_resource.create
|
302
|
+
record = @resource.new(:state => 'idling')
|
303
|
+
record.owner_id = owner.id
|
304
|
+
record.save
|
305
|
+
assert_equal 'idling', owner.vehicle.state
|
306
|
+
end
|
307
|
+
|
308
|
+
def test_should_use_stored_values_when_loading_for_belongs_to_association
|
309
|
+
@machine.state :idling
|
310
|
+
|
311
|
+
driver_resource = new_resource(:driver) do
|
312
|
+
belongs_to :vehicle
|
313
|
+
end
|
314
|
+
|
315
|
+
record = @resource.create(:state => 'idling')
|
316
|
+
driver = driver_resource.create(:vehicle_id => record.id)
|
317
|
+
assert_equal 'idling', driver.vehicle.state
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
class MachineWithDynamicInitialStateTest < BaseTestCase
|
322
|
+
def setup
|
323
|
+
@resource = new_resource do
|
324
|
+
attr_accessor :value
|
325
|
+
end
|
326
|
+
@machine = StateMachine::Machine.new(@resource, :initial => lambda {|object| :parked})
|
327
|
+
@machine.state :parked
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_should_set_initial_state_on_created_object
|
331
|
+
record = @resource.new
|
332
|
+
assert_equal 'parked', record.state
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_should_still_set_attributes
|
336
|
+
record = @resource.new(:value => 1)
|
337
|
+
assert_equal 1, record.value
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_should_not_allow_initialize_blocks
|
341
|
+
block_args = nil
|
342
|
+
@resource.new do |*args|
|
343
|
+
block_args = args
|
344
|
+
end
|
345
|
+
|
346
|
+
assert_nil block_args
|
347
|
+
end
|
348
|
+
|
349
|
+
def test_should_set_initial_state_after_setting_attributes
|
350
|
+
@resource.class_eval do
|
351
|
+
attr_accessor :state_during_setter
|
352
|
+
|
353
|
+
remove_method :value=
|
354
|
+
define_method(:value=) do |value|
|
355
|
+
self.state_during_setter = state || 'nil'
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
record = @resource.new(:value => 1)
|
360
|
+
assert_equal 'nil', record.state_during_setter
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_should_not_set_initial_state_after_already_initialized
|
364
|
+
record = @resource.new(:value => 1)
|
365
|
+
assert_equal 'parked', record.state
|
366
|
+
|
367
|
+
record.state = 'idling'
|
368
|
+
record.attributes = {}
|
369
|
+
assert_equal 'idling', record.state
|
370
|
+
end
|
371
|
+
|
372
|
+
def test_should_persist_initial_state
|
373
|
+
record = @resource.new
|
374
|
+
record.save
|
375
|
+
record.reload
|
376
|
+
assert_equal 'parked', record.state
|
377
|
+
end
|
378
|
+
|
379
|
+
def test_should_persist_initial_state_on_dup
|
380
|
+
record = @resource.create.dup
|
381
|
+
record.save
|
382
|
+
record.reload
|
383
|
+
assert_equal 'parked', record.state
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_should_use_stored_values_when_loading_from_database
|
387
|
+
@machine.state :idling
|
388
|
+
|
389
|
+
record = @resource.get(@resource.create(:state => 'idling').id)
|
390
|
+
assert_equal 'idling', record.state
|
391
|
+
end
|
392
|
+
|
393
|
+
def test_should_use_stored_values_when_loading_from_database_with_nil_state
|
394
|
+
@machine.state nil
|
395
|
+
|
396
|
+
record = @resource.get(@resource.create(:state => nil).id)
|
397
|
+
assert_nil record.state
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
class MachineWithEventsTest < BaseTestCase
|
402
|
+
def setup
|
403
|
+
@resource = new_resource
|
404
|
+
@machine = StateMachine::Machine.new(@resource)
|
405
|
+
@machine.event :shift_up
|
406
|
+
end
|
407
|
+
|
408
|
+
def test_should_humanize_name
|
409
|
+
assert_equal 'shift up', @machine.event(:shift_up).human_name
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
class MachineWithSameColumnDefaultTest < BaseTestCase
|
414
|
+
def setup
|
415
|
+
@original_stderr, $stderr = $stderr, StringIO.new
|
416
|
+
|
417
|
+
@resource = new_resource do
|
418
|
+
property :status, String, :default => 'parked'
|
419
|
+
end
|
420
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
|
421
|
+
@record = @resource.new
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_should_use_machine_default
|
425
|
+
assert_equal 'parked', @record.status
|
426
|
+
end
|
427
|
+
|
428
|
+
def test_should_not_generate_a_warning
|
429
|
+
assert_no_match(/have defined a different default/, $stderr.string)
|
430
|
+
end
|
431
|
+
|
432
|
+
def teardown
|
433
|
+
$stderr = @original_stderr
|
434
|
+
super
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
class MachineWithDifferentColumnDefaultTest < BaseTestCase
|
439
|
+
def setup
|
440
|
+
@original_stderr, $stderr = $stderr, StringIO.new
|
441
|
+
|
442
|
+
@resource = new_resource do
|
443
|
+
property :status, String, :default => 'idling'
|
444
|
+
end
|
445
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
|
446
|
+
@record = @resource.new
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_should_use_machine_default
|
450
|
+
assert_equal 'parked', @record.status
|
451
|
+
end
|
452
|
+
|
453
|
+
def test_should_generate_a_warning
|
454
|
+
assert_match(/Both DataMapperTest::Foo and its :status machine have defined a different default for "status". Use only one or the other for defining defaults to avoid unexpected behaviors\./, $stderr.string)
|
455
|
+
end
|
456
|
+
|
457
|
+
def teardown
|
458
|
+
$stderr = @original_stderr
|
459
|
+
super
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
class MachineWithDifferentIntegerColumnDefaultTest < BaseTestCase
|
464
|
+
def setup
|
465
|
+
@original_stderr, $stderr = $stderr, StringIO.new
|
466
|
+
|
467
|
+
@resource = new_resource do
|
468
|
+
property :status, Integer, :default => 0
|
469
|
+
end
|
470
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
|
471
|
+
@machine.state :parked, :value => 1
|
472
|
+
@record = @resource.new
|
473
|
+
end
|
474
|
+
|
475
|
+
def test_should_use_machine_default
|
476
|
+
assert_equal 1, @record.status
|
477
|
+
end
|
478
|
+
|
479
|
+
def test_should_generate_a_warning
|
480
|
+
assert_match(/Both DataMapperTest::Foo and its :status machine have defined a different default for "status". Use only one or the other for defining defaults to avoid unexpected behaviors\./, $stderr.string)
|
481
|
+
end
|
482
|
+
|
483
|
+
def teardown
|
484
|
+
$stderr = @original_stderr
|
485
|
+
super
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
class MachineWithConflictingPredicateTest < BaseTestCase
|
490
|
+
def setup
|
491
|
+
@resource = new_resource do
|
492
|
+
def state?(*args)
|
493
|
+
true
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
@machine = StateMachine::Machine.new(@resource)
|
498
|
+
@record = @resource.new
|
499
|
+
end
|
500
|
+
|
501
|
+
def test_should_not_define_attribute_predicate
|
502
|
+
assert @record.state?
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
class MachineWithConflictingStateNameTest < BaseTestCase
|
507
|
+
def setup
|
508
|
+
require 'stringio'
|
509
|
+
@original_stderr, $stderr = $stderr, StringIO.new
|
510
|
+
|
511
|
+
@resource = new_resource
|
512
|
+
end
|
513
|
+
|
514
|
+
def test_should_output_warning_with_same_machine_name
|
515
|
+
@machine = StateMachine::Machine.new(@resource)
|
516
|
+
@machine.state :state
|
517
|
+
|
518
|
+
assert_match(/^Instance method "state\?" is already defined in DataMapperTest::Foo :state instance helpers, use generic helper instead.*\n$/, $stderr.string)
|
519
|
+
end
|
520
|
+
|
521
|
+
def test_should_not_output_warning_with_same_machine_attribute
|
522
|
+
@machine = StateMachine::Machine.new(@resource, :public_state, :attribute => :state)
|
523
|
+
@machine.state :state
|
524
|
+
|
525
|
+
assert_no_match(/^Instance method "state\?" is already defined.*\n$/, $stderr.string)
|
526
|
+
end
|
527
|
+
|
528
|
+
def teardown
|
529
|
+
$stderr = @original_stderr
|
530
|
+
super
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
class MachineWithColumnStateAttributeTest < BaseTestCase
|
535
|
+
def setup
|
536
|
+
@resource = new_resource
|
537
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
538
|
+
@machine.other_states(:idling)
|
539
|
+
|
540
|
+
@record = @resource.new
|
541
|
+
end
|
542
|
+
|
543
|
+
def test_should_not_override_the_column_reader
|
544
|
+
@record.attribute_set(:state, 'parked')
|
545
|
+
assert_equal 'parked', @record.state
|
546
|
+
end
|
547
|
+
|
548
|
+
def test_should_not_override_the_column_writer
|
549
|
+
@record.state = 'parked'
|
550
|
+
assert_equal 'parked', @record.attribute_get(:state)
|
551
|
+
end
|
552
|
+
|
553
|
+
def test_should_have_an_attribute_predicate
|
554
|
+
assert @record.respond_to?(:state?)
|
555
|
+
end
|
556
|
+
|
557
|
+
def test_should_raise_exception_for_predicate_without_parameters
|
558
|
+
assert_raise(ArgumentError) { @record.state? }
|
559
|
+
end
|
560
|
+
|
561
|
+
def test_should_return_false_for_predicate_if_does_not_match_current_value
|
562
|
+
assert !@record.state?(:idling)
|
563
|
+
end
|
564
|
+
|
565
|
+
def test_should_return_true_for_predicate_if_matches_current_value
|
566
|
+
assert @record.state?(:parked)
|
567
|
+
end
|
568
|
+
|
569
|
+
def test_should_raise_exception_for_predicate_if_invalid_state_specified
|
570
|
+
assert_raise(IndexError) { @record.state?(:invalid) }
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
|
575
|
+
def setup
|
576
|
+
@resource = new_resource do
|
577
|
+
def initialize
|
578
|
+
# Skip attribute initialization
|
579
|
+
@initialized_state_machines = true
|
580
|
+
super
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => 'parked')
|
585
|
+
@record = @resource.new
|
586
|
+
end
|
587
|
+
|
588
|
+
def test_should_define_a_new_property_for_the_attribute
|
589
|
+
assert_not_nil @resource.properties[:status]
|
590
|
+
end
|
591
|
+
|
592
|
+
def test_should_define_a_reader_attribute_for_the_attribute
|
593
|
+
assert @record.respond_to?(:status)
|
594
|
+
end
|
595
|
+
|
596
|
+
def test_should_define_a_writer_attribute_for_the_attribute
|
597
|
+
assert @record.respond_to?(:status=)
|
598
|
+
end
|
599
|
+
|
600
|
+
def test_should_define_an_attribute_predicate
|
601
|
+
assert @record.respond_to?(:status?)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
605
|
+
class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
|
606
|
+
def setup
|
607
|
+
@resource = new_resource do
|
608
|
+
attr_accessor :status
|
609
|
+
end
|
610
|
+
|
611
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
|
612
|
+
@machine.other_states(:idling)
|
613
|
+
@record = @resource.new
|
614
|
+
end
|
615
|
+
|
616
|
+
def test_should_return_false_for_predicate_if_does_not_match_current_value
|
617
|
+
assert !@record.status?(:idling)
|
618
|
+
end
|
619
|
+
|
620
|
+
def test_should_return_true_for_predicate_if_matches_current_value
|
621
|
+
assert @record.status?(:parked)
|
622
|
+
end
|
623
|
+
|
624
|
+
def test_should_raise_exception_for_predicate_if_invalid_state_specified
|
625
|
+
assert_raise(IndexError) { @record.status?(:invalid) }
|
626
|
+
end
|
627
|
+
|
628
|
+
def test_should_set_initial_state_on_created_object
|
629
|
+
assert_equal 'parked', @record.status
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
class MachineWithInitializedStateTest < BaseTestCase
|
634
|
+
def setup
|
635
|
+
@resource = new_resource
|
636
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
637
|
+
@machine.state :idling
|
638
|
+
end
|
639
|
+
|
640
|
+
def test_should_allow_nil_initial_state_when_static
|
641
|
+
@machine.state nil
|
642
|
+
|
643
|
+
record = @resource.new(:state => nil)
|
644
|
+
assert_nil record.state
|
645
|
+
end
|
646
|
+
|
647
|
+
def test_should_allow_nil_initial_state_when_dynamic
|
648
|
+
@machine.state nil
|
649
|
+
|
650
|
+
@machine.initial_state = lambda {:parked}
|
651
|
+
record = @resource.new(:state => nil)
|
652
|
+
assert_nil record.state
|
653
|
+
end
|
654
|
+
|
655
|
+
def test_should_allow_different_initial_state_when_static
|
656
|
+
record = @resource.new(:state => 'idling')
|
657
|
+
assert_equal 'idling', record.state
|
658
|
+
end
|
659
|
+
|
660
|
+
def test_should_allow_different_initial_state_when_dynamic
|
661
|
+
@machine.initial_state = lambda {:parked}
|
662
|
+
record = @resource.new(:state => 'idling')
|
663
|
+
assert_equal 'idling', record.state
|
664
|
+
end
|
665
|
+
|
666
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.9.8')
|
667
|
+
def test_should_raise_exception_if_protected
|
668
|
+
resource = new_resource do
|
669
|
+
protected :state=
|
670
|
+
end
|
671
|
+
|
672
|
+
machine = StateMachine::Machine.new(resource, :initial => :parked)
|
673
|
+
machine.state :idling
|
674
|
+
|
675
|
+
assert_raise(ArgumentError) { resource.new(:state => 'idling') }
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
class MachineMultipleTest < BaseTestCase
|
681
|
+
def setup
|
682
|
+
@resource = new_resource do
|
683
|
+
property :status, String
|
684
|
+
end
|
685
|
+
@state_machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
686
|
+
@status_machine = StateMachine::Machine.new(@resource, :status, :initial => :idling)
|
687
|
+
end
|
688
|
+
|
689
|
+
def test_should_should_initialize_each_state
|
690
|
+
record = @resource.new
|
691
|
+
assert_equal 'parked', record.state
|
692
|
+
assert_equal 'idling', record.status
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
class MachineWithLoopbackTest < BaseTestCase
|
697
|
+
def setup
|
698
|
+
@resource = new_resource do
|
699
|
+
property :updated_at, DateTime
|
700
|
+
|
701
|
+
# Simulate dm-timestamps
|
702
|
+
before :update do
|
703
|
+
return unless dirty?
|
704
|
+
self.updated_at = DateTime.now
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
709
|
+
@machine.event :park
|
710
|
+
|
711
|
+
@record = @resource.create(:updated_at => Time.now - 1)
|
712
|
+
@transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
|
713
|
+
|
714
|
+
@timestamp = @record.updated_at
|
715
|
+
@transition.perform
|
716
|
+
end
|
717
|
+
|
718
|
+
def test_should_not_update_record
|
719
|
+
assert_equal @timestamp, @record.updated_at
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
class MachineWithDirtyAttributesTest < BaseTestCase
|
724
|
+
def setup
|
725
|
+
@resource = new_resource
|
726
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
727
|
+
@machine.event :ignite
|
728
|
+
@machine.state :idling
|
729
|
+
|
730
|
+
@record = @resource.create
|
731
|
+
|
732
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
733
|
+
@transition.perform(false)
|
734
|
+
end
|
735
|
+
|
736
|
+
def test_should_include_state_in_changed_attributes
|
737
|
+
assert_equal({@resource.properties[:state] => 'idling'}, @record.dirty_attributes)
|
738
|
+
end
|
739
|
+
|
740
|
+
def test_should_track_attribute_change
|
741
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
742
|
+
assert_equal({@resource.properties[:state] => 'parked'}, @record.original_attributes)
|
743
|
+
else
|
744
|
+
assert_equal({:state => 'parked'}, @record.original_values)
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
def test_should_not_reset_changes_on_multiple_transitions
|
749
|
+
transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
|
750
|
+
transition.perform(false)
|
751
|
+
|
752
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
753
|
+
assert_equal({@resource.properties[:state] => 'parked'}, @record.original_attributes)
|
754
|
+
else
|
755
|
+
assert_equal({:state => 'parked'}, @record.original_values)
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
def test_should_not_have_changes_when_loaded_from_database
|
760
|
+
record = @resource.get(@record.id)
|
761
|
+
assert record.dirty_attributes.empty?
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
class MachineWithDirtyAttributesDuringLoopbackTest < BaseTestCase
|
766
|
+
def setup
|
767
|
+
@resource = new_resource
|
768
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
769
|
+
@machine.event :park
|
770
|
+
|
771
|
+
@record = @resource.create
|
772
|
+
|
773
|
+
@transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
|
774
|
+
@transition.perform(false)
|
775
|
+
end
|
776
|
+
|
777
|
+
def test_should_not_include_state_in_changed_attributes
|
778
|
+
assert_equal({}, @record.dirty_attributes)
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
|
783
|
+
def setup
|
784
|
+
@resource = new_resource do
|
785
|
+
property :status, String
|
786
|
+
end
|
787
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
|
788
|
+
@machine.event :ignite
|
789
|
+
@machine.state :idling
|
790
|
+
|
791
|
+
@record = @resource.create
|
792
|
+
|
793
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
794
|
+
@transition.perform(false)
|
795
|
+
end
|
796
|
+
|
797
|
+
def test_should_include_state_in_changed_attributes
|
798
|
+
assert_equal({@resource.properties[:status] => 'idling'}, @record.dirty_attributes)
|
799
|
+
end
|
800
|
+
|
801
|
+
def test_should_track_attribute_change
|
802
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
803
|
+
assert_equal({@resource.properties[:status] => 'parked'}, @record.original_attributes)
|
804
|
+
else
|
805
|
+
assert_equal({:status => 'parked'}, @record.original_values)
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
def test_should_not_reset_changes_on_multiple_transitions
|
810
|
+
transition = StateMachine::Transition.new(@record, @machine, :ignite, :idling, :idling)
|
811
|
+
transition.perform(false)
|
812
|
+
|
813
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
814
|
+
assert_equal({@resource.properties[:status] => 'parked'}, @record.original_attributes)
|
815
|
+
else
|
816
|
+
assert_equal({:status => 'parked'}, @record.original_values)
|
817
|
+
end
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
|
822
|
+
def setup
|
823
|
+
@resource = new_resource do
|
824
|
+
property :status, String
|
825
|
+
end
|
826
|
+
@machine = StateMachine::Machine.new(@resource, :status, :initial => :parked)
|
827
|
+
@machine.event :park
|
828
|
+
|
829
|
+
@record = @resource.create
|
830
|
+
|
831
|
+
@transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
|
832
|
+
@transition.perform(false)
|
833
|
+
end
|
834
|
+
|
835
|
+
def test_should_not_include_state_in_changed_attributes
|
836
|
+
assert_equal({}, @record.dirty_attributes)
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
|
841
|
+
def setup
|
842
|
+
@resource = new_resource
|
843
|
+
@machine = StateMachine::Machine.new(@resource, :initial => :parked)
|
844
|
+
@machine.event :ignite
|
845
|
+
|
846
|
+
@record = @resource.create
|
847
|
+
@record.state_event = 'ignite'
|
848
|
+
end
|
849
|
+
|
850
|
+
def test_should_not_include_state_in_changed_attributes
|
851
|
+
assert_equal({}, @record.dirty_attributes)
|
852
|
+
end
|
853
|
+
|
854
|
+
def test_should_not_track_attribute_change
|
855
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
856
|
+
assert_equal({}, @record.original_attributes)
|
857
|
+
else
|
858
|
+
assert_equal({}, @record.original_values)
|
859
|
+
end
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
class MachineWithoutTransactionsTest < BaseTestCase
|
864
|
+
def setup
|
865
|
+
@resource = new_resource
|
866
|
+
@machine = StateMachine::Machine.new(@resource, :use_transactions => false)
|
867
|
+
end
|
868
|
+
|
869
|
+
def test_should_not_rollback_transaction_if_false
|
870
|
+
@machine.within_transaction(@resource.new) do
|
871
|
+
@resource.create
|
872
|
+
false
|
873
|
+
end
|
874
|
+
|
875
|
+
assert_equal 1, @resource.all.size
|
876
|
+
end
|
877
|
+
|
878
|
+
def test_should_not_rollback_transaction_if_true
|
879
|
+
@machine.within_transaction(@resource.new) do
|
880
|
+
@resource.create
|
881
|
+
true
|
882
|
+
end
|
883
|
+
|
884
|
+
assert_equal 1, @resource.all.size
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
begin
|
889
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.3')
|
890
|
+
require 'dm-transactions'
|
891
|
+
end
|
892
|
+
|
893
|
+
class MachineWithTransactionsTest < BaseTestCase
|
894
|
+
def setup
|
895
|
+
@resource = new_resource
|
896
|
+
@machine = StateMachine::Machine.new(@resource, :use_transactions => true)
|
897
|
+
end
|
898
|
+
|
899
|
+
def test_should_rollback_transaction_if_false
|
900
|
+
@machine.within_transaction(@resource.new) do
|
901
|
+
@resource.create
|
902
|
+
false
|
903
|
+
end
|
904
|
+
|
905
|
+
assert_equal 0, @resource.all.size
|
906
|
+
end
|
907
|
+
|
908
|
+
def test_should_not_rollback_transaction_if_true
|
909
|
+
@machine.within_transaction(@resource.new) do
|
910
|
+
@resource.create
|
911
|
+
true
|
912
|
+
end
|
913
|
+
|
914
|
+
assert_equal 1, @resource.all.size
|
915
|
+
end
|
916
|
+
end
|
917
|
+
rescue LoadError
|
918
|
+
$stderr.puts "Skipping DataMapper Transaction tests."
|
919
|
+
end
|
920
|
+
|
921
|
+
class MachineWithCallbacksTest < BaseTestCase
|
922
|
+
def setup
|
923
|
+
@resource = new_resource
|
924
|
+
@machine = StateMachine::Machine.new(@resource)
|
925
|
+
@machine.state :parked, :idling
|
926
|
+
@machine.event :ignite
|
927
|
+
|
928
|
+
@record = @resource.new(:state => 'parked')
|
929
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
930
|
+
end
|
931
|
+
|
932
|
+
def test_should_run_before_callbacks
|
933
|
+
called = false
|
934
|
+
@machine.before_transition {called = true}
|
935
|
+
|
936
|
+
@transition.perform
|
937
|
+
assert called
|
938
|
+
end
|
939
|
+
|
940
|
+
def test_should_pass_transition_to_before_callbacks_with_one_argument
|
941
|
+
transition = nil
|
942
|
+
@machine.before_transition {|arg| transition = arg}
|
943
|
+
|
944
|
+
@transition.perform
|
945
|
+
assert_equal @transition, transition
|
946
|
+
end
|
947
|
+
|
948
|
+
def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
|
949
|
+
callback_args = nil
|
950
|
+
@machine.before_transition {|*args| callback_args = args}
|
951
|
+
|
952
|
+
@transition.perform
|
953
|
+
assert_equal [@transition], callback_args
|
954
|
+
end
|
955
|
+
|
956
|
+
def test_should_run_before_callbacks_within_the_context_of_the_record
|
957
|
+
context = nil
|
958
|
+
@machine.before_transition {context = self}
|
959
|
+
|
960
|
+
@transition.perform
|
961
|
+
assert_equal @record, context
|
962
|
+
end
|
963
|
+
|
964
|
+
def test_should_run_after_callbacks
|
965
|
+
called = false
|
966
|
+
@machine.after_transition {called = true}
|
967
|
+
|
968
|
+
@transition.perform
|
969
|
+
assert called
|
970
|
+
end
|
971
|
+
|
972
|
+
def test_should_pass_transition_to_after_callbacks_with_multiple_arguments
|
973
|
+
callback_args = nil
|
974
|
+
@machine.after_transition {|*args| callback_args = args}
|
975
|
+
|
976
|
+
@transition.perform
|
977
|
+
assert_equal [@transition], callback_args
|
978
|
+
end
|
979
|
+
|
980
|
+
def test_should_run_after_callbacks_with_the_context_of_the_record
|
981
|
+
context = nil
|
982
|
+
@machine.after_transition {context = self}
|
983
|
+
|
984
|
+
@transition.perform
|
985
|
+
assert_equal @record, context
|
986
|
+
end
|
987
|
+
|
988
|
+
def test_should_run_around_callbacks
|
989
|
+
before_called = false
|
990
|
+
after_called = false
|
991
|
+
ensure_called = 0
|
992
|
+
@machine.around_transition do |block|
|
993
|
+
before_called = true
|
994
|
+
begin
|
995
|
+
block.call
|
996
|
+
ensure
|
997
|
+
ensure_called += 1
|
998
|
+
end
|
999
|
+
after_called = true
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
@transition.perform
|
1003
|
+
assert before_called
|
1004
|
+
assert after_called
|
1005
|
+
assert_equal ensure_called, 1
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def test_should_run_around_callbacks_with_the_context_of_the_record
|
1009
|
+
context = nil
|
1010
|
+
@machine.around_transition {|block| context = self; block.call}
|
1011
|
+
|
1012
|
+
@transition.perform
|
1013
|
+
assert_equal @record, context
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def test_should_allow_symbolic_callbacks
|
1017
|
+
callback_args = nil
|
1018
|
+
|
1019
|
+
klass = class << @record; self; end
|
1020
|
+
klass.send(:define_method, :after_ignite) do |*args|
|
1021
|
+
callback_args = args
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
@machine.before_transition(:after_ignite)
|
1025
|
+
|
1026
|
+
@transition.perform
|
1027
|
+
assert_equal [@transition], callback_args
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def test_should_allow_string_callbacks
|
1031
|
+
class << @record
|
1032
|
+
attr_reader :callback_result
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
@machine.before_transition('@callback_result = [1, 2, 3]')
|
1036
|
+
@transition.perform
|
1037
|
+
|
1038
|
+
assert_equal [1, 2, 3], @record.callback_result
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def test_should_run_in_expected_order
|
1042
|
+
# Avoid Ruby 2.0.0 stack too deep issues
|
1043
|
+
@resource.class_eval do
|
1044
|
+
def valid?(*)
|
1045
|
+
super
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
expected = [
|
1050
|
+
:before_transition, :before_validation, :after_validation,
|
1051
|
+
:before_save, :before_create, :after_create, :after_save,
|
1052
|
+
:after_transition
|
1053
|
+
]
|
1054
|
+
|
1055
|
+
callbacks = []
|
1056
|
+
@resource.before(:valid?) { callbacks << :before_validation }
|
1057
|
+
@resource.after(:valid?) { callbacks << :after_validation }
|
1058
|
+
@resource.before(:save) { callbacks << :before_save }
|
1059
|
+
@resource.before(:create) { callbacks << :before_create }
|
1060
|
+
@resource.after(:create) { callbacks << :after_create }
|
1061
|
+
@resource.after(:save) { callbacks << :after_save }
|
1062
|
+
|
1063
|
+
@machine.before_transition { callbacks << :before_transition }
|
1064
|
+
@machine.after_transition { callbacks << :after_transition }
|
1065
|
+
|
1066
|
+
@transition.perform
|
1067
|
+
|
1068
|
+
assert_equal expected, callbacks
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
class MachineWithFailedBeforeCallbacksTest < BaseTestCase
|
1073
|
+
def setup
|
1074
|
+
callbacks = []
|
1075
|
+
|
1076
|
+
@resource = new_resource
|
1077
|
+
@machine = StateMachine::Machine.new(@resource)
|
1078
|
+
@machine.state :parked, :idling
|
1079
|
+
@machine.event :ignite
|
1080
|
+
@machine.before_transition {callbacks << :before_1; throw :halt}
|
1081
|
+
@machine.before_transition {callbacks << :before_2}
|
1082
|
+
@machine.after_transition {callbacks << :after}
|
1083
|
+
@machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
|
1084
|
+
|
1085
|
+
@record = @resource.new(:state => 'parked')
|
1086
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
1087
|
+
@result = @transition.perform
|
1088
|
+
|
1089
|
+
@callbacks = callbacks
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
def test_should_not_be_successful
|
1093
|
+
assert !@result
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def test_should_not_change_current_state
|
1097
|
+
assert_equal 'parked', @record.state
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
def test_should_not_run_action
|
1101
|
+
assert @record.respond_to?(:new?) ? @record.new? : @record.new_record?
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
def test_should_not_run_further_callbacks
|
1105
|
+
assert_equal [:before_1], @callbacks
|
1106
|
+
end
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('1.0.0')
|
1110
|
+
class MachineNestedActionTest < BaseTestCase
|
1111
|
+
def setup
|
1112
|
+
@callbacks = []
|
1113
|
+
|
1114
|
+
@resource = new_resource
|
1115
|
+
@machine = StateMachine::Machine.new(@resource)
|
1116
|
+
@machine.event :ignite do
|
1117
|
+
transition :parked => :idling
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
@record = @resource.new(:state => 'parked')
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
def test_should_allow_transition_prior_to_creation_if_skipping_action
|
1124
|
+
record = @record
|
1125
|
+
@resource.before(:create) { record.ignite }
|
1126
|
+
result = @record.save
|
1127
|
+
|
1128
|
+
assert_equal true, result
|
1129
|
+
assert_equal "idling", @record.state
|
1130
|
+
@record.reload
|
1131
|
+
assert_equal "idling", @record.state
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
def test_should_not_allow_transition_after_creation
|
1135
|
+
record = @record
|
1136
|
+
@resource.after(:create) { record.ignite(false) }
|
1137
|
+
|
1138
|
+
result = @record.save
|
1139
|
+
|
1140
|
+
assert_equal false, result
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
class MachineWithFailedActionTest < BaseTestCase
|
1146
|
+
def setup
|
1147
|
+
@resource = new_resource do
|
1148
|
+
before(:create) { throw :halt }
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
@machine = StateMachine::Machine.new(@resource)
|
1152
|
+
@machine.state :parked, :idling
|
1153
|
+
@machine.event :ignite
|
1154
|
+
|
1155
|
+
callbacks = []
|
1156
|
+
@machine.before_transition {callbacks << :before}
|
1157
|
+
@machine.after_transition {callbacks << :after}
|
1158
|
+
@machine.after_failure {callbacks << :after_failure}
|
1159
|
+
@machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
|
1160
|
+
|
1161
|
+
@record = @resource.new(:state => 'parked')
|
1162
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
1163
|
+
@result = @transition.perform
|
1164
|
+
|
1165
|
+
@callbacks = callbacks
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
def test_should_not_be_successful
|
1169
|
+
assert !@result
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
def test_should_not_change_current_state
|
1173
|
+
assert_equal 'parked', @record.state
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
def test_should_not_save_record
|
1177
|
+
assert @record.respond_to?(:new?) ? @record.new? : @record.new_record?
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
def test_should_run_before_callbacks_and_after_callbacks_with_failures
|
1181
|
+
assert_equal [:before, :around_before, :after_failure], @callbacks
|
1182
|
+
end
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
class MachineWithFailedAfterCallbacksTest < BaseTestCase
|
1186
|
+
def setup
|
1187
|
+
callbacks = []
|
1188
|
+
|
1189
|
+
@resource = new_resource
|
1190
|
+
@machine = StateMachine::Machine.new(@resource)
|
1191
|
+
@machine.state :parked, :idling
|
1192
|
+
@machine.event :ignite
|
1193
|
+
@machine.after_transition {callbacks << :after_1; throw :halt}
|
1194
|
+
@machine.after_transition {callbacks << :after_2}
|
1195
|
+
@machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
|
1196
|
+
|
1197
|
+
@record = @resource.new(:state => 'parked')
|
1198
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
1199
|
+
@result = @transition.perform
|
1200
|
+
|
1201
|
+
@callbacks = callbacks
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
def test_should_be_successful
|
1205
|
+
assert @result
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
def test_should_change_current_state
|
1209
|
+
assert_equal 'idling', @record.state
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
def test_should_save_record
|
1213
|
+
assert !(@record.respond_to?(:new?) ? @record.new? : @record.new_record?)
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
def test_should_not_run_further_after_callbacks
|
1217
|
+
assert_equal [:around_before, :around_after, :after_1], @callbacks
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
begin
|
1222
|
+
require 'dm-validations'
|
1223
|
+
|
1224
|
+
class MachineWithValidationsTest < BaseTestCase
|
1225
|
+
def setup
|
1226
|
+
@resource = new_resource
|
1227
|
+
@machine = StateMachine::Machine.new(@resource)
|
1228
|
+
@machine.state :parked
|
1229
|
+
|
1230
|
+
@record = @resource.new
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
def test_should_invalidate_using_errors
|
1234
|
+
@record.state = 'parked'
|
1235
|
+
|
1236
|
+
@machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
|
1237
|
+
assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
def test_should_auto_prefix_custom_attributes_on_invalidation
|
1241
|
+
@machine.invalidate(@record, :event, :invalid)
|
1242
|
+
|
1243
|
+
assert_equal ['is invalid'], @record.errors.on(:state_event)
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
def test_should_clear_errors_on_reset
|
1247
|
+
@record.state = 'parked'
|
1248
|
+
@record.errors.add(:state, 'is invalid')
|
1249
|
+
|
1250
|
+
@machine.reset(@record)
|
1251
|
+
assert_nil @record.errors.on(:id)
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
def test_should_be_valid_if_state_is_known
|
1255
|
+
@record.state = 'parked'
|
1256
|
+
|
1257
|
+
assert @record.valid?
|
1258
|
+
end
|
1259
|
+
|
1260
|
+
def test_should_not_be_valid_if_state_is_unknown
|
1261
|
+
@record.state = 'invalid'
|
1262
|
+
|
1263
|
+
assert !@record.valid?
|
1264
|
+
assert_equal ['is invalid'], @record.errors.on(:state)
|
1265
|
+
end
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
|
1269
|
+
def setup
|
1270
|
+
@resource = new_resource
|
1271
|
+
@machine = StateMachine::Machine.new(@resource, :status, :attribute => :state)
|
1272
|
+
@machine.state :parked
|
1273
|
+
|
1274
|
+
@record = @resource.new
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
def test_should_add_validation_errors_to_custom_attribute
|
1278
|
+
@record.state = 'invalid'
|
1279
|
+
|
1280
|
+
assert !@record.valid?
|
1281
|
+
assert_equal ['is invalid'], @record.errors.on(:state)
|
1282
|
+
|
1283
|
+
@record.state = 'parked'
|
1284
|
+
assert @record.valid?
|
1285
|
+
end
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
class MachineErrorsTest < BaseTestCase
|
1289
|
+
def setup
|
1290
|
+
@resource = new_resource
|
1291
|
+
@machine = StateMachine::Machine.new(@resource)
|
1292
|
+
@record = @resource.new
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
def test_should_be_able_to_describe_current_errors
|
1296
|
+
@record.errors.add(:id, 'cannot be blank')
|
1297
|
+
@record.errors.add(:state, 'is invalid')
|
1298
|
+
assert_equal ['id cannot be blank', 'state is invalid'], @machine.errors_for(@record).split(', ').sort
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def test_should_describe_as_halted_with_no_errors
|
1302
|
+
assert_equal 'Transition halted', @machine.errors_for(@record)
|
1303
|
+
end
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
class MachineWithStateDrivenValidationsTest < BaseTestCase
|
1307
|
+
def setup
|
1308
|
+
@resource = resource = new_resource do
|
1309
|
+
attr_accessor :seatbelt
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
@machine = StateMachine::Machine.new(@resource)
|
1313
|
+
@machine.state :first_gear, :second_gear do
|
1314
|
+
if resource.respond_to?(:validates_presence_of)
|
1315
|
+
validates_presence_of :seatbelt
|
1316
|
+
else
|
1317
|
+
validates_present :seatbelt
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
@machine.other_states :parked
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
def test_should_be_valid_if_validation_fails_outside_state_scope
|
1324
|
+
record = @resource.new(:state => 'parked', :seatbelt => nil)
|
1325
|
+
assert record.valid?
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
def test_should_be_invalid_if_validation_fails_within_state_scope
|
1329
|
+
record = @resource.new(:state => 'first_gear', :seatbelt => nil)
|
1330
|
+
assert !record.valid?
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
def test_should_be_valid_if_validation_succeeds_within_state_scope
|
1334
|
+
record = @resource.new(:state => 'second_gear', :seatbelt => true)
|
1335
|
+
assert record.valid?
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
# See README caveats
|
1340
|
+
if Gem::Version.new(DataMapper::VERSION) > Gem::Version.new('0.9.6')
|
1341
|
+
class MachineWithEventAttributesOnValidationTest < BaseTestCase
|
1342
|
+
def setup
|
1343
|
+
@resource = new_resource
|
1344
|
+
@machine = StateMachine::Machine.new(@resource)
|
1345
|
+
@machine.event :ignite do
|
1346
|
+
transition :parked => :idling
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
@record = @resource.new
|
1350
|
+
@record.state = 'parked'
|
1351
|
+
@record.state_event = 'ignite'
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
def test_should_fail_if_event_is_invalid
|
1355
|
+
@record.state_event = 'invalid'
|
1356
|
+
assert !@record.valid?
|
1357
|
+
assert_equal ['is invalid'], @record.errors.full_messages
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
def test_should_fail_if_event_has_no_transition
|
1361
|
+
@record.state = 'idling'
|
1362
|
+
assert !@record.valid?
|
1363
|
+
assert_equal ['cannot transition when idling'], @record.errors.full_messages
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
def test_should_be_successful_if_event_has_transition
|
1367
|
+
assert @record.valid?
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
def test_should_run_before_callbacks
|
1371
|
+
ran_callback = false
|
1372
|
+
@machine.before_transition { ran_callback = true }
|
1373
|
+
|
1374
|
+
@record.valid?
|
1375
|
+
assert ran_callback
|
1376
|
+
end
|
1377
|
+
|
1378
|
+
def test_should_run_around_callbacks_before_yield
|
1379
|
+
ran_callback = false
|
1380
|
+
@machine.around_transition {|block| ran_callback = true; block.call }
|
1381
|
+
|
1382
|
+
begin
|
1383
|
+
@record.valid?
|
1384
|
+
rescue ArgumentError
|
1385
|
+
raise if StateMachine::Transition.pause_supported?
|
1386
|
+
end
|
1387
|
+
assert ran_callback
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def test_should_persist_new_state
|
1391
|
+
@record.valid?
|
1392
|
+
assert_equal 'idling', @record.state
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
def test_should_not_run_after_callbacks
|
1396
|
+
ran_callback = false
|
1397
|
+
@machine.after_transition { ran_callback = true }
|
1398
|
+
|
1399
|
+
@record.valid?
|
1400
|
+
assert !ran_callback
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
|
1404
|
+
@resource.class_eval do
|
1405
|
+
attr_accessor :seatbelt
|
1406
|
+
if respond_to?(:validates_presence_of)
|
1407
|
+
validates_presence_of :seatbelt
|
1408
|
+
else
|
1409
|
+
validates_present :seatbelt
|
1410
|
+
end
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
ran_callback = false
|
1414
|
+
@machine.after_transition { ran_callback = true }
|
1415
|
+
|
1416
|
+
@record.valid?
|
1417
|
+
assert !ran_callback
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
def test_should_run_failure_callbacks_if_validation_fails
|
1421
|
+
@resource.class_eval do
|
1422
|
+
attr_accessor :seatbelt
|
1423
|
+
if respond_to?(:validates_presence_of)
|
1424
|
+
validates_presence_of :seatbelt
|
1425
|
+
else
|
1426
|
+
validates_present :seatbelt
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
ran_callback = false
|
1431
|
+
@machine.after_failure { ran_callback = true }
|
1432
|
+
|
1433
|
+
@record.valid?
|
1434
|
+
assert ran_callback
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
def test_should_not_run_around_callbacks_after_yield
|
1438
|
+
ran_callback = [false]
|
1439
|
+
@machine.around_transition {|block| block.call; ran_callback[0] = true }
|
1440
|
+
|
1441
|
+
begin
|
1442
|
+
@record.valid?
|
1443
|
+
rescue ArgumentError
|
1444
|
+
raise if StateMachine::Transition.pause_supported?
|
1445
|
+
end
|
1446
|
+
assert !ran_callback[0]
|
1447
|
+
end
|
1448
|
+
|
1449
|
+
def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
|
1450
|
+
@resource.class_eval do
|
1451
|
+
attr_accessor :seatbelt
|
1452
|
+
if respond_to?(:validates_presence_of)
|
1453
|
+
validates_presence_of :seatbelt
|
1454
|
+
else
|
1455
|
+
validates_present :seatbelt
|
1456
|
+
end
|
1457
|
+
end
|
1458
|
+
|
1459
|
+
ran_callback = [false]
|
1460
|
+
@machine.around_transition {|block| block.call; ran_callback[0] = true }
|
1461
|
+
|
1462
|
+
begin
|
1463
|
+
@record.valid?
|
1464
|
+
rescue ArgumentError
|
1465
|
+
raise if StateMachine::Transition.pause_supported?
|
1466
|
+
end
|
1467
|
+
assert !ran_callback[0]
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
def test_should_not_run_before_transitions_within_transaction
|
1471
|
+
@machine.before_transition { self.class.create; throw :halt }
|
1472
|
+
|
1473
|
+
assert !@record.valid?
|
1474
|
+
assert_equal 1, @resource.all.size
|
1475
|
+
end
|
1476
|
+
end
|
1477
|
+
|
1478
|
+
class MachineWithEventAttributesOnSaveTest < BaseTestCase
|
1479
|
+
def setup
|
1480
|
+
@resource = new_resource
|
1481
|
+
@machine = StateMachine::Machine.new(@resource)
|
1482
|
+
@machine.event :ignite do
|
1483
|
+
transition :parked => :idling
|
1484
|
+
end
|
1485
|
+
|
1486
|
+
@record = @resource.new
|
1487
|
+
@record.state = 'parked'
|
1488
|
+
@record.state_event = 'ignite'
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
def test_should_fail_if_event_is_invalid
|
1492
|
+
@record.state_event = 'invalid'
|
1493
|
+
assert !@record.save
|
1494
|
+
end
|
1495
|
+
|
1496
|
+
def test_should_fail_if_event_has_no_transition
|
1497
|
+
@record.state = 'idling'
|
1498
|
+
assert !@record.save
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
def test_should_be_successful_if_event_has_transition
|
1502
|
+
assert_equal true, @record.save
|
1503
|
+
end
|
1504
|
+
|
1505
|
+
def test_should_run_before_callbacks
|
1506
|
+
ran_callback = false
|
1507
|
+
@machine.before_transition { ran_callback = true }
|
1508
|
+
|
1509
|
+
@record.save
|
1510
|
+
assert ran_callback
|
1511
|
+
end
|
1512
|
+
|
1513
|
+
def test_should_run_before_callbacks_once
|
1514
|
+
before_count = 0
|
1515
|
+
@machine.before_transition { before_count += 1 }
|
1516
|
+
|
1517
|
+
@record.save
|
1518
|
+
assert_equal 1, before_count
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
def test_should_run_around_callbacks_before_yield
|
1522
|
+
ran_callback = false
|
1523
|
+
@machine.around_transition {|block| ran_callback = true; block.call }
|
1524
|
+
|
1525
|
+
@record.save
|
1526
|
+
assert ran_callback
|
1527
|
+
end
|
1528
|
+
|
1529
|
+
def test_should_run_around_callbacks_before_yield_once
|
1530
|
+
around_before_count = 0
|
1531
|
+
@machine.around_transition {|block| around_before_count += 1; block.call }
|
1532
|
+
|
1533
|
+
@record.save
|
1534
|
+
assert_equal 1, around_before_count
|
1535
|
+
end
|
1536
|
+
|
1537
|
+
def test_should_persist_new_state
|
1538
|
+
@record.save
|
1539
|
+
assert_equal 'idling', @record.state
|
1540
|
+
end
|
1541
|
+
|
1542
|
+
def test_should_run_after_callbacks
|
1543
|
+
ran_callback = false
|
1544
|
+
@machine.after_transition { ran_callback = true }
|
1545
|
+
|
1546
|
+
@record.save
|
1547
|
+
assert ran_callback
|
1548
|
+
end
|
1549
|
+
|
1550
|
+
def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
|
1551
|
+
@resource.before(:create) { throw :halt }
|
1552
|
+
|
1553
|
+
ran_callback = false
|
1554
|
+
@machine.after_transition { ran_callback = true }
|
1555
|
+
|
1556
|
+
@record.save
|
1557
|
+
assert !ran_callback
|
1558
|
+
end
|
1559
|
+
|
1560
|
+
def test_should_run_failure_callbacks_if_fails
|
1561
|
+
@resource.before(:create) { throw :halt }
|
1562
|
+
|
1563
|
+
ran_callback = false
|
1564
|
+
@machine.after_failure { ran_callback = true }
|
1565
|
+
|
1566
|
+
@record.save
|
1567
|
+
assert ran_callback
|
1568
|
+
end
|
1569
|
+
|
1570
|
+
def test_should_not_run_around_callbacks_with_failures_disabled_if_fails
|
1571
|
+
@resource.before(:create) { throw :halt }
|
1572
|
+
|
1573
|
+
ran_callback = [false]
|
1574
|
+
@machine.around_transition {|block| block.call; ran_callback[0] = true }
|
1575
|
+
|
1576
|
+
@record.save
|
1577
|
+
assert !ran_callback[0]
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
def test_should_run_around_callbacks_after_yield
|
1581
|
+
ran_callback = [false]
|
1582
|
+
@machine.around_transition {|block| block.call; ran_callback[0] = true }
|
1583
|
+
|
1584
|
+
@record.save
|
1585
|
+
assert ran_callback[0]
|
1586
|
+
end
|
1587
|
+
|
1588
|
+
def test_should_not_run_before_transitions_within_transaction
|
1589
|
+
@machine.before_transition { self.class.create; throw :halt }
|
1590
|
+
|
1591
|
+
assert_equal false, @record.save
|
1592
|
+
assert_equal 1, @resource.all.size
|
1593
|
+
end
|
1594
|
+
|
1595
|
+
def test_should_not_run_after_transitions_within_transaction
|
1596
|
+
@machine.before_transition { self.class.create; throw :halt }
|
1597
|
+
|
1598
|
+
assert_equal false, @record.save
|
1599
|
+
assert_equal 1, @resource.all.size
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
def test_should_not_run_around_transition_within_transaction
|
1603
|
+
@machine.around_transition { self.class.create; throw :halt }
|
1604
|
+
|
1605
|
+
assert_equal false, @record.save
|
1606
|
+
assert_equal 1, @resource.all.size
|
1607
|
+
end
|
1608
|
+
|
1609
|
+
def test_should_allow_additional_transitions_to_new_state_in_after_transitions
|
1610
|
+
@machine.event :park do
|
1611
|
+
transition :idling => :parked
|
1612
|
+
end
|
1613
|
+
|
1614
|
+
@machine.after_transition(:on => :ignite) { park }
|
1615
|
+
|
1616
|
+
@record.save
|
1617
|
+
assert_equal 'parked', @record.state
|
1618
|
+
|
1619
|
+
@record.reload
|
1620
|
+
assert_equal 'parked', @record.state
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
def test_should_allow_additional_transitions_to_previous_state_in_after_transitions
|
1624
|
+
@machine.event :shift_up do
|
1625
|
+
transition :idling => :first_gear
|
1626
|
+
end
|
1627
|
+
|
1628
|
+
@machine.after_transition(:on => :ignite) { shift_up }
|
1629
|
+
|
1630
|
+
@record.save
|
1631
|
+
assert_equal 'first_gear', @record.state
|
1632
|
+
|
1633
|
+
@record.reload
|
1634
|
+
assert_equal 'first_gear', @record.state
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
end
|
1638
|
+
|
1639
|
+
if Gem::Version.new(DataMapper::VERSION) >= Gem::Version.new('0.10.0')
|
1640
|
+
class MachineWithEventAttributesOnSaveBangTest < BaseTestCase
|
1641
|
+
def setup
|
1642
|
+
@resource = new_resource
|
1643
|
+
@machine = StateMachine::Machine.new(@resource)
|
1644
|
+
@machine.event :ignite do
|
1645
|
+
transition :parked => :idling
|
1646
|
+
end
|
1647
|
+
|
1648
|
+
@record = @resource.new
|
1649
|
+
@record.state = 'parked'
|
1650
|
+
@record.state_event = 'ignite'
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
def test_should_fail_if_event_is_invalid
|
1654
|
+
@record.state_event = 'invalid'
|
1655
|
+
assert !@record.save!
|
1656
|
+
end
|
1657
|
+
|
1658
|
+
def test_should_fail_if_event_has_no_transition
|
1659
|
+
@record.state = 'idling'
|
1660
|
+
assert !@record.save!
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
def test_should_be_successful_if_event_has_transition
|
1664
|
+
assert_equal true, @record.save!
|
1665
|
+
end
|
1666
|
+
|
1667
|
+
def test_should_run_before_callbacks
|
1668
|
+
ran_callback = false
|
1669
|
+
@machine.before_transition { ran_callback = true }
|
1670
|
+
|
1671
|
+
@record.save!
|
1672
|
+
assert ran_callback
|
1673
|
+
end
|
1674
|
+
|
1675
|
+
def test_should_run_before_callbacks_once
|
1676
|
+
before_count = 0
|
1677
|
+
@machine.before_transition { before_count += 1 }
|
1678
|
+
|
1679
|
+
@record.save!
|
1680
|
+
assert_equal 1, before_count
|
1681
|
+
end
|
1682
|
+
|
1683
|
+
def test_should_run_around_callbacks_before_yield
|
1684
|
+
ran_callback = false
|
1685
|
+
@machine.around_transition {|block| ran_callback = true; block.call }
|
1686
|
+
|
1687
|
+
@record.save!
|
1688
|
+
assert ran_callback
|
1689
|
+
end
|
1690
|
+
|
1691
|
+
def test_should_run_around_callbacks_before_yield_once
|
1692
|
+
around_before_count = 0
|
1693
|
+
@machine.around_transition {|block| around_before_count += 1; block.call }
|
1694
|
+
|
1695
|
+
@record.save!
|
1696
|
+
assert_equal 1, around_before_count
|
1697
|
+
end
|
1698
|
+
|
1699
|
+
def test_should_persist_new_state
|
1700
|
+
@record.save!
|
1701
|
+
assert_equal 'idling', @record.state
|
1702
|
+
end
|
1703
|
+
|
1704
|
+
def test_should_run_after_callbacks
|
1705
|
+
ran_callback = false
|
1706
|
+
@machine.after_transition { ran_callback = true }
|
1707
|
+
|
1708
|
+
@record.save!
|
1709
|
+
assert ran_callback
|
1710
|
+
end
|
1711
|
+
|
1712
|
+
def test_should_run_around_callbacks_after_yield
|
1713
|
+
ran_callback = [false]
|
1714
|
+
@machine.around_transition {|block| block.call; ran_callback[0] = true }
|
1715
|
+
|
1716
|
+
@record.save!
|
1717
|
+
assert ran_callback[0]
|
1718
|
+
end
|
1719
|
+
end
|
1720
|
+
end
|
1721
|
+
|
1722
|
+
if Gem::Version.new(DataMapper::VERSION) > Gem::Version.new('0.9.6')
|
1723
|
+
class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
|
1724
|
+
def setup
|
1725
|
+
@superclass = new_resource do
|
1726
|
+
def persist
|
1727
|
+
save
|
1728
|
+
end
|
1729
|
+
end
|
1730
|
+
@resource = Class.new(@superclass)
|
1731
|
+
@machine = StateMachine::Machine.new(@resource, :action => :persist)
|
1732
|
+
@machine.event :ignite do
|
1733
|
+
transition :parked => :idling
|
1734
|
+
end
|
1735
|
+
|
1736
|
+
@record = @resource.new
|
1737
|
+
@record.state = 'parked'
|
1738
|
+
@record.state_event = 'ignite'
|
1739
|
+
end
|
1740
|
+
|
1741
|
+
def test_should_not_transition_on_valid?
|
1742
|
+
@record.valid?
|
1743
|
+
assert_equal 'parked', @record.state
|
1744
|
+
end
|
1745
|
+
|
1746
|
+
def test_should_not_transition_on_save
|
1747
|
+
@record.save
|
1748
|
+
assert_equal 'parked', @record.state
|
1749
|
+
end
|
1750
|
+
|
1751
|
+
def test_should_transition_on_custom_action
|
1752
|
+
@record.persist
|
1753
|
+
assert_equal 'idling', @record.state
|
1754
|
+
end
|
1755
|
+
end
|
1756
|
+
end
|
1757
|
+
rescue LoadError
|
1758
|
+
$stderr.puts "Skipping DataMapper Validation tests."
|
1759
|
+
end
|
1760
|
+
|
1761
|
+
class MachineWithObserversTest < BaseTestCase
|
1762
|
+
def setup
|
1763
|
+
@resource = new_resource
|
1764
|
+
@machine = StateMachine::Machine.new(@resource)
|
1765
|
+
@machine.state :parked, :idling
|
1766
|
+
@machine.event :ignite
|
1767
|
+
@record = @resource.new(:state => 'parked')
|
1768
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
1769
|
+
end
|
1770
|
+
|
1771
|
+
def test_should_provide_matcher_helpers
|
1772
|
+
matchers = []
|
1773
|
+
|
1774
|
+
new_observer(@resource) do
|
1775
|
+
matchers = [all, any, same]
|
1776
|
+
end
|
1777
|
+
|
1778
|
+
assert_equal [StateMachine::AllMatcher.instance, StateMachine::AllMatcher.instance, StateMachine::LoopbackMatcher.instance], matchers
|
1779
|
+
end
|
1780
|
+
|
1781
|
+
def test_should_call_before_transition_callback_if_requirements_match
|
1782
|
+
called = false
|
1783
|
+
|
1784
|
+
new_observer(@resource) do
|
1785
|
+
before_transition :from => :parked do
|
1786
|
+
called = true
|
1787
|
+
end
|
1788
|
+
end
|
1789
|
+
|
1790
|
+
@transition.perform
|
1791
|
+
assert called
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
def test_should_not_call_before_transition_callback_if_requirements_do_not_match
|
1795
|
+
called = false
|
1796
|
+
|
1797
|
+
new_observer(@resource) do
|
1798
|
+
before_transition :from => :idling do
|
1799
|
+
called = true
|
1800
|
+
end
|
1801
|
+
end
|
1802
|
+
|
1803
|
+
@transition.perform
|
1804
|
+
assert !called
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
def test_should_pass_transition_to_before_callbacks
|
1808
|
+
callback_args = nil
|
1809
|
+
|
1810
|
+
new_observer(@resource) do
|
1811
|
+
before_transition do |*args|
|
1812
|
+
callback_args = args
|
1813
|
+
end
|
1814
|
+
end
|
1815
|
+
|
1816
|
+
@transition.perform
|
1817
|
+
assert_equal [@transition], callback_args
|
1818
|
+
end
|
1819
|
+
|
1820
|
+
def test_should_call_after_transition_callback_if_requirements_match
|
1821
|
+
called = false
|
1822
|
+
|
1823
|
+
new_observer(@resource) do
|
1824
|
+
after_transition :from => :parked do
|
1825
|
+
called = true
|
1826
|
+
end
|
1827
|
+
end
|
1828
|
+
|
1829
|
+
@transition.perform
|
1830
|
+
assert called
|
1831
|
+
end
|
1832
|
+
|
1833
|
+
def test_should_not_call_after_transition_callback_if_requirements_do_not_match
|
1834
|
+
called = false
|
1835
|
+
|
1836
|
+
new_observer(@resource) do
|
1837
|
+
after_transition :from => :idling do
|
1838
|
+
called = true
|
1839
|
+
end
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
@transition.perform
|
1843
|
+
assert !called
|
1844
|
+
end
|
1845
|
+
|
1846
|
+
def test_should_pass_transition_to_after_callbacks
|
1847
|
+
callback_args = nil
|
1848
|
+
|
1849
|
+
new_observer(@resource) do
|
1850
|
+
after_transition do |*args|
|
1851
|
+
callback_args = args
|
1852
|
+
end
|
1853
|
+
end
|
1854
|
+
|
1855
|
+
@transition.perform
|
1856
|
+
assert_equal [@transition], callback_args
|
1857
|
+
end
|
1858
|
+
|
1859
|
+
def test_should_call_around_transition_callback_if_requirements_match
|
1860
|
+
called = false
|
1861
|
+
|
1862
|
+
new_observer(@resource) do
|
1863
|
+
around_transition :from => :parked do |block|
|
1864
|
+
called = true
|
1865
|
+
block.call
|
1866
|
+
end
|
1867
|
+
end
|
1868
|
+
|
1869
|
+
@transition.perform
|
1870
|
+
assert called
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
def test_should_not_call_around_transition_callback_if_requirements_do_not_match
|
1874
|
+
called = false
|
1875
|
+
|
1876
|
+
new_observer(@resource) do
|
1877
|
+
around_transition :from => :idling do |block|
|
1878
|
+
called = true
|
1879
|
+
block.call
|
1880
|
+
end
|
1881
|
+
end
|
1882
|
+
|
1883
|
+
@transition.perform
|
1884
|
+
assert !called
|
1885
|
+
end
|
1886
|
+
|
1887
|
+
def test_should_pass_transition_to_around_callbacks
|
1888
|
+
callback_args = nil
|
1889
|
+
|
1890
|
+
new_observer(@resource) do
|
1891
|
+
around_transition do |*args|
|
1892
|
+
block = args.pop
|
1893
|
+
callback_args = args
|
1894
|
+
block.call
|
1895
|
+
end
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
@transition.perform
|
1899
|
+
assert_equal [@transition], callback_args
|
1900
|
+
end
|
1901
|
+
|
1902
|
+
def test_should_call_failure_callback_if_requirements_match
|
1903
|
+
@resource.before(:create) { throw :halt }
|
1904
|
+
|
1905
|
+
called = false
|
1906
|
+
|
1907
|
+
new_observer(@resource) do
|
1908
|
+
after_transition_failure :on => :ignite do
|
1909
|
+
called = true
|
1910
|
+
end
|
1911
|
+
end
|
1912
|
+
|
1913
|
+
@transition.perform
|
1914
|
+
assert called
|
1915
|
+
end
|
1916
|
+
|
1917
|
+
def test_should_not_call_failure_callback_if_requirements_do_not_match
|
1918
|
+
@resource.before(:create) { throw :halt }
|
1919
|
+
|
1920
|
+
called = false
|
1921
|
+
|
1922
|
+
new_observer(@resource) do
|
1923
|
+
after_transition_failure :on => :park do
|
1924
|
+
called = true
|
1925
|
+
end
|
1926
|
+
end
|
1927
|
+
|
1928
|
+
@transition.perform
|
1929
|
+
assert !called
|
1930
|
+
end
|
1931
|
+
|
1932
|
+
def test_should_pass_transition_to_failure_callbacks
|
1933
|
+
@resource.before(:create) { throw :halt }
|
1934
|
+
|
1935
|
+
callback_args = nil
|
1936
|
+
|
1937
|
+
new_observer(@resource) do
|
1938
|
+
after_transition_failure do |*args|
|
1939
|
+
callback_args = args
|
1940
|
+
end
|
1941
|
+
end
|
1942
|
+
|
1943
|
+
@transition.perform
|
1944
|
+
assert_equal [@transition], callback_args
|
1945
|
+
end
|
1946
|
+
|
1947
|
+
def test_should_raise_exception_if_targeting_invalid_machine
|
1948
|
+
assert_raise(RUBY_VERSION < '1.9' ? IndexError : KeyError) do
|
1949
|
+
new_observer(@resource) do
|
1950
|
+
before_transition :invalid, :from => :parked do
|
1951
|
+
end
|
1952
|
+
end
|
1953
|
+
end
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
def test_should_allow_targeting_specific_machine
|
1957
|
+
@second_machine = StateMachine::Machine.new(@resource, :status, :namespace => 'alarm')
|
1958
|
+
@resource.auto_migrate!
|
1959
|
+
|
1960
|
+
called_state = false
|
1961
|
+
called_status = false
|
1962
|
+
|
1963
|
+
new_observer(@resource) do
|
1964
|
+
before_transition :state, :from => :parked do
|
1965
|
+
called_state = true
|
1966
|
+
end
|
1967
|
+
|
1968
|
+
before_transition :status, :from => :parked do
|
1969
|
+
called_status = true
|
1970
|
+
end
|
1971
|
+
end
|
1972
|
+
|
1973
|
+
@transition.perform
|
1974
|
+
|
1975
|
+
assert called_state
|
1976
|
+
assert !called_status
|
1977
|
+
end
|
1978
|
+
|
1979
|
+
def test_should_allow_targeting_multiple_specific_machines
|
1980
|
+
@second_machine = StateMachine::Machine.new(@resource, :status, :namespace => 'alarm')
|
1981
|
+
@second_machine.state :parked, :idling
|
1982
|
+
@second_machine.event :ignite
|
1983
|
+
@resource.auto_migrate!
|
1984
|
+
|
1985
|
+
called_attribute = nil
|
1986
|
+
|
1987
|
+
new_observer(@resource) do
|
1988
|
+
before_transition :state, :status, :from => :parked do |transition|
|
1989
|
+
called_attribute = transition.attribute
|
1990
|
+
end
|
1991
|
+
end
|
1992
|
+
|
1993
|
+
@transition.perform
|
1994
|
+
assert_equal :state, called_attribute
|
1995
|
+
|
1996
|
+
StateMachine::Transition.new(@record, @second_machine, :ignite, :parked, :idling).perform
|
1997
|
+
assert_equal :status, called_attribute
|
1998
|
+
end
|
1999
|
+
end
|
2000
|
+
|
2001
|
+
class MachineWithMixedCallbacksTest < BaseTestCase
|
2002
|
+
def setup
|
2003
|
+
@resource = new_resource
|
2004
|
+
@machine = StateMachine::Machine.new(@resource)
|
2005
|
+
@machine.state :parked, :idling
|
2006
|
+
@machine.event :ignite
|
2007
|
+
@record = @resource.new(:state => 'parked')
|
2008
|
+
@transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
|
2009
|
+
|
2010
|
+
@notifications = notifications = []
|
2011
|
+
|
2012
|
+
# Create callbacks
|
2013
|
+
@machine.before_transition {notifications << :callback_before_transition}
|
2014
|
+
@machine.after_transition {notifications << :callback_after_transition}
|
2015
|
+
@machine.around_transition do |block|
|
2016
|
+
notifications << :callback_around_before_transition
|
2017
|
+
block.call
|
2018
|
+
notifications << :callback_around_after_transition
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
new_observer(@resource) do
|
2022
|
+
before_transition do
|
2023
|
+
notifications << :observer_before_transition
|
2024
|
+
end
|
2025
|
+
|
2026
|
+
after_transition do
|
2027
|
+
notifications << :observer_after_transition
|
2028
|
+
end
|
2029
|
+
|
2030
|
+
around_transition do |block|
|
2031
|
+
notifications << :observer_around_before_transition
|
2032
|
+
block.call
|
2033
|
+
notifications << :observer_around_after_transition
|
2034
|
+
end
|
2035
|
+
end
|
2036
|
+
|
2037
|
+
@transition.perform
|
2038
|
+
end
|
2039
|
+
|
2040
|
+
def test_should_invoke_callbacks_in_specific_order
|
2041
|
+
expected = [
|
2042
|
+
:callback_before_transition,
|
2043
|
+
:callback_around_before_transition,
|
2044
|
+
:observer_before_transition,
|
2045
|
+
:observer_around_before_transition,
|
2046
|
+
:observer_around_after_transition,
|
2047
|
+
:callback_around_after_transition,
|
2048
|
+
:callback_after_transition,
|
2049
|
+
:observer_after_transition
|
2050
|
+
]
|
2051
|
+
|
2052
|
+
assert_equal expected, @notifications
|
2053
|
+
end
|
2054
|
+
end
|
2055
|
+
|
2056
|
+
class MachineWithScopesTest < BaseTestCase
|
2057
|
+
def setup
|
2058
|
+
@resource = new_resource
|
2059
|
+
@machine = StateMachine::Machine.new(@resource)
|
2060
|
+
@machine.state :parked, :first_gear
|
2061
|
+
@machine.state :idling, :value => lambda {'idling'}
|
2062
|
+
end
|
2063
|
+
|
2064
|
+
def test_should_create_singular_with_scope
|
2065
|
+
assert @resource.respond_to?(:with_state)
|
2066
|
+
end
|
2067
|
+
|
2068
|
+
def test_should_only_include_records_with_state_in_singular_with_scope
|
2069
|
+
parked = @resource.create :state => 'parked'
|
2070
|
+
@resource.create :state => 'idling'
|
2071
|
+
|
2072
|
+
assert_equal [parked], @resource.with_state(:parked)
|
2073
|
+
end
|
2074
|
+
|
2075
|
+
def test_should_create_plural_with_scope
|
2076
|
+
assert @resource.respond_to?(:with_states)
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
def test_should_only_include_records_with_states_in_plural_with_scope
|
2080
|
+
parked = @resource.create :state => 'parked'
|
2081
|
+
idling = @resource.create :state => 'idling'
|
2082
|
+
|
2083
|
+
assert_equal [parked, idling], @resource.with_states(:parked, :idling)
|
2084
|
+
end
|
2085
|
+
|
2086
|
+
def test_should_allow_lookup_by_string_name
|
2087
|
+
parked = @resource.create :state => 'parked'
|
2088
|
+
idling = @resource.create :state => 'idling'
|
2089
|
+
|
2090
|
+
assert_equal [parked, idling], @resource.with_states('parked', 'idling')
|
2091
|
+
end
|
2092
|
+
|
2093
|
+
def test_should_create_singular_without_scope
|
2094
|
+
assert @resource.respond_to?(:without_state)
|
2095
|
+
end
|
2096
|
+
|
2097
|
+
def test_should_only_include_records_without_state_in_singular_without_scope
|
2098
|
+
parked = @resource.create :state => 'parked'
|
2099
|
+
idling = @resource.create :state => 'idling'
|
2100
|
+
|
2101
|
+
assert_equal [parked], @resource.without_state(:idling)
|
2102
|
+
end
|
2103
|
+
|
2104
|
+
def test_should_create_plural_without_scope
|
2105
|
+
assert @resource.respond_to?(:without_states)
|
2106
|
+
end
|
2107
|
+
|
2108
|
+
def test_should_only_include_records_without_states_in_plural_without_scope
|
2109
|
+
parked = @resource.create :state => 'parked'
|
2110
|
+
idling = @resource.create :state => 'idling'
|
2111
|
+
first_gear = @resource.create :state => 'first_gear'
|
2112
|
+
|
2113
|
+
assert_equal [parked, idling], @resource.without_states(:first_gear)
|
2114
|
+
end
|
2115
|
+
|
2116
|
+
def test_should_allow_chaining_scopes
|
2117
|
+
parked = @resource.create :state => 'parked'
|
2118
|
+
idling = @resource.create :state => 'idling'
|
2119
|
+
|
2120
|
+
assert_equal [idling], @resource.without_state(:parked).with_state(:idling)
|
2121
|
+
end
|
2122
|
+
end
|
2123
|
+
|
2124
|
+
class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
|
2125
|
+
def setup
|
2126
|
+
@resource = new_resource
|
2127
|
+
@machine = StateMachine::Machine.new(@resource, :state)
|
2128
|
+
|
2129
|
+
@subclass = Class.new(@resource)
|
2130
|
+
DataMapperTest.const_set('Bar', @subclass)
|
2131
|
+
@resources << 'Bar'
|
2132
|
+
@subclass.auto_migrate!
|
2133
|
+
@subclass_machine = @subclass.state_machine(:state) {}
|
2134
|
+
@subclass_machine.state :parked, :idling, :first_gear
|
2135
|
+
end
|
2136
|
+
|
2137
|
+
def test_should_only_include_records_with_subclass_states_in_with_scope
|
2138
|
+
parked = @subclass.create :state => 'parked'
|
2139
|
+
idling = @subclass.create :state => 'idling'
|
2140
|
+
|
2141
|
+
assert_equal [parked, idling], @subclass.with_states(:parked, :idling)
|
2142
|
+
end
|
2143
|
+
|
2144
|
+
def test_should_only_include_records_without_subclass_states_in_without_scope
|
2145
|
+
parked = @subclass.create :state => 'parked'
|
2146
|
+
idling = @subclass.create :state => 'idling'
|
2147
|
+
first_gear = @subclass.create :state => 'first_gear'
|
2148
|
+
|
2149
|
+
assert_equal [parked, idling], @subclass.without_states(:first_gear)
|
2150
|
+
end
|
2151
|
+
end
|
2152
|
+
|
2153
|
+
class MachineWithComplexPluralizationScopesTest < BaseTestCase
|
2154
|
+
def setup
|
2155
|
+
@resource = new_resource
|
2156
|
+
@machine = StateMachine::Machine.new(@resource, :status)
|
2157
|
+
end
|
2158
|
+
|
2159
|
+
def test_should_create_singular_with_scope
|
2160
|
+
assert @resource.respond_to?(:with_status)
|
2161
|
+
end
|
2162
|
+
|
2163
|
+
def test_should_create_plural_with_scope
|
2164
|
+
assert @resource.respond_to?(:with_statuses)
|
2165
|
+
end
|
2166
|
+
end
|
2167
|
+
|
2168
|
+
class MachineWithScopesAndJoinsTest < BaseTestCase
|
2169
|
+
def setup
|
2170
|
+
@company = new_resource(:company)
|
2171
|
+
|
2172
|
+
@vehicle = new_resource(:vehicle) do
|
2173
|
+
property :company_id, Integer
|
2174
|
+
|
2175
|
+
belongs_to :company
|
2176
|
+
end
|
2177
|
+
|
2178
|
+
@company_machine = StateMachine::Machine.new(@company, :initial => :active)
|
2179
|
+
@vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
|
2180
|
+
@vehicle_machine.state :idling
|
2181
|
+
|
2182
|
+
@ford = @company.create
|
2183
|
+
@mustang = @vehicle.create(:company => @ford)
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
def test_should_find_records_in_with_scope
|
2187
|
+
assert_equal [@mustang], @vehicle.with_states(:parked).all(Vehicle.company.state => 'active')
|
2188
|
+
end
|
2189
|
+
|
2190
|
+
def test_should_find_records_in_without_scope
|
2191
|
+
assert_equal [@mustang], @vehicle.without_states(:idling).all(Vehicle.company.state => 'active')
|
2192
|
+
end
|
2193
|
+
end
|
2194
|
+
end
|