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