glebtv_state_machine 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +2 -0
  5. data/.travis.yml +72 -0
  6. data/.yardopts +5 -0
  7. data/Appraisals +491 -0
  8. data/CHANGELOG.md +502 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE +20 -0
  11. data/README.md +1244 -0
  12. data/Rakefile +42 -0
  13. data/examples/AutoShop_state.png +0 -0
  14. data/examples/Car_state.png +0 -0
  15. data/examples/Gemfile +5 -0
  16. data/examples/Gemfile.lock +14 -0
  17. data/examples/TrafficLight_state.png +0 -0
  18. data/examples/Vehicle_state.png +0 -0
  19. data/examples/auto_shop.rb +13 -0
  20. data/examples/car.rb +21 -0
  21. data/examples/doc/AutoShop.html +2856 -0
  22. data/examples/doc/AutoShop_state.png +0 -0
  23. data/examples/doc/Car.html +919 -0
  24. data/examples/doc/Car_state.png +0 -0
  25. data/examples/doc/TrafficLight.html +2230 -0
  26. data/examples/doc/TrafficLight_state.png +0 -0
  27. data/examples/doc/Vehicle.html +7921 -0
  28. data/examples/doc/Vehicle_state.png +0 -0
  29. data/examples/doc/_index.html +136 -0
  30. data/examples/doc/class_list.html +47 -0
  31. data/examples/doc/css/common.css +1 -0
  32. data/examples/doc/css/full_list.css +55 -0
  33. data/examples/doc/css/style.css +322 -0
  34. data/examples/doc/file_list.html +46 -0
  35. data/examples/doc/frames.html +13 -0
  36. data/examples/doc/index.html +136 -0
  37. data/examples/doc/js/app.js +205 -0
  38. data/examples/doc/js/full_list.js +173 -0
  39. data/examples/doc/js/jquery.js +16 -0
  40. data/examples/doc/method_list.html +734 -0
  41. data/examples/doc/top-level-namespace.html +105 -0
  42. data/examples/merb-rest/controller.rb +51 -0
  43. data/examples/merb-rest/model.rb +28 -0
  44. data/examples/merb-rest/view_edit.html.erb +24 -0
  45. data/examples/merb-rest/view_index.html.erb +23 -0
  46. data/examples/merb-rest/view_new.html.erb +13 -0
  47. data/examples/merb-rest/view_show.html.erb +17 -0
  48. data/examples/rails-rest/controller.rb +43 -0
  49. data/examples/rails-rest/migration.rb +7 -0
  50. data/examples/rails-rest/model.rb +23 -0
  51. data/examples/rails-rest/view__form.html.erb +34 -0
  52. data/examples/rails-rest/view_edit.html.erb +6 -0
  53. data/examples/rails-rest/view_index.html.erb +25 -0
  54. data/examples/rails-rest/view_new.html.erb +5 -0
  55. data/examples/rails-rest/view_show.html.erb +19 -0
  56. data/examples/traffic_light.rb +9 -0
  57. data/examples/vehicle.rb +33 -0
  58. data/gemfiles/active_model_3.0.0.gemfile +7 -0
  59. data/gemfiles/active_model_3.0.0.gemfile.lock +35 -0
  60. data/gemfiles/active_model_3.0.5.gemfile +7 -0
  61. data/gemfiles/active_model_3.0.5.gemfile.lock +35 -0
  62. data/gemfiles/active_model_3.1.1.gemfile +7 -0
  63. data/gemfiles/active_model_3.1.1.gemfile.lock +36 -0
  64. data/gemfiles/active_model_3.2.1.gemfile +7 -0
  65. data/gemfiles/active_model_3.2.12.gemfile +7 -0
  66. data/gemfiles/active_model_3.2.12.gemfile.lock +36 -0
  67. data/gemfiles/active_model_3.2.13.rc1.gemfile +7 -0
  68. data/gemfiles/active_model_3.2.13.rc1.gemfile.lock +36 -0
  69. data/gemfiles/active_model_4.0.0.gemfile +9 -0
  70. data/gemfiles/active_model_4.0.0.gemfile.lock +78 -0
  71. data/gemfiles/active_record_2.0.0.gemfile +9 -0
  72. data/gemfiles/active_record_2.0.0.gemfile.lock +39 -0
  73. data/gemfiles/active_record_2.0.5.gemfile +9 -0
  74. data/gemfiles/active_record_2.0.5.gemfile.lock +39 -0
  75. data/gemfiles/active_record_2.1.0.gemfile +9 -0
  76. data/gemfiles/active_record_2.1.0.gemfile.lock +39 -0
  77. data/gemfiles/active_record_2.1.2.gemfile +9 -0
  78. data/gemfiles/active_record_2.1.2.gemfile.lock +39 -0
  79. data/gemfiles/active_record_2.2.3.gemfile +9 -0
  80. data/gemfiles/active_record_2.2.3.gemfile.lock +39 -0
  81. data/gemfiles/active_record_2.3.12.gemfile +9 -0
  82. data/gemfiles/active_record_2.3.12.gemfile.lock +39 -0
  83. data/gemfiles/active_record_2.3.5.gemfile +9 -0
  84. data/gemfiles/active_record_2.3.5.gemfile.lock +39 -0
  85. data/gemfiles/active_record_3.0.0.gemfile +9 -0
  86. data/gemfiles/active_record_3.0.0.gemfile.lock +51 -0
  87. data/gemfiles/active_record_3.0.5.gemfile +9 -0
  88. data/gemfiles/active_record_3.0.5.gemfile.lock +50 -0
  89. data/gemfiles/active_record_3.1.1.gemfile +9 -0
  90. data/gemfiles/active_record_3.1.1.gemfile.lock +51 -0
  91. data/gemfiles/active_record_3.2.12.gemfile +9 -0
  92. data/gemfiles/active_record_3.2.12.gemfile.lock +51 -0
  93. data/gemfiles/active_record_3.2.13.rc1.gemfile +9 -0
  94. data/gemfiles/active_record_3.2.13.rc1.gemfile.lock +51 -0
  95. data/gemfiles/active_record_4.0.0.gemfile +11 -0
  96. data/gemfiles/active_record_4.0.0.gemfile.lock +83 -0
  97. data/gemfiles/data_mapper_0.10.2.gemfile +13 -0
  98. data/gemfiles/data_mapper_0.10.2.gemfile.lock +56 -0
  99. data/gemfiles/data_mapper_0.9.11.gemfile +13 -0
  100. data/gemfiles/data_mapper_0.9.11.gemfile.lock +71 -0
  101. data/gemfiles/data_mapper_0.9.4.gemfile +12 -0
  102. data/gemfiles/data_mapper_0.9.4.gemfile.lock +70 -0
  103. data/gemfiles/data_mapper_0.9.7.gemfile +13 -0
  104. data/gemfiles/data_mapper_0.9.7.gemfile.lock +67 -0
  105. data/gemfiles/data_mapper_1.0.0.gemfile +12 -0
  106. data/gemfiles/data_mapper_1.0.0.gemfile.lock +63 -0
  107. data/gemfiles/data_mapper_1.0.1.gemfile +12 -0
  108. data/gemfiles/data_mapper_1.0.1.gemfile.lock +63 -0
  109. data/gemfiles/data_mapper_1.0.2.gemfile +12 -0
  110. data/gemfiles/data_mapper_1.0.2.gemfile.lock +63 -0
  111. data/gemfiles/data_mapper_1.1.0.gemfile +12 -0
  112. data/gemfiles/data_mapper_1.1.0.gemfile.lock +61 -0
  113. data/gemfiles/data_mapper_1.2.0.gemfile +12 -0
  114. data/gemfiles/data_mapper_1.2.0.gemfile.lock +61 -0
  115. data/gemfiles/default.gemfile +7 -0
  116. data/gemfiles/default.gemfile.lock +27 -0
  117. data/gemfiles/graphviz_0.9.17.gemfile +7 -0
  118. data/gemfiles/graphviz_0.9.17.gemfile.lock +29 -0
  119. data/gemfiles/graphviz_0.9.21.gemfile +7 -0
  120. data/gemfiles/graphviz_0.9.21.gemfile.lock +29 -0
  121. data/gemfiles/graphviz_1.0.0.gemfile +7 -0
  122. data/gemfiles/graphviz_1.0.0.gemfile.lock +29 -0
  123. data/gemfiles/graphviz_1.0.3.gemfile +7 -0
  124. data/gemfiles/graphviz_1.0.3.gemfile.lock +29 -0
  125. data/gemfiles/graphviz_1.0.8.gemfile +7 -0
  126. data/gemfiles/graphviz_1.0.8.gemfile.lock +29 -0
  127. data/gemfiles/mongo_mapper_0.10.0.gemfile +8 -0
  128. data/gemfiles/mongo_mapper_0.10.0.gemfile.lock +47 -0
  129. data/gemfiles/mongo_mapper_0.11.2.gemfile +9 -0
  130. data/gemfiles/mongo_mapper_0.11.2.gemfile.lock +48 -0
  131. data/gemfiles/mongo_mapper_0.12.0.gemfile +9 -0
  132. data/gemfiles/mongo_mapper_0.12.0.gemfile.lock +48 -0
  133. data/gemfiles/mongo_mapper_0.5.5.gemfile +8 -0
  134. data/gemfiles/mongo_mapper_0.5.5.gemfile.lock +36 -0
  135. data/gemfiles/mongo_mapper_0.5.8.gemfile +8 -0
  136. data/gemfiles/mongo_mapper_0.5.8.gemfile.lock +36 -0
  137. data/gemfiles/mongo_mapper_0.6.0.gemfile +8 -0
  138. data/gemfiles/mongo_mapper_0.6.0.gemfile.lock +36 -0
  139. data/gemfiles/mongo_mapper_0.6.10.gemfile +8 -0
  140. data/gemfiles/mongo_mapper_0.6.10.gemfile.lock +36 -0
  141. data/gemfiles/mongo_mapper_0.7.0.gemfile +8 -0
  142. data/gemfiles/mongo_mapper_0.7.0.gemfile.lock +36 -0
  143. data/gemfiles/mongo_mapper_0.7.5.gemfile +8 -0
  144. data/gemfiles/mongo_mapper_0.7.5.gemfile.lock +39 -0
  145. data/gemfiles/mongo_mapper_0.8.0.gemfile +10 -0
  146. data/gemfiles/mongo_mapper_0.8.0.gemfile.lock +43 -0
  147. data/gemfiles/mongo_mapper_0.8.3.gemfile +10 -0
  148. data/gemfiles/mongo_mapper_0.8.3.gemfile.lock +43 -0
  149. data/gemfiles/mongo_mapper_0.8.4.gemfile +8 -0
  150. data/gemfiles/mongo_mapper_0.8.4.gemfile.lock +42 -0
  151. data/gemfiles/mongo_mapper_0.8.6.gemfile +8 -0
  152. data/gemfiles/mongo_mapper_0.8.6.gemfile.lock +42 -0
  153. data/gemfiles/mongo_mapper_0.9.0.gemfile +7 -0
  154. data/gemfiles/mongo_mapper_0.9.0.gemfile.lock +45 -0
  155. data/gemfiles/mongoid_2.0.0.gemfile +9 -0
  156. data/gemfiles/mongoid_2.0.0.gemfile.lock +49 -0
  157. data/gemfiles/mongoid_2.1.4.gemfile +9 -0
  158. data/gemfiles/mongoid_2.1.4.gemfile.lock +47 -0
  159. data/gemfiles/mongoid_2.2.4.gemfile +9 -0
  160. data/gemfiles/mongoid_2.2.4.gemfile.lock +47 -0
  161. data/gemfiles/mongoid_2.3.3.gemfile +9 -0
  162. data/gemfiles/mongoid_2.3.3.gemfile.lock +47 -0
  163. data/gemfiles/mongoid_2.4.0.gemfile +9 -0
  164. data/gemfiles/mongoid_2.4.0.gemfile.lock +47 -0
  165. data/gemfiles/mongoid_2.4.10.gemfile +9 -0
  166. data/gemfiles/mongoid_2.4.10.gemfile.lock +47 -0
  167. data/gemfiles/mongoid_2.5.2.gemfile +9 -0
  168. data/gemfiles/mongoid_2.5.2.gemfile.lock +47 -0
  169. data/gemfiles/mongoid_2.6.0.gemfile +9 -0
  170. data/gemfiles/mongoid_2.6.0.gemfile.lock +47 -0
  171. data/gemfiles/mongoid_3.0.0.gemfile +8 -0
  172. data/gemfiles/mongoid_3.0.0.gemfile.lock +45 -0
  173. data/gemfiles/mongoid_3.0.22.gemfile +8 -0
  174. data/gemfiles/mongoid_3.0.22.gemfile.lock +45 -0
  175. data/gemfiles/mongoid_3.1.0.gemfile +8 -0
  176. data/gemfiles/mongoid_3.1.0.gemfile.lock +45 -0
  177. data/gemfiles/sequel_2.11.0.gemfile +9 -0
  178. data/gemfiles/sequel_2.11.0.gemfile.lock +33 -0
  179. data/gemfiles/sequel_2.12.0.gemfile +9 -0
  180. data/gemfiles/sequel_2.12.0.gemfile.lock +33 -0
  181. data/gemfiles/sequel_2.8.0.gemfile +9 -0
  182. data/gemfiles/sequel_2.8.0.gemfile.lock +33 -0
  183. data/gemfiles/sequel_3.0.0.gemfile +9 -0
  184. data/gemfiles/sequel_3.0.0.gemfile.lock +33 -0
  185. data/gemfiles/sequel_3.10.0.gemfile +9 -0
  186. data/gemfiles/sequel_3.10.0.gemfile.lock +33 -0
  187. data/gemfiles/sequel_3.13.0.gemfile +9 -0
  188. data/gemfiles/sequel_3.13.0.gemfile.lock +33 -0
  189. data/gemfiles/sequel_3.14.0.gemfile +9 -0
  190. data/gemfiles/sequel_3.14.0.gemfile.lock +33 -0
  191. data/gemfiles/sequel_3.23.0.gemfile +9 -0
  192. data/gemfiles/sequel_3.23.0.gemfile.lock +33 -0
  193. data/gemfiles/sequel_3.24.0.gemfile +9 -0
  194. data/gemfiles/sequel_3.24.0.gemfile.lock +33 -0
  195. data/gemfiles/sequel_3.29.0.gemfile +9 -0
  196. data/gemfiles/sequel_3.29.0.gemfile.lock +33 -0
  197. data/gemfiles/sequel_3.34.0.gemfile +9 -0
  198. data/gemfiles/sequel_3.34.0.gemfile.lock +33 -0
  199. data/gemfiles/sequel_3.35.0.gemfile +9 -0
  200. data/gemfiles/sequel_3.35.0.gemfile.lock +33 -0
  201. data/gemfiles/sequel_3.4.0.gemfile +9 -0
  202. data/gemfiles/sequel_3.4.0.gemfile.lock +33 -0
  203. data/gemfiles/sequel_3.44.0.gemfile +9 -0
  204. data/gemfiles/sequel_3.44.0.gemfile.lock +33 -0
  205. data/glebtv_state_machine.gemspec +22 -0
  206. data/init.rb +1 -0
  207. data/lib/glebtv_state_machine.rb +1 -0
  208. data/lib/state_machine.rb +8 -0
  209. data/lib/state_machine/assertions.rb +36 -0
  210. data/lib/state_machine/branch.rb +225 -0
  211. data/lib/state_machine/callback.rb +236 -0
  212. data/lib/state_machine/core.rb +12 -0
  213. data/lib/state_machine/core_ext.rb +2 -0
  214. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  215. data/lib/state_machine/error.rb +13 -0
  216. data/lib/state_machine/eval_helpers.rb +87 -0
  217. data/lib/state_machine/event.rb +257 -0
  218. data/lib/state_machine/event_collection.rb +141 -0
  219. data/lib/state_machine/extensions.rb +149 -0
  220. data/lib/state_machine/graph.rb +92 -0
  221. data/lib/state_machine/helper_module.rb +17 -0
  222. data/lib/state_machine/initializers.rb +4 -0
  223. data/lib/state_machine/initializers/merb.rb +1 -0
  224. data/lib/state_machine/initializers/rails.rb +25 -0
  225. data/lib/state_machine/integrations.rb +121 -0
  226. data/lib/state_machine/integrations/active_model.rb +585 -0
  227. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  228. data/lib/state_machine/integrations/active_model/observer.rb +33 -0
  229. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  230. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  231. data/lib/state_machine/integrations/active_record.rb +548 -0
  232. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  233. data/lib/state_machine/integrations/active_record/versions.rb +123 -0
  234. data/lib/state_machine/integrations/base.rb +100 -0
  235. data/lib/state_machine/integrations/data_mapper.rb +511 -0
  236. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  237. data/lib/state_machine/integrations/data_mapper/versions.rb +85 -0
  238. data/lib/state_machine/integrations/mongo_mapper.rb +389 -0
  239. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  240. data/lib/state_machine/integrations/mongo_mapper/versions.rb +89 -0
  241. data/lib/state_machine/integrations/mongoid.rb +465 -0
  242. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  243. data/lib/state_machine/integrations/mongoid/versions.rb +81 -0
  244. data/lib/state_machine/integrations/sequel.rb +486 -0
  245. data/lib/state_machine/integrations/sequel/versions.rb +95 -0
  246. data/lib/state_machine/machine.rb +2292 -0
  247. data/lib/state_machine/machine_collection.rb +86 -0
  248. data/lib/state_machine/macro_methods.rb +522 -0
  249. data/lib/state_machine/matcher.rb +123 -0
  250. data/lib/state_machine/matcher_helpers.rb +54 -0
  251. data/lib/state_machine/node_collection.rb +222 -0
  252. data/lib/state_machine/path.rb +120 -0
  253. data/lib/state_machine/path_collection.rb +90 -0
  254. data/lib/state_machine/state.rb +297 -0
  255. data/lib/state_machine/state_collection.rb +112 -0
  256. data/lib/state_machine/state_context.rb +138 -0
  257. data/lib/state_machine/transition.rb +470 -0
  258. data/lib/state_machine/transition_collection.rb +245 -0
  259. data/lib/state_machine/version.rb +3 -0
  260. data/lib/state_machine/yard.rb +8 -0
  261. data/lib/state_machine/yard/handlers.rb +12 -0
  262. data/lib/state_machine/yard/handlers/base.rb +32 -0
  263. data/lib/state_machine/yard/handlers/event.rb +25 -0
  264. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  265. data/lib/state_machine/yard/handlers/state.rb +25 -0
  266. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  267. data/lib/state_machine/yard/templates.rb +3 -0
  268. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  269. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  270. data/lib/tasks/state_machine.rake +1 -0
  271. data/lib/tasks/state_machine.rb +30 -0
  272. data/lib/yard-state_machine.rb +2 -0
  273. data/test/files/en.yml +17 -0
  274. data/test/files/switch.rb +15 -0
  275. data/test/functional/state_machine_test.rb +1066 -0
  276. data/test/test_helper.rb +7 -0
  277. data/test/unit/assertions_test.rb +40 -0
  278. data/test/unit/branch_test.rb +969 -0
  279. data/test/unit/callback_test.rb +704 -0
  280. data/test/unit/error_test.rb +43 -0
  281. data/test/unit/eval_helpers_test.rb +270 -0
  282. data/test/unit/event_collection_test.rb +398 -0
  283. data/test/unit/event_test.rb +1196 -0
  284. data/test/unit/graph_test.rb +98 -0
  285. data/test/unit/helper_module_test.rb +17 -0
  286. data/test/unit/integrations/active_model_test.rb +1245 -0
  287. data/test/unit/integrations/active_record_test.rb +2551 -0
  288. data/test/unit/integrations/base_test.rb +104 -0
  289. data/test/unit/integrations/data_mapper_test.rb +2194 -0
  290. data/test/unit/integrations/mongo_mapper_test.rb +2026 -0
  291. data/test/unit/integrations/mongoid_test.rb +2309 -0
  292. data/test/unit/integrations/sequel_test.rb +1896 -0
  293. data/test/unit/integrations_test.rb +83 -0
  294. data/test/unit/invalid_event_test.rb +20 -0
  295. data/test/unit/invalid_parallel_transition_test.rb +18 -0
  296. data/test/unit/invalid_transition_test.rb +115 -0
  297. data/test/unit/machine_collection_test.rb +603 -0
  298. data/test/unit/machine_test.rb +3407 -0
  299. data/test/unit/matcher_helpers_test.rb +37 -0
  300. data/test/unit/matcher_test.rb +155 -0
  301. data/test/unit/node_collection_test.rb +362 -0
  302. data/test/unit/path_collection_test.rb +266 -0
  303. data/test/unit/path_test.rb +485 -0
  304. data/test/unit/state_collection_test.rb +352 -0
  305. data/test/unit/state_context_test.rb +441 -0
  306. data/test/unit/state_machine_test.rb +31 -0
  307. data/test/unit/state_test.rb +1101 -0
  308. data/test/unit/transition_collection_test.rb +2168 -0
  309. data/test/unit/transition_test.rb +1558 -0
  310. metadata +438 -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