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,1896 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../test_helper')
2
+
3
+ require 'sequel'
4
+ require 'logger'
5
+ require 'stringio'
6
+
7
+ # Establish database connection
8
+ DB = Sequel.connect(RUBY_PLATFORM == 'java' ? 'jdbc:sqlite::memory:' : 'sqlite:///', :loggers => [Logger.new("#{File.dirname(__FILE__)}/../../sequel.log")])
9
+
10
+ module SequelTest
11
+ class BaseTestCase < Test::Unit::TestCase
12
+ def default_test
13
+ end
14
+
15
+ protected
16
+ # Creates a new Sequel model (and the associated table)
17
+ def new_model(create_table = :foo, &block)
18
+ name = create_table || :foo
19
+ table_name = "#{name}_#{rand(1000000)}"
20
+ table_identifier = Sequel::SQL::Identifier.new(table_name)
21
+
22
+ if !defined?(Sequel::VERSION) || Gem::Version.new(Sequel::VERSION) <= Gem::Version.new('3.26.0')
23
+ class << table_identifier
24
+ alias_method :original_to_s, :to_s
25
+ def to_s(*args); args.empty? ? inspect : original_to_s(*args); end
26
+ end
27
+ end
28
+
29
+ DB.create_table!(table_identifier) do
30
+ primary_key :id
31
+ column :state, :string
32
+ end if create_table
33
+ model = Class.new(Sequel::Model) do
34
+ self.raise_on_save_failure = false
35
+ (class << self; self; end).class_eval do
36
+ define_method(:name) { "SequelTest::#{name.to_s.capitalize}" }
37
+ define_method(:table_identifier) { table_identifier }
38
+ end
39
+
40
+ set_dataset(DB[table_identifier])
41
+ end
42
+ model.set_dataset(DB[table_identifier])
43
+ model.class_eval(&block) if block_given?
44
+ model
45
+ end
46
+ end
47
+
48
+ class IntegrationTest < BaseTestCase
49
+ def test_should_have_an_integration_name
50
+ assert_equal :sequel, StateMachine::Integrations::Sequel.integration_name
51
+ end
52
+
53
+ def test_should_be_available
54
+ assert StateMachine::Integrations::Sequel.available?
55
+ end
56
+
57
+ def test_should_match_if_class_inherits_from_sequel
58
+ assert StateMachine::Integrations::Sequel.matches?(new_model)
59
+ end
60
+
61
+ def test_should_not_match_if_class_does_not_inherit_from_sequel
62
+ assert !StateMachine::Integrations::Sequel.matches?(Class.new)
63
+ end
64
+
65
+ def test_should_have_defaults
66
+ assert_equal({:action => :save}, StateMachine::Integrations::Sequel.defaults)
67
+ end
68
+
69
+ def test_should_not_have_a_locale_path
70
+ assert_nil StateMachine::Integrations::Sequel.locale_path
71
+ end
72
+ end
73
+
74
+ class MachineWithoutDatabaseTest < BaseTestCase
75
+ def setup
76
+ @model = new_model(false)
77
+ end
78
+
79
+ def test_should_allow_machine_creation
80
+ assert_nothing_raised { StateMachine::Machine.new(@model) }
81
+ end
82
+ end
83
+
84
+ class MachineUnmigratedTest < BaseTestCase
85
+ def setup
86
+ @model = new_model(false)
87
+ end
88
+
89
+ def test_should_allow_machine_creation
90
+ assert_nothing_raised { StateMachine::Machine.new(@model) }
91
+ end
92
+ end
93
+
94
+ class MachineByDefaultTest < BaseTestCase
95
+ def setup
96
+ @model = new_model
97
+ @machine = StateMachine::Machine.new(@model)
98
+ end
99
+
100
+ def test_should_use_save_as_action
101
+ assert_equal :save, @machine.action
102
+ end
103
+
104
+ def test_should_use_transactions
105
+ assert_equal true, @machine.use_transactions
106
+ end
107
+
108
+ def test_should_not_have_any_before_callbacks
109
+ assert_equal 0, @machine.callbacks[:before].size
110
+ end
111
+
112
+ def test_should_not_have_any_after_callbacks
113
+ assert_equal 0, @machine.callbacks[:after].size
114
+ end
115
+ end
116
+
117
+ class MachineWithStatesTest < BaseTestCase
118
+ def setup
119
+ @model = new_model
120
+ @machine = StateMachine::Machine.new(@model)
121
+ @machine.state :first_gear
122
+ end
123
+
124
+ def test_should_humanize_name
125
+ assert_equal 'first gear', @machine.state(:first_gear).human_name
126
+ end
127
+ end
128
+
129
+ class MachineWithStaticInitialStateTest < BaseTestCase
130
+ def setup
131
+ @model = new_model(:vehicle) do
132
+ attr_accessor :value
133
+ end
134
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
135
+ end
136
+
137
+ def test_should_set_initial_state_on_created_object
138
+ record = @model.new
139
+ assert_equal 'parked', record.state
140
+ end
141
+
142
+ def test_should_still_set_attributes
143
+ record = @model.new(:value => 1)
144
+ assert_equal 1, record.value
145
+ end
146
+
147
+ def test_should_still_allow_initialize_blocks
148
+ block_args = nil
149
+ record = @model.new do |*args|
150
+ block_args = args
151
+ end
152
+
153
+ assert_equal [record], block_args
154
+ end
155
+
156
+ def test_should_set_attributes_prior_to_initialize_block
157
+ state = nil
158
+ @model.new do |record|
159
+ state = record.state
160
+ end
161
+
162
+ assert_equal 'parked', state
163
+ end
164
+
165
+ def test_should_set_attributes_prior_to_after_initialize_hook
166
+ state = nil
167
+ @model.class_eval do
168
+ define_method(:after_initialize) do
169
+ state = self.state
170
+ end
171
+ end
172
+ @model.new
173
+ assert_equal 'parked', state
174
+ end
175
+
176
+ def test_should_set_initial_state_before_setting_attributes
177
+ @model.class_eval do
178
+ attr_accessor :state_during_setter
179
+
180
+ remove_method :value=
181
+ define_method(:value=) do |value|
182
+ self.state_during_setter = state
183
+ end
184
+ end
185
+
186
+ record = @model.new(:value => 1)
187
+ assert_equal 'parked', record.state_during_setter
188
+ end
189
+
190
+ def test_should_not_set_initial_state_after_already_initialized
191
+ record = @model.new(:value => 1)
192
+ assert_equal 'parked', record.state
193
+
194
+ record.state = 'idling'
195
+ record.set({})
196
+ assert_equal 'idling', record.state
197
+ end
198
+
199
+ def test_should_persist_initial_state
200
+ record = @model.new
201
+ record.save
202
+ record.reload
203
+ assert_equal 'parked', record.state
204
+ end
205
+
206
+ def test_should_persist_initial_state_on_dup
207
+ record = @model.create.dup
208
+ record.save
209
+ record.reload
210
+ assert_equal 'parked', record.state
211
+ end
212
+
213
+ def test_should_use_stored_values_when_loading_from_database
214
+ @machine.state :idling
215
+
216
+ record = @model[@model.create(:state => 'idling').id]
217
+ assert_equal 'idling', record.state
218
+ end
219
+
220
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
221
+ @machine.state nil
222
+
223
+ record = @model[@model.create(:state => nil).id]
224
+ assert_nil record.state
225
+ end
226
+
227
+ def test_should_use_stored_values_when_loading_for_many_association
228
+ @machine.state :idling
229
+
230
+ DB.alter_table(@model.table_identifier) do
231
+ add_column :owner_id, :integer
232
+ end
233
+ @model.class_eval { get_db_schema(true) }
234
+ SequelTest.const_set('Vehicle', @model)
235
+
236
+ owner_model = new_model(:owner) do
237
+ one_to_many :vehicles, :class_name => 'SequelTest::Vehicle'
238
+ end
239
+ SequelTest.const_set('Owner', owner_model)
240
+
241
+ owner = owner_model.create
242
+ record = @model.create(:state => 'idling', :owner_id => owner.id)
243
+ assert_equal 'idling', owner.vehicles[0].state
244
+ end
245
+
246
+ if defined?(Sequel::VERSION) && Gem::Version.new(Sequel::VERSION) >= Gem::Version.new('3.10.0')
247
+ def test_should_use_stored_values_when_loading_for_one_association
248
+ @machine.state :idling
249
+
250
+ DB.alter_table(@model.table_identifier) do
251
+ add_column :owner_id, :integer
252
+ end
253
+ @model.class_eval { get_db_schema(true) }
254
+ SequelTest.const_set('Vehicle', @model)
255
+
256
+ owner_model = new_model(:owner) do
257
+ one_to_one :vehicle, :class_name => 'SequelTest::Vehicle'
258
+ end
259
+ SequelTest.const_set('Owner', owner_model)
260
+
261
+ owner = owner_model.create
262
+ record = @model.create(:state => 'idling', :owner_id => owner.id)
263
+ owner.reload
264
+ assert_equal 'idling', owner.vehicle.state
265
+ end
266
+ end
267
+
268
+ def test_should_use_stored_values_when_loading_for_belongs_to_association
269
+ @machine.state :idling
270
+
271
+ SequelTest.const_set('Vehicle', @model)
272
+
273
+ driver_model = new_model(:driver) do
274
+ DB.alter_table(table_identifier) do
275
+ add_column :vehicle_id, :integer
276
+ end
277
+ get_db_schema(true)
278
+
279
+ many_to_one :vehicle, :class_name => 'SequelTest::Vehicle'
280
+ end
281
+
282
+ SequelTest.const_set('Driver', driver_model)
283
+
284
+ record = @model.create(:state => 'idling')
285
+ driver = driver_model.create(:vehicle_id => record.id)
286
+ assert_equal 'idling', driver.vehicle.state
287
+ end
288
+
289
+ def teardown
290
+ SequelTest.class_eval do
291
+ remove_const('Vehicle') if defined?(SequelTest::Vehicle)
292
+ remove_const('Owner') if defined?(SequelTest::Owner)
293
+ remove_const('Driver') if defined?(SequelTest::Driver)
294
+ end
295
+ super
296
+ end
297
+ end
298
+
299
+ class MachineWithDynamicInitialStateTest < BaseTestCase
300
+ def setup
301
+ @model = new_model do
302
+ attr_accessor :value
303
+ end
304
+ @machine = StateMachine::Machine.new(@model, :initial => lambda {|object| :parked})
305
+ @machine.state :parked
306
+ end
307
+
308
+ def test_should_set_initial_state_on_created_object
309
+ record = @model.new
310
+ assert_equal 'parked', record.state
311
+ end
312
+
313
+ def test_should_still_set_attributes
314
+ record = @model.new(:value => 1)
315
+ assert_equal 1, record.value
316
+ end
317
+
318
+ def test_should_still_allow_initialize_blocks
319
+ block_args = nil
320
+ record = @model.new do |*args|
321
+ block_args = args
322
+ end
323
+
324
+ assert_equal [record], block_args
325
+ end
326
+
327
+ def test_should_not_have_any_changed_columns
328
+ record = @model.new
329
+ assert record.changed_columns.empty?
330
+ end
331
+
332
+ def test_should_set_attributes_prior_to_initialize_block
333
+ state = nil
334
+ @model.new do |record|
335
+ state = record.state
336
+ end
337
+
338
+ assert_equal 'parked', state
339
+ end
340
+
341
+ def test_should_set_attributes_prior_to_after_initialize_hook
342
+ state = nil
343
+ @model.class_eval do
344
+ define_method(:after_initialize) do
345
+ state = self.state
346
+ end
347
+ end
348
+ @model.new
349
+ assert_equal 'parked', state
350
+ end
351
+
352
+ def test_should_set_initial_state_after_setting_attributes
353
+ @model.class_eval do
354
+ attr_accessor :state_during_setter
355
+
356
+ remove_method :value=
357
+ define_method(:value=) do |value|
358
+ self.state_during_setter = state || 'nil'
359
+ end
360
+ end
361
+
362
+ record = @model.new(:value => 1)
363
+ assert_equal 'nil', record.state_during_setter
364
+ end
365
+
366
+ def test_should_not_set_initial_state_after_already_initialized
367
+ record = @model.new(:value => 1)
368
+ assert_equal 'parked', record.state
369
+
370
+ record.state = 'idling'
371
+ record.set({})
372
+ assert_equal 'idling', record.state
373
+ end
374
+
375
+ def test_should_persist_initial_state
376
+ record = @model.new
377
+ record.save
378
+ record.reload
379
+ assert_equal 'parked', record.state
380
+ end
381
+
382
+ def test_should_persist_initial_state_on_dup
383
+ record = @model.create.dup
384
+ record.save
385
+ record.reload
386
+ assert_equal 'parked', record.state
387
+ end
388
+
389
+ def test_should_use_stored_values_when_loading_from_database
390
+ @machine.state :idling
391
+
392
+ record = @model[@model.create(:state => 'idling').id]
393
+ assert_equal 'idling', record.state
394
+ end
395
+
396
+ def test_should_use_stored_values_when_loading_from_database_with_nil_state
397
+ @machine.state nil
398
+
399
+ record = @model[@model.create(:state => nil).id]
400
+ assert_nil record.state
401
+ end
402
+ end
403
+
404
+ class MachineWithEventsTest < BaseTestCase
405
+ def setup
406
+ @model = new_model
407
+ @machine = StateMachine::Machine.new(@model)
408
+ @machine.event :shift_up
409
+ end
410
+
411
+ def test_should_humanize_name
412
+ assert_equal 'shift up', @machine.event(:shift_up).human_name
413
+ end
414
+ end
415
+
416
+ class MachineWithSameColumnDefaultTest < BaseTestCase
417
+ def setup
418
+ @original_stderr, $stderr = $stderr, StringIO.new
419
+
420
+ @model = new_model(false)
421
+ DB.create_table!(@model.table_identifier) do
422
+ primary_key :id
423
+ column :status, :string, :default => 'parked'
424
+ end
425
+ @model.class_eval do
426
+ set_dataset(DB[table_identifier])
427
+ get_db_schema(true)
428
+ end
429
+
430
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
431
+ @record = @model.new
432
+ end
433
+
434
+ def test_should_use_machine_default
435
+ assert_equal 'parked', @record.status
436
+ end
437
+
438
+ def test_should_not_generate_a_warning
439
+ assert_no_match(/have defined a different default/, $stderr.string)
440
+ end
441
+
442
+ def teardown
443
+ $stderr = @original_stderr
444
+ end
445
+ end
446
+
447
+ class MachineWithDifferentColumnDefaultTest < BaseTestCase
448
+ def setup
449
+ @original_stderr, $stderr = $stderr, StringIO.new
450
+
451
+ @model = new_model(false)
452
+ DB.create_table!(@model.table_identifier) do
453
+ primary_key :id
454
+ column :status, :string, :default => 'idling'
455
+ end
456
+ @model.class_eval do
457
+ set_dataset(DB[table_identifier])
458
+ get_db_schema(true)
459
+ end
460
+
461
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
462
+ @record = @model.new
463
+ end
464
+
465
+ def test_should_use_machine_default
466
+ assert_equal 'parked', @record.status
467
+ end
468
+
469
+ def test_should_generate_a_warning
470
+ assert_match(/Both SequelTest::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)
471
+ end
472
+
473
+ def teardown
474
+ $stderr = @original_stderr
475
+ end
476
+ end
477
+
478
+ class MachineWithDifferentIntegerColumnDefaultTest < BaseTestCase
479
+ def setup
480
+ @original_stderr, $stderr = $stderr, StringIO.new
481
+
482
+ @model = new_model(false)
483
+ DB.create_table!(@model.table_identifier) do
484
+ primary_key :id
485
+ column :status, :integer, :default => 0
486
+ end
487
+ @model.class_eval do
488
+ set_dataset(DB[table_identifier])
489
+ get_db_schema(true)
490
+ end
491
+
492
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
493
+ @machine.state :parked, :value => 1
494
+ @record = @model.new
495
+ end
496
+
497
+ def test_should_use_machine_default
498
+ assert_equal 1, @record.status
499
+ end
500
+
501
+ def test_should_generate_a_warning
502
+ assert_match(/Both SequelTest::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)
503
+ end
504
+
505
+ def teardown
506
+ $stderr = @original_stderr
507
+ end
508
+ end
509
+
510
+ class MachineWithConflictingPredicateTest < BaseTestCase
511
+ def setup
512
+ @model = new_model do
513
+ def state?(*args)
514
+ true
515
+ end
516
+ end
517
+
518
+ @machine = StateMachine::Machine.new(@model)
519
+ @record = @model.new
520
+ end
521
+
522
+ def test_should_not_define_attribute_predicate
523
+ assert @record.state?
524
+ end
525
+ end
526
+
527
+ class MachineWithColumnStateAttributeTest < BaseTestCase
528
+ def setup
529
+ @model = new_model
530
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
531
+ @machine.other_states(:idling)
532
+
533
+ @record = @model.new
534
+ end
535
+
536
+ def test_should_not_override_the_column_reader
537
+ record = @model.new
538
+ record[:state] = 'parked'
539
+ assert_equal 'parked', record.state
540
+ end
541
+
542
+ def test_should_not_override_the_column_writer
543
+ record = @model.new
544
+ record.state = 'parked'
545
+ assert_equal 'parked', record[:state]
546
+ end
547
+
548
+ def test_should_have_an_attribute_predicate
549
+ assert @record.respond_to?(:state?)
550
+ end
551
+
552
+ def test_should_raise_exception_for_predicate_without_parameters
553
+ assert_raise(ArgumentError) { @record.state? }
554
+ end
555
+
556
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
557
+ assert !@record.state?(:idling)
558
+ end
559
+
560
+ def test_should_return_true_for_predicate_if_matches_current_value
561
+ assert @record.state?(:parked)
562
+ end
563
+
564
+ def test_should_raise_exception_for_predicate_if_invalid_state_specified
565
+ assert_raise(IndexError) { @record.state?(:invalid) }
566
+ end
567
+ end
568
+
569
+ class MachineWithNonColumnStateAttributeUndefinedTest < BaseTestCase
570
+ def setup
571
+ @model = new_model do
572
+ # Prevent attempts to access the status field
573
+ def method_missing(method, *args)
574
+ super unless %w(status status=).include?(method.to_s)
575
+ end
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_not_define_a_reader_attribute_for_the_attribute
584
+ assert !@record.respond_to?(:status)
585
+ end
586
+
587
+ def test_should_not_define_a_writer_attribute_for_the_attribute
588
+ assert !@record.respond_to?(:status=)
589
+ end
590
+
591
+ def test_should_define_an_attribute_predicate
592
+ assert @record.respond_to?(:status?)
593
+ end
594
+ end
595
+
596
+ class MachineWithNonColumnStateAttributeDefinedTest < BaseTestCase
597
+ def setup
598
+ @model = new_model do
599
+ attr_accessor :status
600
+ end
601
+
602
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
603
+ @machine.other_states(:idling)
604
+ @record = @model.new
605
+ end
606
+
607
+ def test_should_return_false_for_predicate_if_does_not_match_current_value
608
+ assert !@record.status?(:idling)
609
+ end
610
+
611
+ def test_should_return_true_for_predicate_if_matches_current_value
612
+ assert @record.status?(:parked)
613
+ end
614
+
615
+ def test_should_set_initial_state_on_created_object
616
+ assert_equal 'parked', @record.status
617
+ end
618
+ end
619
+
620
+ class MachineWithInitializedStateTest < BaseTestCase
621
+ def setup
622
+ @model = new_model
623
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
624
+ @machine.state :idling
625
+ end
626
+
627
+ def test_should_allow_nil_initial_state_when_static
628
+ @machine.state nil
629
+
630
+ record = @model.new(:state => nil)
631
+ assert_nil record.state
632
+ end
633
+
634
+ def test_should_allow_nil_initial_state_when_dynamic
635
+ @machine.state nil
636
+
637
+ @machine.initial_state = lambda {:parked}
638
+ record = @model.new(:state => nil)
639
+ assert_nil record.state
640
+ end
641
+
642
+ def test_should_allow_different_initial_state_when_static
643
+ record = @model.new(:state => 'idling')
644
+ assert_equal 'idling', record.state
645
+ end
646
+
647
+ def test_should_allow_different_initial_state_when_dynamic
648
+ @machine.initial_state = lambda {:parked}
649
+ record = @model.new(:state => 'idling')
650
+ assert_equal 'idling', record.state
651
+ end
652
+
653
+ def test_should_use_default_state_if_protected
654
+ @model.class_eval do
655
+ self.strict_param_setting = false
656
+ set_restricted_columns :state
657
+ end
658
+
659
+ record = @model.new(:state => 'idling')
660
+ assert_equal 'parked', record.state
661
+ end
662
+ end
663
+
664
+ class MachineMultipleTest < BaseTestCase
665
+ def setup
666
+ @model = new_model
667
+ DB.alter_table(@model.table_identifier) do
668
+ add_column :status, :string
669
+ end
670
+ @model.class_eval { get_db_schema(true) }
671
+
672
+ @state_machine = StateMachine::Machine.new(@model, :initial => :parked)
673
+ @status_machine = StateMachine::Machine.new(@model, :status, :initial => :idling)
674
+ end
675
+
676
+ def test_should_should_initialize_each_state
677
+ record = @model.new
678
+ assert_equal 'parked', record.state
679
+ assert_equal 'idling', record.status
680
+ end
681
+ end
682
+
683
+ class MachineWithAliasedAttributeTest < BaseTestCase
684
+ def setup
685
+ @model = new_model do
686
+ alias_method :vehicle_status, :state
687
+ alias_method :vehicle_status=, :state=
688
+ end
689
+
690
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :vehicle_status)
691
+ @machine.state :parked
692
+
693
+ @record = @model.new
694
+ end
695
+
696
+ def test_should_add_validation_errors_to_custom_attribute
697
+ @record.vehicle_status = 'invalid'
698
+
699
+ assert !@record.valid?
700
+ assert_equal ['is invalid'], @record.errors.on(:vehicle_status)
701
+
702
+ @record.vehicle_status = 'parked'
703
+ assert @record.valid?
704
+ end
705
+ end
706
+
707
+ class MachineWithLoopbackTest < BaseTestCase
708
+ def setup
709
+ @model = new_model do
710
+ # Simulate timestamps plugin
711
+ define_method(:before_update) do
712
+ changed_columns = self.changed_columns.dup
713
+
714
+ super()
715
+ self.updated_at = Time.now if changed_columns.any?
716
+ end
717
+ end
718
+
719
+ DB.alter_table(@model.table_identifier) do
720
+ add_column :updated_at, :datetime
721
+ end
722
+ @model.class_eval { get_db_schema(true) }
723
+
724
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
725
+ @machine.event :park
726
+
727
+ @record = @model.create(:updated_at => Time.now - 1)
728
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
729
+
730
+ @timestamp = @record.updated_at
731
+ @transition.perform
732
+ end
733
+
734
+ def test_should_update_record
735
+ assert_not_equal @timestamp, @record.updated_at
736
+ end
737
+ end
738
+
739
+ class MachineWithDirtyAttributesTest < BaseTestCase
740
+ def setup
741
+ @model = new_model
742
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
743
+ @machine.event :ignite
744
+ @machine.state :idling
745
+
746
+ @record = @model.create
747
+
748
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
749
+ @transition.perform(false)
750
+ end
751
+
752
+ def test_should_include_state_in_changed_attributes
753
+ assert_equal [:state], @record.changed_columns
754
+ end
755
+
756
+ def test_should_not_have_changes_when_loaded_from_database
757
+ record = @model[@record.id]
758
+ assert record.changed_columns.empty?
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_include_state_in_changed_attributes
775
+ assert_equal [:state], @record.changed_columns
776
+ end
777
+ end
778
+
779
+ class MachineWithDirtyAttributesAndCustomAttributeTest < BaseTestCase
780
+ def setup
781
+ @model = new_model
782
+ DB.alter_table(@model.table_identifier) do
783
+ add_column :status, :string
784
+ end
785
+ @model.class_eval { get_db_schema(true) }
786
+
787
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
788
+ @machine.event :ignite
789
+ @machine.state :idling
790
+
791
+ @record = @model.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 [:status], @record.changed_columns
799
+ end
800
+ end
801
+
802
+ class MachineWithDirtyAttributeAndCustomAttributesDuringLoopbackTest < BaseTestCase
803
+ def setup
804
+ @model = new_model
805
+ DB.alter_table(@model.table_identifier) do
806
+ add_column :status, :string
807
+ end
808
+ @model.class_eval { get_db_schema(true) }
809
+
810
+ @machine = StateMachine::Machine.new(@model, :status, :initial => :parked)
811
+ @machine.event :park
812
+
813
+ @record = @model.create
814
+
815
+ @transition = StateMachine::Transition.new(@record, @machine, :park, :parked, :parked)
816
+ @transition.perform(false)
817
+ end
818
+
819
+ def test_should_include_state_in_changed_attributes
820
+ assert_equal [:status], @record.changed_columns
821
+ end
822
+ end
823
+
824
+ class MachineWithDirtyAttributeAndStateEventsTest < BaseTestCase
825
+ def setup
826
+ @model = new_model
827
+ @machine = StateMachine::Machine.new(@model, :initial => :parked)
828
+ @machine.event :ignite
829
+
830
+ @record = @model.create
831
+ @record.state_event = 'ignite'
832
+ end
833
+
834
+ def test_should_include_state_in_changed_attributes
835
+ assert_equal [:state], @record.changed_columns
836
+ end
837
+
838
+ def test_should_not_include_state_in_changed_attributes_if_nil
839
+ @record = @model.create
840
+ @record.state_event = nil
841
+
842
+ assert_equal [], @record.changed_columns
843
+ end
844
+ end
845
+
846
+ class MachineWithoutTransactionsTest < BaseTestCase
847
+ def setup
848
+ @model = new_model
849
+ @machine = StateMachine::Machine.new(@model, :use_transactions => false)
850
+ end
851
+
852
+ def test_should_not_rollback_transaction_if_false
853
+ @machine.within_transaction(@model.new) do
854
+ @model.create
855
+ false
856
+ end
857
+
858
+ assert_equal 1, @model.count
859
+ end
860
+
861
+ def test_should_not_rollback_transaction_if_true
862
+ @machine.within_transaction(@model.new) do
863
+ @model.create
864
+ true
865
+ end
866
+
867
+ assert_equal 1, @model.count
868
+ end
869
+ end
870
+
871
+ class MachineWithTransactionsTest < BaseTestCase
872
+ def setup
873
+ @model = new_model
874
+ @machine = StateMachine::Machine.new(@model, :use_transactions => true)
875
+ end
876
+
877
+ def test_should_rollback_transaction_if_false
878
+ @machine.within_transaction(@model.new) do
879
+ @model.create
880
+ false
881
+ end
882
+
883
+ assert_equal 0, @model.count
884
+ end
885
+
886
+ def test_should_not_rollback_transaction_if_true
887
+ @machine.within_transaction(@model.new) do
888
+ @model.create
889
+ true
890
+ end
891
+
892
+ assert_equal 1, @model.count
893
+ end
894
+ end
895
+
896
+ class MachineWithCallbacksTest < BaseTestCase
897
+ def setup
898
+ @model = new_model
899
+ @machine = StateMachine::Machine.new(@model)
900
+ @machine.state :parked, :idling
901
+ @machine.event :ignite
902
+
903
+ @record = @model.new(:state => 'parked')
904
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
905
+ end
906
+
907
+ def test_should_run_before_callbacks
908
+ called = false
909
+ @machine.before_transition {called = true}
910
+
911
+ @transition.perform
912
+ assert called
913
+ end
914
+
915
+ def test_should_pass_transition_to_before_callbacks_with_one_argument
916
+ transition = nil
917
+ @machine.before_transition {|arg| transition = arg}
918
+
919
+ @transition.perform
920
+ assert_equal @transition, transition
921
+ end
922
+
923
+ def test_should_pass_transition_to_before_callbacks_with_multiple_arguments
924
+ callback_args = nil
925
+ @machine.before_transition {|*args| callback_args = args}
926
+
927
+ @transition.perform
928
+ assert_equal [@transition], callback_args
929
+ end
930
+
931
+ def test_should_run_before_callbacks_within_the_context_of_the_record
932
+ context = nil
933
+ @machine.before_transition {context = self}
934
+
935
+ @transition.perform
936
+ assert_equal @record, context
937
+ end
938
+
939
+ def test_should_run_after_callbacks
940
+ called = false
941
+ @machine.after_transition {called = true}
942
+
943
+ @transition.perform
944
+ assert called
945
+ end
946
+
947
+ def test_should_pass_transition_to_after_callbacks_with_multiple_arguments
948
+ callback_args = nil
949
+ @machine.after_transition {|*args| callback_args = args}
950
+
951
+ @transition.perform
952
+ assert_equal [@transition], callback_args
953
+ end
954
+
955
+ def test_should_run_after_callbacks_with_the_context_of_the_record
956
+ context = nil
957
+ @machine.after_transition {context = self}
958
+
959
+ @transition.perform
960
+ assert_equal @record, context
961
+ end
962
+
963
+ def test_should_run_around_callbacks
964
+ before_called = false
965
+ after_called = false
966
+ ensure_called = 0
967
+ @machine.around_transition do |block|
968
+ before_called = true
969
+ begin
970
+ block.call
971
+ ensure
972
+ ensure_called += 1
973
+ end
974
+ after_called = true
975
+ end
976
+
977
+ @transition.perform
978
+ assert before_called
979
+ assert after_called
980
+ assert_equal ensure_called, 1
981
+ end
982
+
983
+ def test_should_run_around_callbacks_with_the_context_of_the_record
984
+ context = nil
985
+ @machine.around_transition {|block| context = self; block.call}
986
+
987
+ @transition.perform
988
+ assert_equal @record, context
989
+ end
990
+
991
+ def test_should_allow_symbolic_callbacks
992
+ callback_args = nil
993
+
994
+ klass = class << @record; self; end
995
+ klass.send(:define_method, :after_ignite) do |*args|
996
+ callback_args = args
997
+ end
998
+
999
+ @machine.before_transition(:after_ignite)
1000
+
1001
+ @transition.perform
1002
+ assert_equal [@transition], callback_args
1003
+ end
1004
+
1005
+ def test_should_allow_string_callbacks
1006
+ class << @record
1007
+ attr_reader :callback_result
1008
+ end
1009
+
1010
+ @machine.before_transition('@callback_result = [1, 2, 3]')
1011
+ @transition.perform
1012
+
1013
+ assert_equal [1, 2, 3], @record.callback_result
1014
+ end
1015
+
1016
+ def test_should_run_in_expected_order
1017
+ expected = [
1018
+ :before_transition, :before_validation, :after_validation,
1019
+ :before_save, :before_create, :after_create, :after_save,
1020
+ :after_transition
1021
+ ]
1022
+
1023
+ callbacks = []
1024
+ @model.before_validation { callbacks << :before_validation }
1025
+ @model.after_validation { callbacks << :after_validation }
1026
+ @model.before_save { callbacks << :before_save }
1027
+ @model.before_create { callbacks << :before_create }
1028
+ @model.after_create { callbacks << :after_create }
1029
+ @model.after_save { callbacks << :after_save }
1030
+ if @model.respond_to?(:after_commit)
1031
+ @model.after_commit { callbacks << :after_commit }
1032
+ expected << :after_commit
1033
+ end
1034
+
1035
+ @machine.before_transition { callbacks << :before_transition }
1036
+ @machine.after_transition { callbacks << :after_transition }
1037
+
1038
+ @transition.perform
1039
+
1040
+ assert_equal expected, callbacks
1041
+ end
1042
+ end
1043
+
1044
+ class MachineWithFailedBeforeCallbacksTest < BaseTestCase
1045
+ def setup
1046
+ callbacks = []
1047
+
1048
+ @model = new_model
1049
+ @machine = StateMachine::Machine.new(@model)
1050
+ @machine.state :parked, :idling
1051
+ @machine.event :ignite
1052
+ @machine.before_transition {callbacks << :before_1; false}
1053
+ @machine.before_transition {callbacks << :before_2}
1054
+ @machine.after_transition {callbacks << :after}
1055
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
1056
+
1057
+ @record = @model.new(:state => 'parked')
1058
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1059
+ @result = @transition.perform
1060
+
1061
+ @callbacks = callbacks
1062
+ end
1063
+
1064
+ def test_should_not_be_successful
1065
+ assert !@result
1066
+ end
1067
+
1068
+ def test_should_not_change_current_state
1069
+ assert_equal 'parked', @record.state
1070
+ end
1071
+
1072
+ def test_should_not_run_action
1073
+ assert @record.new?
1074
+ end
1075
+
1076
+ def test_should_not_run_further_callbacks
1077
+ assert_equal [:before_1], @callbacks
1078
+ end
1079
+ end
1080
+
1081
+ class MachineNestedActionTest < BaseTestCase
1082
+ def setup
1083
+ @callbacks = []
1084
+
1085
+ @model = new_model
1086
+ @machine = StateMachine::Machine.new(@model)
1087
+ @machine.event :ignite do
1088
+ transition :parked => :idling
1089
+ end
1090
+
1091
+ @record = @model.new(:state => 'parked')
1092
+ end
1093
+
1094
+ def test_should_allow_transition_prior_to_creation_if_skipping_action
1095
+ record = @record
1096
+ @model.before_create { record.ignite(false) }
1097
+ result = @record.save
1098
+
1099
+ assert result
1100
+ assert_equal "idling", @record.state
1101
+ @record.reload
1102
+ assert_equal "idling", @record.state
1103
+ end
1104
+
1105
+ if !defined?(Sequel::VERSION) || !%w(2.10.0 2.11.0).include?(Sequel::VERSION)
1106
+ def test_should_allow_transition_after_creation
1107
+ record = @record
1108
+ @model.after_create { record.ignite }
1109
+ result = @record.save
1110
+
1111
+ assert result
1112
+ assert_equal "idling", @record.state
1113
+ @record.reload
1114
+ assert_equal "idling", @record.state
1115
+ end
1116
+ end
1117
+ end
1118
+
1119
+ class MachineWithFailedActionTest < BaseTestCase
1120
+ def setup
1121
+ @model = new_model do
1122
+ def validate
1123
+ super
1124
+ errors[:state] << 'is invalid' unless %w(first_gear).include?(state)
1125
+ end
1126
+ end
1127
+
1128
+ @machine = StateMachine::Machine.new(@model)
1129
+ @machine.state :parked, :idling
1130
+ @machine.event :ignite
1131
+
1132
+ callbacks = []
1133
+ @machine.before_transition {callbacks << :before}
1134
+ @machine.after_transition {callbacks << :after}
1135
+ @machine.after_failure {callbacks << :after_failure}
1136
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
1137
+
1138
+ @record = @model.new(:state => 'parked')
1139
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1140
+ @result = @transition.perform
1141
+
1142
+ @callbacks = callbacks
1143
+ end
1144
+
1145
+ def test_should_not_be_successful
1146
+ assert !@result
1147
+ end
1148
+
1149
+ def test_should_not_change_current_state
1150
+ assert_equal 'parked', @record.state
1151
+ end
1152
+
1153
+ def test_should_not_save_record
1154
+ assert @record.new?
1155
+ end
1156
+
1157
+ def test_should_run_before_callbacks_and_after_callbacks_with_failures
1158
+ assert_equal [:before, :around_before, :after_failure], @callbacks
1159
+ end
1160
+ end
1161
+
1162
+ class MachineWithFailedAfterCallbacksTest < BaseTestCase
1163
+ def setup
1164
+ callbacks = []
1165
+
1166
+ @model = new_model
1167
+ @machine = StateMachine::Machine.new(@model)
1168
+ @machine.state :parked, :idling
1169
+ @machine.event :ignite
1170
+ @machine.after_transition {callbacks << :after_1; false}
1171
+ @machine.after_transition {callbacks << :after_2}
1172
+ @machine.around_transition {|block| callbacks << :around_before; block.call; callbacks << :around_after}
1173
+
1174
+ @record = @model.new(:state => 'parked')
1175
+ @transition = StateMachine::Transition.new(@record, @machine, :ignite, :parked, :idling)
1176
+ @result = @transition.perform
1177
+
1178
+ @callbacks = callbacks
1179
+ end
1180
+
1181
+ def test_should_be_successful
1182
+ assert @result
1183
+ end
1184
+
1185
+ def test_should_change_current_state
1186
+ assert_equal 'idling', @record.state
1187
+ end
1188
+
1189
+ def test_should_save_record
1190
+ assert !@record.new?
1191
+ end
1192
+
1193
+ def test_should_not_run_further_after_callbacks
1194
+ assert_equal [:around_before, :around_after, :after_1], @callbacks
1195
+ end
1196
+ end
1197
+
1198
+ class MachineWithValidationsTest < BaseTestCase
1199
+ def setup
1200
+ @model = new_model
1201
+ @machine = StateMachine::Machine.new(@model)
1202
+ @machine.state :parked
1203
+
1204
+ @record = @model.new
1205
+ end
1206
+
1207
+ def test_should_invalidate_using_errors
1208
+ @record.state = 'parked'
1209
+
1210
+ @machine.invalidate(@record, :state, :invalid_transition, [[:event, 'park']])
1211
+ assert_equal ['cannot transition via "park"'], @record.errors.on(:state)
1212
+ end
1213
+
1214
+ def test_should_auto_prefix_custom_attributes_on_invalidation
1215
+ @machine.invalidate(@record, :event, :invalid)
1216
+
1217
+ assert_equal ['is invalid'], @record.errors.on(:state_event)
1218
+ end
1219
+
1220
+ def test_should_clear_errors_on_reset
1221
+ @record.state = 'parked'
1222
+ @record.errors.add(:state, 'is invalid')
1223
+
1224
+ @machine.reset(@record)
1225
+ assert_nil @record.errors.on(:id)
1226
+ end
1227
+
1228
+ def test_should_be_valid_if_state_is_known
1229
+ @record.state = 'parked'
1230
+
1231
+ assert @record.valid?
1232
+ end
1233
+
1234
+ def test_should_not_be_valid_if_state_is_unknown
1235
+ @record.state = 'invalid'
1236
+
1237
+ assert !@record.valid?
1238
+ assert_equal ['state is invalid'], @record.errors.full_messages
1239
+ end
1240
+ end
1241
+
1242
+ class MachineWithValidationsAndCustomAttributeTest < BaseTestCase
1243
+ def setup
1244
+ @model = new_model do
1245
+ alias_method :status, :state
1246
+ alias_method :status=, :state=
1247
+ end
1248
+
1249
+ @machine = StateMachine::Machine.new(@model, :status, :attribute => :state)
1250
+ @machine.state :parked
1251
+
1252
+ @record = @model.new
1253
+ end
1254
+
1255
+ def test_should_add_validation_errors_to_custom_attribute
1256
+ @record.state = 'invalid'
1257
+
1258
+ assert !@record.valid?
1259
+ assert_equal ['state is invalid'], @record.errors.full_messages
1260
+
1261
+ @record.state = 'parked'
1262
+ assert @record.valid?
1263
+ end
1264
+ end
1265
+
1266
+ class MachineErrorsTest < BaseTestCase
1267
+ def setup
1268
+ @model = new_model
1269
+ @machine = StateMachine::Machine.new(@model)
1270
+ @record = @model.new
1271
+ end
1272
+
1273
+ def test_should_be_able_to_describe_current_errors
1274
+ @record.errors.add(:id, 'cannot be blank')
1275
+ @record.errors.add(:state, 'is invalid')
1276
+ assert_equal ['id cannot be blank', 'state is invalid'], @machine.errors_for(@record).split(', ').sort
1277
+ end
1278
+
1279
+ def test_should_describe_as_halted_with_no_errors
1280
+ assert_equal 'Transition halted', @machine.errors_for(@record)
1281
+ end
1282
+ end
1283
+
1284
+ class MachineWithStateDrivenValidationsTest < BaseTestCase
1285
+ def setup
1286
+ @model = new_model do
1287
+ attr_accessor :seatbelt
1288
+ end
1289
+
1290
+ @machine = StateMachine::Machine.new(@model)
1291
+ @machine.state :first_gear do
1292
+ def validate
1293
+ super if defined?(super)
1294
+ errors[:seatbelt] << 'is not present' if seatbelt.nil?
1295
+ end
1296
+ end
1297
+ @machine.other_states :parked
1298
+ end
1299
+
1300
+ def test_should_be_valid_if_validation_fails_outside_state_scope
1301
+ record = @model.new(:state => 'parked', :seatbelt => nil)
1302
+ assert record.valid?
1303
+ end
1304
+
1305
+ def test_should_be_invalid_if_validation_fails_within_state_scope
1306
+ record = @model.new(:state => 'first_gear', :seatbelt => nil)
1307
+ assert !record.valid?
1308
+ end
1309
+
1310
+ def test_should_be_valid_if_validation_succeeds_within_state_scope
1311
+ record = @model.new(:state => 'first_gear', :seatbelt => true)
1312
+ assert record.valid?
1313
+ end
1314
+ end
1315
+
1316
+ class MachineWithEventAttributesOnValidationTest < BaseTestCase
1317
+ def setup
1318
+ @model = new_model
1319
+ @machine = StateMachine::Machine.new(@model)
1320
+ @machine.event :ignite do
1321
+ transition :parked => :idling
1322
+ end
1323
+
1324
+ @record = @model.new
1325
+ @record.state = 'parked'
1326
+ @record.state_event = 'ignite'
1327
+ end
1328
+
1329
+ def test_should_fail_if_event_is_invalid
1330
+ @record.state_event = 'invalid'
1331
+ assert !@record.valid?
1332
+ assert_equal ['state_event is invalid'], @record.errors.full_messages
1333
+ end
1334
+
1335
+ def test_should_fail_if_event_has_no_transition
1336
+ @record.state = 'idling'
1337
+ assert !@record.valid?
1338
+ assert_equal ['state_event cannot transition when idling'], @record.errors.full_messages
1339
+ end
1340
+
1341
+ def test_should_be_successful_if_event_has_transition
1342
+ assert @record.valid?
1343
+ end
1344
+
1345
+ def test_should_run_before_callbacks
1346
+ ran_callback = false
1347
+ @machine.before_transition { ran_callback = true }
1348
+
1349
+ @record.valid?
1350
+ assert ran_callback
1351
+ end
1352
+
1353
+ def test_should_run_around_callbacks_before_yield
1354
+ ran_callback = false
1355
+ @machine.around_transition {|block| ran_callback = true; block.call }
1356
+
1357
+ begin
1358
+ @record.valid?
1359
+ rescue ArgumentError
1360
+ raise if StateMachine::Transition.pause_supported?
1361
+ end
1362
+ assert ran_callback
1363
+ end
1364
+
1365
+ def test_should_persist_new_state
1366
+ @record.valid?
1367
+ assert_equal 'idling', @record.state
1368
+ end
1369
+
1370
+ def test_should_not_run_after_callbacks
1371
+ ran_callback = false
1372
+ @machine.after_transition { ran_callback = true }
1373
+
1374
+ @record.valid?
1375
+ assert !ran_callback
1376
+ end
1377
+
1378
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_validation_fails
1379
+ @model.class_eval do
1380
+ attr_accessor :seatbelt
1381
+ def validate
1382
+ super
1383
+ errors[:seatbelt] << 'is not present' if seatbelt.nil?
1384
+ end
1385
+ end
1386
+
1387
+ ran_callback = false
1388
+ @machine.after_transition { ran_callback = true }
1389
+
1390
+ @record.valid?
1391
+ assert !ran_callback
1392
+ end
1393
+
1394
+ def test_should_run_failure_callbacks_if_validation_fails
1395
+ @model.class_eval do
1396
+ attr_accessor :seatbelt
1397
+ def validate
1398
+ super
1399
+ errors[:seatbelt] << 'is not present' if seatbelt.nil?
1400
+ end
1401
+ end
1402
+
1403
+ ran_callback = false
1404
+ @machine.after_failure { ran_callback = true }
1405
+
1406
+ @record.valid?
1407
+ assert ran_callback
1408
+ end
1409
+
1410
+ def test_should_not_run_around_callbacks_after_yield
1411
+ ran_callback = [false]
1412
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1413
+
1414
+ begin
1415
+ @record.valid?
1416
+ rescue ArgumentError
1417
+ raise if StateMachine::Transition.pause_supported?
1418
+ end
1419
+ assert !ran_callback[0]
1420
+ end
1421
+
1422
+ def test_should_not_run_around_callbacks_after_yield_with_failures_disabled_if_validation_fails
1423
+ @model.class_eval do
1424
+ attr_accessor :seatbelt
1425
+ def validate
1426
+ super
1427
+ errors[:seatbelt] << 'is not present' if seatbelt.nil?
1428
+ end
1429
+ end
1430
+
1431
+ ran_callback = [false]
1432
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1433
+
1434
+ begin
1435
+ @record.valid?
1436
+ rescue ArgumentError
1437
+ raise if StateMachine::Transition.pause_supported?
1438
+ end
1439
+ assert !ran_callback[0]
1440
+ end
1441
+
1442
+ def test_should_not_run_before_transitions_within_transaction
1443
+ @machine.before_transition { self.class.create; raise Sequel::Error::Rollback }
1444
+
1445
+ begin
1446
+ @record.valid?
1447
+ rescue Sequel::Error::Rollback
1448
+ end
1449
+
1450
+ assert_equal 1, @model.count
1451
+ end
1452
+ end
1453
+
1454
+ class MachineWithEventAttributesOnSaveTest < BaseTestCase
1455
+ def setup
1456
+ @model = new_model
1457
+ @machine = StateMachine::Machine.new(@model)
1458
+ @machine.event :ignite do
1459
+ transition :parked => :idling
1460
+ end
1461
+
1462
+ @record = @model.new
1463
+ @record.state = 'parked'
1464
+ @record.state_event = 'ignite'
1465
+ end
1466
+
1467
+ def test_should_fail_if_event_is_invalid
1468
+ @record.state_event = 'invalid'
1469
+ assert !@record.save
1470
+ end
1471
+
1472
+ def test_should_raise_exception_when_enabled_if_event_is_invalid
1473
+ @record.state_event = 'invalid'
1474
+ @model.raise_on_save_failure = true
1475
+ if defined?(Sequel::BeforeHookFailed)
1476
+ assert_raise(Sequel::BeforeHookFailed) { @record.save }
1477
+ else
1478
+ assert_raise(Sequel::Error) { @record.save }
1479
+ end
1480
+ end
1481
+
1482
+ def test_should_fail_if_event_has_no_transition
1483
+ @record.state = 'idling'
1484
+ assert !@record.save
1485
+ end
1486
+
1487
+ def test_should_raise_exception_when_enabled_if_event_has_no_transition
1488
+ @record.state = 'idling'
1489
+ @model.raise_on_save_failure = true
1490
+ if defined?(Sequel::BeforeHookFailed)
1491
+ assert_raise(Sequel::BeforeHookFailed) { @record.save }
1492
+ else
1493
+ assert_raise(Sequel::Error) { @record.save }
1494
+ end
1495
+ end
1496
+
1497
+ def test_should_be_successful_if_event_has_transition
1498
+ assert @record.save
1499
+ end
1500
+
1501
+ def test_should_run_before_callbacks
1502
+ ran_callback = false
1503
+ @machine.before_transition { ran_callback = true }
1504
+
1505
+ @record.save
1506
+ assert ran_callback
1507
+ end
1508
+
1509
+ def test_should_run_before_callbacks_once
1510
+ before_count = 0
1511
+ @machine.before_transition { before_count += 1 }
1512
+
1513
+ @record.save
1514
+ assert_equal 1, before_count
1515
+ end
1516
+
1517
+ def test_should_run_around_callbacks_before_yield
1518
+ ran_callback = false
1519
+ @machine.around_transition {|block| ran_callback = true; block.call }
1520
+
1521
+ @record.save
1522
+ assert ran_callback
1523
+ end
1524
+
1525
+ def test_should_run_around_callbacks_before_yield_once
1526
+ around_before_count = 0
1527
+ @machine.around_transition {|block| around_before_count += 1; block.call }
1528
+
1529
+ @record.save
1530
+ assert_equal 1, around_before_count
1531
+ end
1532
+
1533
+ def test_should_persist_new_state
1534
+ @record.save
1535
+ assert_equal 'idling', @record.state
1536
+ end
1537
+
1538
+ def test_should_run_after_callbacks
1539
+ ran_callback = false
1540
+ @machine.after_transition { ran_callback = true }
1541
+
1542
+ @record.save
1543
+ assert ran_callback
1544
+ end
1545
+
1546
+ def test_should_not_run_after_callbacks_with_failures_disabled_if_fails
1547
+ @model.before_create {|record| false}
1548
+
1549
+ ran_callback = false
1550
+ @machine.after_transition { ran_callback = true }
1551
+
1552
+ @record.save
1553
+ assert !ran_callback
1554
+ end
1555
+
1556
+ def test_should_run_failure_callbacks_if_fails
1557
+ @model.before_create {|record| false}
1558
+
1559
+ ran_callback = false
1560
+ @machine.after_failure { ran_callback = true }
1561
+
1562
+ @record.save
1563
+ assert ran_callback
1564
+ end
1565
+
1566
+ def test_should_run_before_transitions_within_transaction
1567
+ @machine.before_transition { self.class.create; raise Sequel::Error::Rollback }
1568
+
1569
+ begin
1570
+ @record.save
1571
+ rescue Sequel::Error::Rollback
1572
+ end
1573
+
1574
+ assert_equal 0, @model.count
1575
+ end
1576
+
1577
+ def test_should_not_run_around_callbacks_with_failures_disabled_if_fails
1578
+ @model.before_create {|record| false}
1579
+
1580
+ ran_callback = [false]
1581
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1582
+
1583
+ @record.save
1584
+ assert !ran_callback[0]
1585
+ end
1586
+
1587
+ def test_should_run_around_callbacks_after_yield
1588
+ ran_callback = [false]
1589
+ @machine.around_transition {|block| block.call; ran_callback[0] = true }
1590
+
1591
+ @record.save
1592
+ assert ran_callback[0]
1593
+ end
1594
+
1595
+ def test_should_run_after_transitions_within_transaction
1596
+ @machine.after_transition { self.class.create; raise Sequel::Error::Rollback }
1597
+
1598
+ @record.save
1599
+
1600
+ assert_equal 0, @model.count
1601
+ end
1602
+
1603
+ def test_should_run_around_transition_within_transaction
1604
+ @machine.around_transition {|block| block.call; self.class.create; raise Sequel::Error::Rollback }
1605
+
1606
+ @record.save
1607
+
1608
+ assert_equal 0, @model.count
1609
+ end
1610
+
1611
+ def test_should_allow_additional_transitions_to_new_state_in_after_transitions
1612
+ @machine.event :park do
1613
+ transition :idling => :parked
1614
+ end
1615
+
1616
+ @machine.after_transition(:on => :ignite) { park }
1617
+
1618
+ @record.save
1619
+ assert_equal 'parked', @record.state
1620
+
1621
+ @record.reload
1622
+ assert_equal 'parked', @record.state
1623
+ end
1624
+
1625
+ def test_should_allow_additional_transitions_to_previous_state_in_after_transitions
1626
+ @machine.event :shift_up do
1627
+ transition :idling => :first_gear
1628
+ end
1629
+
1630
+ @machine.after_transition(:on => :ignite) { shift_up }
1631
+
1632
+ @record.save
1633
+ assert_equal 'first_gear', @record.state
1634
+
1635
+ @record.reload
1636
+ assert_equal 'first_gear', @record.state
1637
+ end
1638
+
1639
+ def test_should_return_nil_on_manual_rollback
1640
+ @machine.before_transition { raise Sequel::Error::Rollback }
1641
+
1642
+ assert_equal nil, @record.save
1643
+ end
1644
+ end
1645
+
1646
+ if defined?(Sequel::VERSION) && Gem::Version.new(Sequel::VERSION) >= Gem::Version.new('3.4.0')
1647
+ class MachineWithEventAttributesOnAutosaveTest < BaseTestCase
1648
+ def setup
1649
+ @vehicle_model = new_model(:vehicle)
1650
+ DB.alter_table(@vehicle_model.table_identifier) do
1651
+ add_column :owner_id, :integer
1652
+ end
1653
+ @vehicle_model.class_eval { get_db_schema(true) }
1654
+ SequelTest.const_set('Vehicle', @vehicle_model)
1655
+
1656
+ @owner_model = new_model(:owner) do
1657
+ plugin :nested_attributes
1658
+ end
1659
+ SequelTest.const_set('Owner', @owner_model)
1660
+
1661
+ machine = StateMachine::Machine.new(@vehicle_model)
1662
+ machine.event :ignite do
1663
+ transition :parked => :idling
1664
+ end
1665
+
1666
+ @owner = @owner_model.create
1667
+ @vehicle = @vehicle_model.create(:state => 'parked', :owner_id => @owner.id)
1668
+ end
1669
+
1670
+ def test_should_persist_many_association
1671
+ @owner_model.one_to_many :vehicles, :class_name => 'SequelTest::Vehicle'
1672
+ @owner_model.nested_attributes :vehicles
1673
+
1674
+ @owner.vehicles_attributes = [{:id => @vehicle.id, :state_event => 'ignite'}]
1675
+ @owner.save
1676
+
1677
+ @vehicle.reload
1678
+ assert_equal 'idling', @vehicle.state
1679
+ end
1680
+
1681
+ if Gem::Version.new(Sequel::VERSION) >= Gem::Version.new('3.10.0')
1682
+ def test_should_persist_one_association
1683
+ @owner_model.one_to_one :vehicle, :class_name => 'SequelTest::Vehicle'
1684
+ @owner_model.nested_attributes :vehicle
1685
+
1686
+ @owner.vehicle_attributes = {:id => @vehicle.id, :state_event => 'ignite'}
1687
+ @owner.save
1688
+
1689
+ @vehicle.reload
1690
+ assert_equal 'idling', @vehicle.state
1691
+ end
1692
+ end
1693
+
1694
+ def teardown
1695
+ SequelTest.class_eval do
1696
+ remove_const('Vehicle')
1697
+ remove_const('Owner')
1698
+ end
1699
+ super
1700
+ end
1701
+ end
1702
+ end
1703
+
1704
+ class MachineWithEventAttributesOnCustomActionTest < BaseTestCase
1705
+ def setup
1706
+ @superclass = new_model do
1707
+ def persist
1708
+ save
1709
+ end
1710
+ end
1711
+ @model = Class.new(@superclass)
1712
+ @machine = StateMachine::Machine.new(@model, :action => :persist)
1713
+ @machine.event :ignite do
1714
+ transition :parked => :idling
1715
+ end
1716
+
1717
+ @record = @model.new
1718
+ @record.state = 'parked'
1719
+ @record.state_event = 'ignite'
1720
+ end
1721
+
1722
+ def test_should_not_transition_on_valid?
1723
+ @record.valid?
1724
+ assert_equal 'parked', @record.state
1725
+ end
1726
+
1727
+ def test_should_not_transition_on_save
1728
+ @record.save
1729
+ assert_equal 'parked', @record.state
1730
+ end
1731
+
1732
+ def test_should_transition_on_custom_action
1733
+ @record.persist
1734
+ assert_equal 'idling', @record.state
1735
+ end
1736
+ end
1737
+
1738
+ class MachineWithScopesTest < BaseTestCase
1739
+ def setup
1740
+ @model = new_model
1741
+ @machine = StateMachine::Machine.new(@model)
1742
+ @machine.state :parked, :first_gear
1743
+ @machine.state :idling, :value => lambda {'idling'}
1744
+ end
1745
+
1746
+ def test_should_create_singular_with_scope
1747
+ assert @model.respond_to?(:with_state)
1748
+ end
1749
+
1750
+ def test_should_only_include_records_with_state_in_singular_with_scope
1751
+ parked = @model.create :state => 'parked'
1752
+ @model.create :state => 'idling'
1753
+
1754
+ assert_equal [parked], @model.with_state(:parked).all
1755
+ end
1756
+
1757
+ def test_should_create_plural_with_scope
1758
+ assert @model.respond_to?(:with_states)
1759
+ end
1760
+
1761
+ def test_should_only_include_records_with_states_in_plural_with_scope
1762
+ parked = @model.create :state => 'parked'
1763
+ idling = @model.create :state => 'idling'
1764
+
1765
+ assert_equal [parked, idling], @model.with_states(:parked, :idling).all
1766
+ end
1767
+
1768
+ def test_should_allow_lookup_by_string_name
1769
+ parked = @model.create :state => 'parked'
1770
+ idling = @model.create :state => 'idling'
1771
+
1772
+ assert_equal [parked, idling], @model.with_states('parked', 'idling').all
1773
+ end
1774
+
1775
+ def test_should_create_singular_without_scope
1776
+ assert @model.respond_to?(:without_state)
1777
+ end
1778
+
1779
+ def test_should_only_include_records_without_state_in_singular_without_scope
1780
+ parked = @model.create :state => 'parked'
1781
+ idling = @model.create :state => 'idling'
1782
+
1783
+ assert_equal [parked], @model.without_state(:idling).all
1784
+ end
1785
+
1786
+ def test_should_create_plural_without_scope
1787
+ assert @model.respond_to?(:without_states)
1788
+ end
1789
+
1790
+ def test_should_only_include_records_without_states_in_plural_without_scope
1791
+ parked = @model.create :state => 'parked'
1792
+ idling = @model.create :state => 'idling'
1793
+ first_gear = @model.create :state => 'first_gear'
1794
+
1795
+ assert_equal [parked, idling], @model.without_states(:first_gear).all
1796
+ end
1797
+
1798
+ def test_should_allow_chaining_scopes_and_filters
1799
+ parked = @model.create :state => 'parked'
1800
+ idling = @model.create :state => 'idling'
1801
+
1802
+ assert_equal [idling], @model.without_state(:parked).with_state(:idling).all
1803
+ end
1804
+
1805
+ def test_should_run_on_tables_with_double_underscores
1806
+ @model = new_model(:foo__bar)
1807
+ @machine = StateMachine::Machine.new(@model)
1808
+ @machine.state :parked, :first_gear
1809
+ @machine.state :idling, :value => lambda {'idling'}
1810
+
1811
+ parked = @model.create :state => 'parked'
1812
+ @model.create :state => 'idling'
1813
+
1814
+ assert_equal [parked], @model.with_state(:parked).all
1815
+ end
1816
+ end
1817
+
1818
+ class MachineWithScopesAndOwnerSubclassTest < BaseTestCase
1819
+ def setup
1820
+ @model = new_model
1821
+ @machine = StateMachine::Machine.new(@model, :state)
1822
+
1823
+ @subclass = Class.new(@model)
1824
+ @subclass_machine = @subclass.state_machine(:state) {}
1825
+ @subclass_machine.state :parked, :idling, :first_gear
1826
+ end
1827
+
1828
+ def test_should_only_include_records_with_subclass_states_in_with_scope
1829
+ parked = @subclass.create :state => 'parked'
1830
+ idling = @subclass.create :state => 'idling'
1831
+
1832
+ assert_equal [parked, idling], @subclass.with_states(:parked, :idling).all
1833
+ end
1834
+
1835
+ def test_should_only_include_records_without_subclass_states_in_without_scope
1836
+ parked = @subclass.create :state => 'parked'
1837
+ idling = @subclass.create :state => 'idling'
1838
+ first_gear = @subclass.create :state => 'first_gear'
1839
+
1840
+ assert_equal [parked, idling], @subclass.without_states(:first_gear).all
1841
+ end
1842
+ end
1843
+
1844
+ class MachineWithComplexPluralizationScopesTest < BaseTestCase
1845
+ def setup
1846
+ @model = new_model
1847
+ @machine = StateMachine::Machine.new(@model, :status)
1848
+ end
1849
+
1850
+ def test_should_create_singular_with_scope
1851
+ assert @model.respond_to?(:with_status)
1852
+ end
1853
+
1854
+ def test_should_create_plural_with_scope
1855
+ assert @model.respond_to?(:with_statuses)
1856
+ end
1857
+ end
1858
+
1859
+ class MachineWithScopesAndJoinsTest < BaseTestCase
1860
+ def setup
1861
+ @company = new_model(:company)
1862
+ SequelTest.const_set('Company', @company)
1863
+
1864
+ @vehicle = new_model(:vehicle) do
1865
+ many_to_one :company, :class => SequelTest::Company
1866
+ end
1867
+ DB.alter_table(@vehicle.table_identifier) do
1868
+ add_column :company_id, :integer
1869
+ end
1870
+ @vehicle.class_eval { get_db_schema(true) }
1871
+ SequelTest.const_set('Vehicle', @vehicle)
1872
+
1873
+ @company_machine = StateMachine::Machine.new(@company, :initial => :active)
1874
+ @vehicle_machine = StateMachine::Machine.new(@vehicle, :initial => :parked)
1875
+ @vehicle_machine.state :idling
1876
+
1877
+ @ford = @company.create
1878
+ @mustang = @vehicle.create(:company => @ford)
1879
+ end
1880
+
1881
+ def test_should_find_records_in_with_scope
1882
+ assert_equal [@mustang], @vehicle.with_states(:parked).join(@company.table_identifier.value, :id => :company_id).filter(:"#{@company.table_identifier.value}__state" => 'active').select(@vehicle.table_identifier.value.to_sym.*).all
1883
+ end
1884
+
1885
+ def test_should_find_records_in_without_scope
1886
+ assert_equal [@mustang], @vehicle.without_states(:idling).join(@company.table_identifier.value, :id => :company_id).filter(:"#{@company.table_identifier.value}__state" => 'active').select(@vehicle.table_identifier.value.to_sym.*).all
1887
+ end
1888
+
1889
+ def teardown
1890
+ SequelTest.class_eval do
1891
+ remove_const('Vehicle')
1892
+ remove_const('Company')
1893
+ end
1894
+ end
1895
+ end
1896
+ end