state_machine_deuxito 0.0.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 (310) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +8 -0
  4. data/.yardopts +5 -0
  5. data/Appraisals +12 -0
  6. data/CHANGELOG.md +502 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +20 -0
  9. data/README.md +8 -0
  10. data/Rakefile +41 -0
  11. data/TODO.md +2 -0
  12. data/examples/AutoShop_state.png +0 -0
  13. data/examples/Car_state.png +0 -0
  14. data/examples/Gemfile +5 -0
  15. data/examples/Gemfile.lock +14 -0
  16. data/examples/TrafficLight_state.png +0 -0
  17. data/examples/Vehicle_state.png +0 -0
  18. data/examples/auto_shop.rb +13 -0
  19. data/examples/car.rb +21 -0
  20. data/examples/doc/AutoShop.html +2856 -0
  21. data/examples/doc/AutoShop_state.png +0 -0
  22. data/examples/doc/Car.html +919 -0
  23. data/examples/doc/Car_state.png +0 -0
  24. data/examples/doc/TrafficLight.html +2230 -0
  25. data/examples/doc/TrafficLight_state.png +0 -0
  26. data/examples/doc/Vehicle.html +7921 -0
  27. data/examples/doc/Vehicle_state.png +0 -0
  28. data/examples/doc/_index.html +136 -0
  29. data/examples/doc/class_list.html +47 -0
  30. data/examples/doc/css/common.css +1 -0
  31. data/examples/doc/css/full_list.css +55 -0
  32. data/examples/doc/css/style.css +322 -0
  33. data/examples/doc/file_list.html +46 -0
  34. data/examples/doc/frames.html +13 -0
  35. data/examples/doc/index.html +136 -0
  36. data/examples/doc/js/app.js +205 -0
  37. data/examples/doc/js/full_list.js +173 -0
  38. data/examples/doc/js/jquery.js +16 -0
  39. data/examples/doc/method_list.html +734 -0
  40. data/examples/doc/top-level-namespace.html +105 -0
  41. data/examples/merb-rest/controller.rb +51 -0
  42. data/examples/merb-rest/model.rb +28 -0
  43. data/examples/merb-rest/view_edit.html.erb +24 -0
  44. data/examples/merb-rest/view_index.html.erb +23 -0
  45. data/examples/merb-rest/view_new.html.erb +13 -0
  46. data/examples/merb-rest/view_show.html.erb +17 -0
  47. data/examples/rails-rest/controller.rb +43 -0
  48. data/examples/rails-rest/migration.rb +7 -0
  49. data/examples/rails-rest/model.rb +23 -0
  50. data/examples/rails-rest/view__form.html.erb +34 -0
  51. data/examples/rails-rest/view_edit.html.erb +6 -0
  52. data/examples/rails-rest/view_index.html.erb +25 -0
  53. data/examples/rails-rest/view_new.html.erb +5 -0
  54. data/examples/rails-rest/view_show.html.erb +19 -0
  55. data/examples/traffic_light.rb +9 -0
  56. data/examples/vehicle.rb +33 -0
  57. data/gemfiles/4.2.11.1.gemfile +9 -0
  58. data/gemfiles/4.2.11.1.gemfile.lock +57 -0
  59. data/gemfiles/active_model_3.0.0.gemfile +7 -0
  60. data/gemfiles/active_model_3.0.0.gemfile.lock +35 -0
  61. data/gemfiles/active_model_3.0.5.gemfile +7 -0
  62. data/gemfiles/active_model_3.0.5.gemfile.lock +35 -0
  63. data/gemfiles/active_model_3.1.1.gemfile +7 -0
  64. data/gemfiles/active_model_3.1.1.gemfile.lock +36 -0
  65. data/gemfiles/active_model_3.2.1.gemfile +7 -0
  66. data/gemfiles/active_model_3.2.12.gemfile +7 -0
  67. data/gemfiles/active_model_3.2.12.gemfile.lock +36 -0
  68. data/gemfiles/active_model_3.2.13.rc1.gemfile +7 -0
  69. data/gemfiles/active_model_3.2.13.rc1.gemfile.lock +36 -0
  70. data/gemfiles/active_model_4.0.0.gemfile +9 -0
  71. data/gemfiles/active_model_4.0.0.gemfile.lock +78 -0
  72. data/gemfiles/active_record_2.0.0.gemfile +9 -0
  73. data/gemfiles/active_record_2.0.0.gemfile.lock +39 -0
  74. data/gemfiles/active_record_2.0.5.gemfile +9 -0
  75. data/gemfiles/active_record_2.0.5.gemfile.lock +39 -0
  76. data/gemfiles/active_record_2.1.0.gemfile +9 -0
  77. data/gemfiles/active_record_2.1.0.gemfile.lock +39 -0
  78. data/gemfiles/active_record_2.1.2.gemfile +9 -0
  79. data/gemfiles/active_record_2.1.2.gemfile.lock +39 -0
  80. data/gemfiles/active_record_2.2.3.gemfile +9 -0
  81. data/gemfiles/active_record_2.2.3.gemfile.lock +39 -0
  82. data/gemfiles/active_record_2.3.12.gemfile +9 -0
  83. data/gemfiles/active_record_2.3.12.gemfile.lock +39 -0
  84. data/gemfiles/active_record_2.3.5.gemfile +9 -0
  85. data/gemfiles/active_record_2.3.5.gemfile.lock +39 -0
  86. data/gemfiles/active_record_3.0.0.gemfile +9 -0
  87. data/gemfiles/active_record_3.0.0.gemfile.lock +51 -0
  88. data/gemfiles/active_record_3.0.5.gemfile +9 -0
  89. data/gemfiles/active_record_3.0.5.gemfile.lock +50 -0
  90. data/gemfiles/active_record_3.1.1.gemfile +9 -0
  91. data/gemfiles/active_record_3.1.1.gemfile.lock +51 -0
  92. data/gemfiles/active_record_3.2.12.gemfile +9 -0
  93. data/gemfiles/active_record_3.2.12.gemfile.lock +51 -0
  94. data/gemfiles/active_record_3.2.13.rc1.gemfile +9 -0
  95. data/gemfiles/active_record_3.2.13.rc1.gemfile.lock +51 -0
  96. data/gemfiles/active_record_4.0.0.gemfile +11 -0
  97. data/gemfiles/active_record_4.0.0.gemfile.lock +83 -0
  98. data/gemfiles/data_mapper_0.10.2.gemfile +13 -0
  99. data/gemfiles/data_mapper_0.10.2.gemfile.lock +56 -0
  100. data/gemfiles/data_mapper_0.9.11.gemfile +13 -0
  101. data/gemfiles/data_mapper_0.9.11.gemfile.lock +71 -0
  102. data/gemfiles/data_mapper_0.9.4.gemfile +12 -0
  103. data/gemfiles/data_mapper_0.9.4.gemfile.lock +70 -0
  104. data/gemfiles/data_mapper_0.9.7.gemfile +13 -0
  105. data/gemfiles/data_mapper_0.9.7.gemfile.lock +67 -0
  106. data/gemfiles/data_mapper_1.0.0.gemfile +12 -0
  107. data/gemfiles/data_mapper_1.0.0.gemfile.lock +63 -0
  108. data/gemfiles/data_mapper_1.0.1.gemfile +12 -0
  109. data/gemfiles/data_mapper_1.0.1.gemfile.lock +63 -0
  110. data/gemfiles/data_mapper_1.0.2.gemfile +12 -0
  111. data/gemfiles/data_mapper_1.0.2.gemfile.lock +63 -0
  112. data/gemfiles/data_mapper_1.1.0.gemfile +12 -0
  113. data/gemfiles/data_mapper_1.1.0.gemfile.lock +61 -0
  114. data/gemfiles/data_mapper_1.2.0.gemfile +12 -0
  115. data/gemfiles/data_mapper_1.2.0.gemfile.lock +61 -0
  116. data/gemfiles/default.gemfile +5 -0
  117. data/gemfiles/default.gemfile.lock +30 -0
  118. data/gemfiles/graphviz_0.9.17.gemfile +7 -0
  119. data/gemfiles/graphviz_0.9.17.gemfile.lock +29 -0
  120. data/gemfiles/graphviz_0.9.21.gemfile +7 -0
  121. data/gemfiles/graphviz_0.9.21.gemfile.lock +29 -0
  122. data/gemfiles/graphviz_1.0.0.gemfile +7 -0
  123. data/gemfiles/graphviz_1.0.0.gemfile.lock +29 -0
  124. data/gemfiles/graphviz_1.0.3.gemfile +7 -0
  125. data/gemfiles/graphviz_1.0.3.gemfile.lock +29 -0
  126. data/gemfiles/graphviz_1.0.8.gemfile +7 -0
  127. data/gemfiles/graphviz_1.0.8.gemfile.lock +29 -0
  128. data/gemfiles/mongo_mapper_0.10.0.gemfile +8 -0
  129. data/gemfiles/mongo_mapper_0.10.0.gemfile.lock +47 -0
  130. data/gemfiles/mongo_mapper_0.11.2.gemfile +9 -0
  131. data/gemfiles/mongo_mapper_0.11.2.gemfile.lock +48 -0
  132. data/gemfiles/mongo_mapper_0.12.0.gemfile +9 -0
  133. data/gemfiles/mongo_mapper_0.12.0.gemfile.lock +48 -0
  134. data/gemfiles/mongo_mapper_0.5.5.gemfile +8 -0
  135. data/gemfiles/mongo_mapper_0.5.5.gemfile.lock +36 -0
  136. data/gemfiles/mongo_mapper_0.5.8.gemfile +8 -0
  137. data/gemfiles/mongo_mapper_0.5.8.gemfile.lock +36 -0
  138. data/gemfiles/mongo_mapper_0.6.0.gemfile +8 -0
  139. data/gemfiles/mongo_mapper_0.6.0.gemfile.lock +36 -0
  140. data/gemfiles/mongo_mapper_0.6.10.gemfile +8 -0
  141. data/gemfiles/mongo_mapper_0.6.10.gemfile.lock +36 -0
  142. data/gemfiles/mongo_mapper_0.7.0.gemfile +8 -0
  143. data/gemfiles/mongo_mapper_0.7.0.gemfile.lock +36 -0
  144. data/gemfiles/mongo_mapper_0.7.5.gemfile +8 -0
  145. data/gemfiles/mongo_mapper_0.7.5.gemfile.lock +39 -0
  146. data/gemfiles/mongo_mapper_0.8.0.gemfile +10 -0
  147. data/gemfiles/mongo_mapper_0.8.0.gemfile.lock +43 -0
  148. data/gemfiles/mongo_mapper_0.8.3.gemfile +10 -0
  149. data/gemfiles/mongo_mapper_0.8.3.gemfile.lock +43 -0
  150. data/gemfiles/mongo_mapper_0.8.4.gemfile +8 -0
  151. data/gemfiles/mongo_mapper_0.8.4.gemfile.lock +42 -0
  152. data/gemfiles/mongo_mapper_0.8.6.gemfile +8 -0
  153. data/gemfiles/mongo_mapper_0.8.6.gemfile.lock +42 -0
  154. data/gemfiles/mongo_mapper_0.9.0.gemfile +7 -0
  155. data/gemfiles/mongo_mapper_0.9.0.gemfile.lock +45 -0
  156. data/gemfiles/mongoid_2.0.0.gemfile +9 -0
  157. data/gemfiles/mongoid_2.0.0.gemfile.lock +49 -0
  158. data/gemfiles/mongoid_2.1.4.gemfile +9 -0
  159. data/gemfiles/mongoid_2.1.4.gemfile.lock +47 -0
  160. data/gemfiles/mongoid_2.2.4.gemfile +9 -0
  161. data/gemfiles/mongoid_2.2.4.gemfile.lock +47 -0
  162. data/gemfiles/mongoid_2.3.3.gemfile +9 -0
  163. data/gemfiles/mongoid_2.3.3.gemfile.lock +47 -0
  164. data/gemfiles/mongoid_2.4.0.gemfile +9 -0
  165. data/gemfiles/mongoid_2.4.0.gemfile.lock +47 -0
  166. data/gemfiles/mongoid_2.4.10.gemfile +9 -0
  167. data/gemfiles/mongoid_2.4.10.gemfile.lock +47 -0
  168. data/gemfiles/mongoid_2.5.2.gemfile +9 -0
  169. data/gemfiles/mongoid_2.5.2.gemfile.lock +47 -0
  170. data/gemfiles/mongoid_2.6.0.gemfile +9 -0
  171. data/gemfiles/mongoid_2.6.0.gemfile.lock +47 -0
  172. data/gemfiles/mongoid_3.0.0.gemfile +8 -0
  173. data/gemfiles/mongoid_3.0.0.gemfile.lock +45 -0
  174. data/gemfiles/mongoid_3.0.22.gemfile +8 -0
  175. data/gemfiles/mongoid_3.0.22.gemfile.lock +45 -0
  176. data/gemfiles/mongoid_3.1.0.gemfile +8 -0
  177. data/gemfiles/mongoid_3.1.0.gemfile.lock +45 -0
  178. data/gemfiles/sequel_2.11.0.gemfile +9 -0
  179. data/gemfiles/sequel_2.11.0.gemfile.lock +33 -0
  180. data/gemfiles/sequel_2.12.0.gemfile +9 -0
  181. data/gemfiles/sequel_2.12.0.gemfile.lock +33 -0
  182. data/gemfiles/sequel_2.8.0.gemfile +9 -0
  183. data/gemfiles/sequel_2.8.0.gemfile.lock +33 -0
  184. data/gemfiles/sequel_3.0.0.gemfile +9 -0
  185. data/gemfiles/sequel_3.0.0.gemfile.lock +33 -0
  186. data/gemfiles/sequel_3.10.0.gemfile +9 -0
  187. data/gemfiles/sequel_3.10.0.gemfile.lock +33 -0
  188. data/gemfiles/sequel_3.13.0.gemfile +9 -0
  189. data/gemfiles/sequel_3.13.0.gemfile.lock +33 -0
  190. data/gemfiles/sequel_3.14.0.gemfile +9 -0
  191. data/gemfiles/sequel_3.14.0.gemfile.lock +33 -0
  192. data/gemfiles/sequel_3.23.0.gemfile +9 -0
  193. data/gemfiles/sequel_3.23.0.gemfile.lock +33 -0
  194. data/gemfiles/sequel_3.24.0.gemfile +9 -0
  195. data/gemfiles/sequel_3.24.0.gemfile.lock +33 -0
  196. data/gemfiles/sequel_3.29.0.gemfile +9 -0
  197. data/gemfiles/sequel_3.29.0.gemfile.lock +33 -0
  198. data/gemfiles/sequel_3.34.0.gemfile +9 -0
  199. data/gemfiles/sequel_3.34.0.gemfile.lock +33 -0
  200. data/gemfiles/sequel_3.35.0.gemfile +9 -0
  201. data/gemfiles/sequel_3.35.0.gemfile.lock +33 -0
  202. data/gemfiles/sequel_3.4.0.gemfile +9 -0
  203. data/gemfiles/sequel_3.4.0.gemfile.lock +33 -0
  204. data/gemfiles/sequel_3.44.0.gemfile +9 -0
  205. data/gemfiles/sequel_3.44.0.gemfile.lock +33 -0
  206. data/init.rb +1 -0
  207. data/lib/state_machine.rb +8 -0
  208. data/lib/state_machine/assertions.rb +36 -0
  209. data/lib/state_machine/branch.rb +225 -0
  210. data/lib/state_machine/callback.rb +236 -0
  211. data/lib/state_machine/core.rb +12 -0
  212. data/lib/state_machine/core_ext.rb +2 -0
  213. data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
  214. data/lib/state_machine/error.rb +13 -0
  215. data/lib/state_machine/eval_helpers.rb +87 -0
  216. data/lib/state_machine/event.rb +257 -0
  217. data/lib/state_machine/event_collection.rb +141 -0
  218. data/lib/state_machine/extensions.rb +149 -0
  219. data/lib/state_machine/graph.rb +92 -0
  220. data/lib/state_machine/helper_module.rb +17 -0
  221. data/lib/state_machine/initializers.rb +4 -0
  222. data/lib/state_machine/initializers/merb.rb +1 -0
  223. data/lib/state_machine/initializers/rails.rb +25 -0
  224. data/lib/state_machine/integrations.rb +121 -0
  225. data/lib/state_machine/integrations/active_model.rb +585 -0
  226. data/lib/state_machine/integrations/active_model/locale.rb +11 -0
  227. data/lib/state_machine/integrations/active_model/observer.rb +33 -0
  228. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  229. data/lib/state_machine/integrations/active_model/versions.rb +31 -0
  230. data/lib/state_machine/integrations/active_record.rb +552 -0
  231. data/lib/state_machine/integrations/active_record/locale.rb +20 -0
  232. data/lib/state_machine/integrations/active_record/versions.rb +123 -0
  233. data/lib/state_machine/integrations/base.rb +100 -0
  234. data/lib/state_machine/integrations/data_mapper.rb +511 -0
  235. data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
  236. data/lib/state_machine/integrations/data_mapper/versions.rb +85 -0
  237. data/lib/state_machine/integrations/mongo_mapper.rb +389 -0
  238. data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
  239. data/lib/state_machine/integrations/mongo_mapper/versions.rb +89 -0
  240. data/lib/state_machine/integrations/mongoid.rb +465 -0
  241. data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
  242. data/lib/state_machine/integrations/mongoid/versions.rb +81 -0
  243. data/lib/state_machine/integrations/sequel.rb +486 -0
  244. data/lib/state_machine/integrations/sequel/versions.rb +95 -0
  245. data/lib/state_machine/machine.rb +2292 -0
  246. data/lib/state_machine/machine_collection.rb +86 -0
  247. data/lib/state_machine/macro_methods.rb +522 -0
  248. data/lib/state_machine/matcher.rb +123 -0
  249. data/lib/state_machine/matcher_helpers.rb +54 -0
  250. data/lib/state_machine/node_collection.rb +222 -0
  251. data/lib/state_machine/path.rb +120 -0
  252. data/lib/state_machine/path_collection.rb +90 -0
  253. data/lib/state_machine/state.rb +297 -0
  254. data/lib/state_machine/state_collection.rb +112 -0
  255. data/lib/state_machine/state_context.rb +138 -0
  256. data/lib/state_machine/transition.rb +470 -0
  257. data/lib/state_machine/transition_collection.rb +245 -0
  258. data/lib/state_machine/version.rb +3 -0
  259. data/lib/state_machine/yard.rb +8 -0
  260. data/lib/state_machine/yard/handlers.rb +12 -0
  261. data/lib/state_machine/yard/handlers/base.rb +32 -0
  262. data/lib/state_machine/yard/handlers/event.rb +25 -0
  263. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  264. data/lib/state_machine/yard/handlers/state.rb +25 -0
  265. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  266. data/lib/state_machine/yard/templates.rb +3 -0
  267. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  268. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  269. data/lib/tasks/state_machine.rake +1 -0
  270. data/lib/tasks/state_machine.rb +30 -0
  271. data/lib/yard-state_machine.rb +2 -0
  272. data/state_machine.gemspec +21 -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 +1067 -0
  276. data/test/test_helper.rb +8 -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 +3431 -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 +451 -0
@@ -0,0 +1,25 @@
1
+ module StateMachine
2
+ module YARD
3
+ module Handlers
4
+ # Handles and processes #state
5
+ class State < Base
6
+ handles method_call(:state)
7
+
8
+ def process
9
+ if owner.is_a?(StateMachine::Machine)
10
+ handler = self
11
+ statement = self.statement
12
+ names = extract_node_names(statement.parameters(false))
13
+
14
+ names.each do |name|
15
+ owner.state(name) do
16
+ # Parse the block
17
+ handler.parse_block(statement.last.last, :owner => self)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ module StateMachine
2
+ module YARD
3
+ module Handlers
4
+ # Handles and processes #transition
5
+ class Transition < Base
6
+ handles method_call(:transition)
7
+
8
+ def process
9
+ if [StateMachine::Machine, StateMachine::Event, StateMachine::State].include?(owner.class)
10
+ options = {}
11
+
12
+ # Extract requirements
13
+ ast = statement.parameters.first
14
+ ast.children.each do |assoc|
15
+ # Skip conditionals
16
+ next if %w(if unless).include?(assoc[0].jump(:ident).source)
17
+
18
+ options[extract_requirement(assoc[0])] = extract_requirement(assoc[1])
19
+ end
20
+
21
+ owner.transition(options)
22
+ end
23
+ end
24
+
25
+ private
26
+ # Extracts the statement requirement from the given node
27
+ def extract_requirement(ast)
28
+ case ast.type
29
+ when :symbol_literal, :string_literal, :array
30
+ extract_node_names(ast, false)
31
+ when :binary
32
+ AllMatcher.instance - extract_node_names(ast.children.last)
33
+ when :var_ref, :vcall
34
+ case ast.source
35
+ when 'nil'
36
+ nil
37
+ when 'same'
38
+ LoopbackMatcher.instance
39
+ else
40
+ AllMatcher.instance
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,3 @@
1
+ require 'yard'
2
+
3
+ YARD::Templates::Engine.register_template_path File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
@@ -0,0 +1,30 @@
1
+ require 'tempfile'
2
+
3
+ # Define where state machine descriptions will be rendered
4
+ def init
5
+ super
6
+ sections.place(:state_machine_details).before(:children)
7
+ end
8
+
9
+ # Renders state machine details in the main content of the class's documentation
10
+ def state_machine_details
11
+ erb(:state_machines) if state_machines
12
+ end
13
+
14
+ # Gets a list of state machines prased for this class
15
+ def state_machines
16
+ @state_machines ||= begin
17
+ if state_machines = object['state_machines']
18
+ state_machines.each do |name, machine|
19
+ serializer.serialize(state_machine_image_path(machine), machine[:image]) if machine[:image]
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # Generates the image path for the given machine's visualization
26
+ def state_machine_image_path(machine)
27
+ base_path = File.dirname(serializer.serialized_path(object))
28
+ image_name = "#{object.name}_#{machine[:name]}"
29
+ "#{File.join(base_path, image_name)}.png"
30
+ end
@@ -0,0 +1,12 @@
1
+ <h2>State Machines</h2>
2
+
3
+ This class contains <%= state_machines.count %> state machine(s).
4
+
5
+ <% state_machines.each do |name, machine| %>
6
+ <h3><%= h(machine[:name]) %></h3>
7
+ <p><%= h(machine[:description]) %></p>
8
+
9
+ <% if machine[:image] %>
10
+ <img alt="State machine diagram for <%= h(machine[:name]) %>" src="<%= url_for(state_machine_image_path(machine)) %>" />
11
+ <% end %>
12
+ <% end %>
@@ -0,0 +1 @@
1
+ require File.join("#{File.dirname(__FILE__)}/state_machine")
@@ -0,0 +1,30 @@
1
+ namespace :state_machine do
2
+ desc 'Draws state machines using GraphViz (options: CLASS=User,Vehicle; FILE=user.rb,vehicle.rb [not required in Rails / Merb]; FONT=Arial; FORMAT=png; ORIENTATION=portrait; HUMAN_NAMES=true'
3
+ task :draw do
4
+ # Build drawing options
5
+ options = {}
6
+ options[:file] = ENV['FILE'] if ENV['FILE']
7
+ options[:path] = ENV['TARGET'] if ENV['TARGET']
8
+ options[:format] = ENV['FORMAT'] if ENV['FORMAT']
9
+ options[:font] = ENV['FONT'] if ENV['FONT']
10
+ options[:orientation] = ENV['ORIENTATION'] if ENV['ORIENTATION']
11
+ options[:human_names] = ENV['HUMAN_NAMES'] == 'true' if ENV['HUMAN_NAMES']
12
+
13
+ if defined?(Rails)
14
+ puts "Files are automatically loaded in Rails; ignoring FILE option" if options.delete(:file)
15
+ Rake::Task['environment'].invoke
16
+ elsif defined?(Merb)
17
+ puts "Files are automatically loaded in Merb; ignoring FILE option" if options.delete(:file)
18
+ Rake::Task['merb_env'].invoke
19
+
20
+ # Fix ruby-graphviz being incompatible with Merb's process title
21
+ $0 = 'rake'
22
+ else
23
+ # Load the library
24
+ $:.unshift(File.dirname(__FILE__) + '/..')
25
+ require 'state_machine'
26
+ end
27
+
28
+ StateMachine::Machine.draw(ENV['CLASS'], options)
29
+ end
30
+ end
@@ -0,0 +1,2 @@
1
+ require 'state_machine/core'
2
+ require 'state_machine/yard'
@@ -0,0 +1,21 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
+ require 'state_machine/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "state_machine_deuxito"
6
+ s.version = StateMachine::VERSION
7
+ s.authors = ["Cwro Wren"]
8
+ s.description = "Fork of seuros/state_machine for use by DU"
9
+ s.summary = "State machines for attributes"
10
+ s.require_paths = ["lib"]
11
+ ignores = File.read(".gitignore").split.map {|i| i.sub(/\/$/, "/*").sub(/^[^\/]/, "**/\\0")}
12
+ s.files = (Dir[".*"] + Dir["**/*"]).select {|f| File.file?(f) && !ignores.any? {|i| File.fnmatch(i, "/#{f}")}}
13
+ s.test_files = s.files.grep(/^test\//)
14
+ s.rdoc_options = %w(--line-numbers --inline-source --title state_machine --main README.md)
15
+ s.extra_rdoc_files = %w(README.md CHANGELOG.md LICENSE)
16
+
17
+ s.add_development_dependency("rake")
18
+ s.add_development_dependency("simplecov")
19
+ s.add_development_dependency("appraisal")
20
+ s.add_development_dependency("test-unit")
21
+ end
data/test/files/en.yml ADDED
@@ -0,0 +1,17 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ messages:
5
+ invalid_transition: "cannot transition"
6
+ activemodel:
7
+ errors:
8
+ messages:
9
+ invalid_transition: "cannot %{event}"
10
+ mongoid:
11
+ errors:
12
+ messages:
13
+ invalid_transition: "cannot transition"
14
+ mongo_mapper:
15
+ errors:
16
+ messages:
17
+ invalid_transition: "cannot transition"
@@ -0,0 +1,15 @@
1
+ class Switch
2
+ def self.name
3
+ @name ||= "Switch_#{rand(1000000)}"
4
+ end
5
+
6
+ state_machine do
7
+ event :turn_on do
8
+ transition all => :on
9
+ end
10
+
11
+ event :turn_off do
12
+ transition all => :off
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,1067 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require 'test/unit'
3
+
4
+ class AutoShop
5
+ attr_accessor :num_customers
6
+
7
+ def initialize
8
+ @num_customers = 0
9
+ super
10
+ end
11
+
12
+ state_machine :initial => :available do
13
+ after_transition :available => any, :do => :increment_customers
14
+ after_transition :busy => any, :do => :decrement_customers
15
+
16
+ event :tow_vehicle do
17
+ transition :available => :busy
18
+ end
19
+
20
+ event :fix_vehicle do
21
+ transition :busy => :available
22
+ end
23
+ end
24
+
25
+ # Increments the number of customers in service
26
+ def increment_customers
27
+ self.num_customers += 1
28
+ end
29
+
30
+ # Decrements the number of customers in service
31
+ def decrement_customers
32
+ self.num_customers -= 1
33
+ end
34
+ end
35
+
36
+ class ModelBase
37
+ def save
38
+ @saved = true
39
+ self
40
+ end
41
+ end
42
+
43
+ class Vehicle < ModelBase
44
+ attr_accessor :auto_shop, :seatbelt_on, :insurance_premium, :force_idle, :callbacks, :saved, :time_elapsed, :last_transition_args
45
+
46
+ def initialize(attributes = {})
47
+ attributes = {
48
+ :auto_shop => AutoShop.new,
49
+ :seatbelt_on => false,
50
+ :insurance_premium => 50,
51
+ :force_idle => false,
52
+ :callbacks => [],
53
+ :saved => false
54
+ }.merge(attributes)
55
+
56
+ attributes.each {|attr, value| send("#{attr}=", value)}
57
+ super()
58
+ end
59
+
60
+ # Defines the state machine for the state of the vehicled
61
+ state_machine :initial => lambda {|vehicle| vehicle.force_idle ? :idling : :parked}, :action => :save do
62
+ before_transition {|vehicle, transition| vehicle.last_transition_args = transition.args}
63
+ before_transition :parked => any, :do => :put_on_seatbelt
64
+ before_transition any => :stalled, :do => :increase_insurance_premium
65
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.seatbelt_on = false}
66
+ after_transition :on => :crash, :do => :tow
67
+ after_transition :on => :repair, :do => :fix
68
+
69
+ # Callback tracking for initial state callbacks
70
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_parked'}
71
+ before_transition any => :idling, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_idling'}
72
+
73
+ around_transition do |vehicle, transition, block|
74
+ time = Time.now
75
+ block.call
76
+ vehicle.time_elapsed = Time.now - time
77
+ end
78
+
79
+ event all do
80
+ transition :locked => :parked
81
+ end
82
+
83
+ event :park do
84
+ transition [:idling, :first_gear] => :parked
85
+ end
86
+
87
+ event :ignite do
88
+ transition :stalled => :stalled
89
+ transition :parked => :idling
90
+ end
91
+
92
+ event :idle do
93
+ transition :first_gear => :idling
94
+ end
95
+
96
+ event :shift_up do
97
+ transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
98
+ end
99
+
100
+ event :shift_down do
101
+ transition :third_gear => :second_gear
102
+ transition :second_gear => :first_gear
103
+ end
104
+
105
+ event :crash do
106
+ transition [:first_gear, :second_gear, :third_gear] => :stalled, :if => lambda {|vehicle| vehicle.auto_shop.available?}
107
+ end
108
+
109
+ event :repair do
110
+ transition :stalled => :parked, :if => :auto_shop_busy?
111
+ end
112
+ end
113
+
114
+ state_machine :insurance_state, :initial => :inactive, :namespace => 'insurance' do
115
+ event :buy do
116
+ transition :inactive => :active
117
+ end
118
+
119
+ event :cancel do
120
+ transition :active => :inactive
121
+ end
122
+ end
123
+
124
+ def save
125
+ super
126
+ end
127
+
128
+ def new_record?
129
+ @saved == false
130
+ end
131
+
132
+ def park
133
+ super
134
+ end
135
+
136
+ # Tows the vehicle to the auto shop
137
+ def tow
138
+ auto_shop.tow_vehicle
139
+ end
140
+
141
+ # Fixes the vehicle; it will no longer be in the auto shop
142
+ def fix
143
+ auto_shop.fix_vehicle
144
+ end
145
+
146
+ def decibels
147
+ 0.0
148
+ end
149
+
150
+ private
151
+ # Safety first! Puts on our seatbelt
152
+ def put_on_seatbelt
153
+ self.seatbelt_on = true
154
+ end
155
+
156
+ # We crashed! Increase the insurance premium on the vehicle
157
+ def increase_insurance_premium
158
+ self.insurance_premium += 100
159
+ end
160
+
161
+ # Is the auto shop currently servicing another customer?
162
+ def auto_shop_busy?
163
+ auto_shop.busy?
164
+ end
165
+ end
166
+
167
+ class Car < Vehicle
168
+ state_machine do
169
+ event :reverse do
170
+ transition [:parked, :idling, :first_gear] => :backing_up
171
+ end
172
+
173
+ event :park do
174
+ transition :backing_up => :parked
175
+ end
176
+
177
+ event :idle do
178
+ transition :backing_up => :idling
179
+ end
180
+
181
+ event :shift_up do
182
+ transition :backing_up => :first_gear
183
+ end
184
+ end
185
+ end
186
+
187
+ class Motorcycle < Vehicle
188
+ state_machine :initial => :idling do
189
+ state :first_gear do
190
+ def decibels
191
+ 1.0
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ class TrafficLight
198
+ state_machine :initial => :stop do
199
+ event :cycle do
200
+ transition :stop => :proceed, :proceed => :caution, :caution => :stop
201
+ end
202
+
203
+ state :stop do
204
+ def color(transform)
205
+ value = 'red'
206
+
207
+ if block_given?
208
+ yield value
209
+ else
210
+ value.send(transform)
211
+ end
212
+
213
+ value
214
+ end
215
+ end
216
+
217
+ state all - :proceed do
218
+ def capture_violations?
219
+ true
220
+ end
221
+ end
222
+
223
+ state :proceed do
224
+ def color(transform)
225
+ 'green'
226
+ end
227
+
228
+ def capture_violations?
229
+ false
230
+ end
231
+ end
232
+
233
+ state :caution do
234
+ def color(transform)
235
+ 'yellow'
236
+ end
237
+ end
238
+ end
239
+
240
+ def color(transform = :to_s)
241
+ super
242
+ end
243
+ end
244
+
245
+ class VehicleTest < Test::Unit::TestCase
246
+ def setup
247
+ @vehicle = Vehicle.new
248
+ end
249
+
250
+ def test_should_not_allow_access_to_subclass_events
251
+ assert !@vehicle.respond_to?(:reverse)
252
+ end
253
+
254
+ def test_should_have_human_state_names
255
+ assert_equal 'parked', Vehicle.human_state_name(:parked)
256
+ end
257
+
258
+ def test_should_have_human_state_event_names
259
+ assert_equal 'park', Vehicle.human_state_event_name(:park)
260
+ end
261
+ end
262
+
263
+ class VehicleUnsavedTest < Test::Unit::TestCase
264
+ def setup
265
+ @vehicle = Vehicle.new
266
+ end
267
+
268
+ def test_should_be_in_parked_state
269
+ assert_equal 'parked', @vehicle.state
270
+ end
271
+
272
+ def test_should_raise_exception_if_checking_invalid_state
273
+ assert_raise(IndexError) { @vehicle.state?(:invalid) }
274
+ end
275
+
276
+ def test_should_raise_exception_if_getting_name_of_invalid_state
277
+ @vehicle.state = 'invalid'
278
+ assert_raise(ArgumentError) { @vehicle.state_name }
279
+ end
280
+
281
+ def test_should_be_parked
282
+ assert @vehicle.parked?
283
+ assert @vehicle.state?(:parked)
284
+ assert_equal :parked, @vehicle.state_name
285
+ assert_equal 'parked', @vehicle.human_state_name
286
+ end
287
+
288
+ def test_should_not_be_idling
289
+ assert !@vehicle.idling?
290
+ end
291
+
292
+ def test_should_not_be_first_gear
293
+ assert !@vehicle.first_gear?
294
+ end
295
+
296
+ def test_should_not_be_second_gear
297
+ assert !@vehicle.second_gear?
298
+ end
299
+
300
+ def test_should_not_be_stalled
301
+ assert !@vehicle.stalled?
302
+ end
303
+
304
+ def test_should_not_be_able_to_park
305
+ assert !@vehicle.can_park?
306
+ end
307
+
308
+ def test_should_not_have_a_transition_for_park
309
+ assert_nil @vehicle.park_transition
310
+ end
311
+
312
+ def test_should_not_allow_park
313
+ assert !@vehicle.park
314
+ end
315
+
316
+ def test_should_be_able_to_ignite
317
+ assert @vehicle.can_ignite?
318
+ end
319
+
320
+ def test_should_have_a_transition_for_ignite
321
+ transition = @vehicle.ignite_transition
322
+ assert_not_nil transition
323
+ assert_equal 'parked', transition.from
324
+ assert_equal 'idling', transition.to
325
+ assert_equal :ignite, transition.event
326
+ assert_equal :state, transition.attribute
327
+ assert_equal @vehicle, transition.object
328
+ end
329
+
330
+ def test_should_have_a_list_of_possible_events
331
+ assert_equal [:ignite], @vehicle.state_events
332
+ end
333
+
334
+ def test_should_have_a_list_of_possible_transitions
335
+ assert_equal [{:object => @vehicle, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @vehicle.state_transitions.map {|transition| transition.attributes}
336
+ end
337
+
338
+ def test_should_have_a_list_of_possible_paths
339
+ assert_equal [[
340
+ StateMachine::Transition.new(@vehicle, Vehicle.state_machine, :ignite, :parked, :idling),
341
+ StateMachine::Transition.new(@vehicle, Vehicle.state_machine, :shift_up, :idling, :first_gear)
342
+ ]], @vehicle.state_paths(:to => :first_gear)
343
+ end
344
+
345
+ def test_should_allow_generic_event_to_fire
346
+ assert @vehicle.fire_state_event(:ignite)
347
+ assert_equal 'idling', @vehicle.state
348
+ end
349
+
350
+ def test_should_pass_arguments_through_to_generic_event_runner
351
+ @vehicle.fire_state_event(:ignite, 1, 2, 3)
352
+ assert_equal [1, 2, 3], @vehicle.last_transition_args
353
+ end
354
+
355
+ def test_should_allow_skipping_action_through_generic_event_runner
356
+ @vehicle.fire_state_event(:ignite, false)
357
+ assert_equal false, @vehicle.saved
358
+ end
359
+
360
+ def test_should_raise_error_with_invalid_event_through_generic_event_runer
361
+ assert_raise(IndexError) { @vehicle.fire_state_event(:invalid) }
362
+ end
363
+
364
+ def test_should_allow_ignite
365
+ assert @vehicle.ignite
366
+ assert_equal 'idling', @vehicle.state
367
+ end
368
+
369
+ def test_should_allow_ignite_with_skipped_action
370
+ assert @vehicle.ignite(false)
371
+ assert @vehicle.new_record?
372
+ end
373
+
374
+ def test_should_allow_ignite_bang
375
+ assert @vehicle.ignite!
376
+ end
377
+
378
+ def test_should_allow_ignite_bang_with_skipped_action
379
+ assert @vehicle.ignite!(false)
380
+ assert @vehicle.new_record?
381
+ end
382
+
383
+ def test_should_be_saved_after_successful_event
384
+ @vehicle.ignite
385
+ assert !@vehicle.new_record?
386
+ end
387
+
388
+ def test_should_not_allow_idle
389
+ assert !@vehicle.idle
390
+ end
391
+
392
+ def test_should_not_allow_shift_up
393
+ assert !@vehicle.shift_up
394
+ end
395
+
396
+ def test_should_not_allow_shift_down
397
+ assert !@vehicle.shift_down
398
+ end
399
+
400
+ def test_should_not_allow_crash
401
+ assert !@vehicle.crash
402
+ end
403
+
404
+ def test_should_not_allow_repair
405
+ assert !@vehicle.repair
406
+ end
407
+
408
+ def test_should_be_insurance_inactive
409
+ assert @vehicle.insurance_inactive?
410
+ end
411
+
412
+ def test_should_be_able_to_buy
413
+ assert @vehicle.can_buy_insurance?
414
+ end
415
+
416
+ def test_should_allow_buying_insurance
417
+ assert @vehicle.buy_insurance
418
+ end
419
+
420
+ def test_should_allow_buying_insurance_bang
421
+ assert @vehicle.buy_insurance!
422
+ end
423
+
424
+ def test_should_allow_ignite_buying_insurance_with_skipped_action
425
+ assert @vehicle.buy_insurance!(false)
426
+ assert @vehicle.new_record?
427
+ end
428
+
429
+ def test_should_not_be_insurance_active
430
+ assert !@vehicle.insurance_active?
431
+ end
432
+
433
+ def test_should_not_be_able_to_cancel
434
+ assert !@vehicle.can_cancel_insurance?
435
+ end
436
+
437
+ def test_should_not_allow_cancelling_insurance
438
+ assert !@vehicle.cancel_insurance
439
+ end
440
+ end
441
+
442
+ class VehicleParkedTest < Test::Unit::TestCase
443
+ def setup
444
+ @vehicle = Vehicle.new
445
+ end
446
+
447
+ def test_should_be_in_parked_state
448
+ assert_equal 'parked', @vehicle.state
449
+ end
450
+
451
+ def test_should_not_have_the_seatbelt_on
452
+ assert !@vehicle.seatbelt_on
453
+ end
454
+
455
+ def test_should_not_allow_park
456
+ assert !@vehicle.park
457
+ end
458
+
459
+ def test_should_allow_ignite
460
+ assert @vehicle.ignite
461
+ assert_equal 'idling', @vehicle.state
462
+ end
463
+
464
+ def test_should_not_allow_idle
465
+ assert !@vehicle.idle
466
+ end
467
+
468
+ def test_should_not_allow_shift_up
469
+ assert !@vehicle.shift_up
470
+ end
471
+
472
+ def test_should_not_allow_shift_down
473
+ assert !@vehicle.shift_down
474
+ end
475
+
476
+ def test_should_not_allow_crash
477
+ assert !@vehicle.crash
478
+ end
479
+
480
+ def test_should_not_allow_repair
481
+ assert !@vehicle.repair
482
+ end
483
+
484
+ def test_should_raise_exception_if_repair_not_allowed!
485
+ exception = assert_raise(StateMachine::InvalidTransition) {@vehicle.repair!}
486
+ assert_equal @vehicle, exception.object
487
+ assert_equal Vehicle.state_machine(:state), exception.machine
488
+ assert_equal :repair, exception.event
489
+ assert_equal 'parked', exception.from
490
+ end
491
+ end
492
+
493
+ class VehicleIdlingTest < Test::Unit::TestCase
494
+ def setup
495
+ @vehicle = Vehicle.new
496
+ @vehicle.ignite
497
+ end
498
+
499
+ def test_should_be_in_idling_state
500
+ assert_equal 'idling', @vehicle.state
501
+ end
502
+
503
+ def test_should_be_idling
504
+ assert @vehicle.idling?
505
+ end
506
+
507
+ def test_should_have_seatbelt_on
508
+ assert @vehicle.seatbelt_on
509
+ end
510
+
511
+ def test_should_track_time_elapsed
512
+ assert_not_nil @vehicle.time_elapsed
513
+ end
514
+
515
+ def test_should_allow_park
516
+ assert @vehicle.park
517
+ end
518
+
519
+ def test_should_call_park_with_bang_action
520
+ class << @vehicle
521
+ def park
522
+ super && 1
523
+ end
524
+ end
525
+
526
+ assert_equal 1, @vehicle.park!
527
+ end
528
+
529
+ def test_should_not_allow_idle
530
+ assert !@vehicle.idle
531
+ end
532
+
533
+ def test_should_allow_shift_up
534
+ assert @vehicle.shift_up
535
+ end
536
+
537
+ def test_should_not_allow_shift_down
538
+ assert !@vehicle.shift_down
539
+ end
540
+
541
+ def test_should_not_allow_crash
542
+ assert !@vehicle.crash
543
+ end
544
+
545
+ def test_should_not_allow_repair
546
+ assert !@vehicle.repair
547
+ end
548
+ end
549
+
550
+ class VehicleFirstGearTest < Test::Unit::TestCase
551
+ def setup
552
+ @vehicle = Vehicle.new
553
+ @vehicle.ignite
554
+ @vehicle.shift_up
555
+ end
556
+
557
+ def test_should_be_in_first_gear_state
558
+ assert_equal 'first_gear', @vehicle.state
559
+ end
560
+
561
+ def test_should_be_first_gear
562
+ assert @vehicle.first_gear?
563
+ end
564
+
565
+ def test_should_allow_park
566
+ assert @vehicle.park
567
+ end
568
+
569
+ def test_should_allow_idle
570
+ assert @vehicle.idle
571
+ end
572
+
573
+ def test_should_allow_shift_up
574
+ assert @vehicle.shift_up
575
+ end
576
+
577
+ def test_should_not_allow_shift_down
578
+ assert !@vehicle.shift_down
579
+ end
580
+
581
+ def test_should_allow_crash
582
+ assert @vehicle.crash
583
+ end
584
+
585
+ def test_should_not_allow_repair
586
+ assert !@vehicle.repair
587
+ end
588
+ end
589
+
590
+ class VehicleSecondGearTest < Test::Unit::TestCase
591
+ def setup
592
+ @vehicle = Vehicle.new
593
+ @vehicle.ignite
594
+ 2.times {@vehicle.shift_up}
595
+ end
596
+
597
+ def test_should_be_in_second_gear_state
598
+ assert_equal 'second_gear', @vehicle.state
599
+ end
600
+
601
+ def test_should_be_second_gear
602
+ assert @vehicle.second_gear?
603
+ end
604
+
605
+ def test_should_not_allow_park
606
+ assert !@vehicle.park
607
+ end
608
+
609
+ def test_should_not_allow_idle
610
+ assert !@vehicle.idle
611
+ end
612
+
613
+ def test_should_allow_shift_up
614
+ assert @vehicle.shift_up
615
+ end
616
+
617
+ def test_should_allow_shift_down
618
+ assert @vehicle.shift_down
619
+ end
620
+
621
+ def test_should_allow_crash
622
+ assert @vehicle.crash
623
+ end
624
+
625
+ def test_should_not_allow_repair
626
+ assert !@vehicle.repair
627
+ end
628
+ end
629
+
630
+ class VehicleThirdGearTest < Test::Unit::TestCase
631
+ def setup
632
+ @vehicle = Vehicle.new
633
+ @vehicle.ignite
634
+ 3.times {@vehicle.shift_up}
635
+ end
636
+
637
+ def test_should_be_in_third_gear_state
638
+ assert_equal 'third_gear', @vehicle.state
639
+ end
640
+
641
+ def test_should_be_third_gear
642
+ assert @vehicle.third_gear?
643
+ end
644
+
645
+ def test_should_not_allow_park
646
+ assert !@vehicle.park
647
+ end
648
+
649
+ def test_should_not_allow_idle
650
+ assert !@vehicle.idle
651
+ end
652
+
653
+ def test_should_not_allow_shift_up
654
+ assert !@vehicle.shift_up
655
+ end
656
+
657
+ def test_should_allow_shift_down
658
+ assert @vehicle.shift_down
659
+ end
660
+
661
+ def test_should_allow_crash
662
+ assert @vehicle.crash
663
+ end
664
+
665
+ def test_should_not_allow_repair
666
+ assert !@vehicle.repair
667
+ end
668
+ end
669
+
670
+ class VehicleStalledTest < Test::Unit::TestCase
671
+ def setup
672
+ @vehicle = Vehicle.new
673
+ @vehicle.ignite
674
+ @vehicle.shift_up
675
+ @vehicle.crash
676
+ end
677
+
678
+ def test_should_be_in_stalled_state
679
+ assert_equal 'stalled', @vehicle.state
680
+ end
681
+
682
+ def test_should_be_stalled
683
+ assert @vehicle.stalled?
684
+ end
685
+
686
+ def test_should_be_towed
687
+ assert @vehicle.auto_shop.busy?
688
+ assert_equal 1, @vehicle.auto_shop.num_customers
689
+ end
690
+
691
+ def test_should_have_an_increased_insurance_premium
692
+ assert_equal 150, @vehicle.insurance_premium
693
+ end
694
+
695
+ def test_should_not_allow_park
696
+ assert !@vehicle.park
697
+ end
698
+
699
+ def test_should_allow_ignite
700
+ assert @vehicle.ignite
701
+ end
702
+
703
+ def test_should_not_change_state_when_ignited
704
+ assert_equal 'stalled', @vehicle.state
705
+ end
706
+
707
+ def test_should_not_allow_idle
708
+ assert !@vehicle.idle
709
+ end
710
+
711
+ def test_should_now_allow_shift_up
712
+ assert !@vehicle.shift_up
713
+ end
714
+
715
+ def test_should_not_allow_shift_down
716
+ assert !@vehicle.shift_down
717
+ end
718
+
719
+ def test_should_not_allow_crash
720
+ assert !@vehicle.crash
721
+ end
722
+
723
+ def test_should_allow_repair_if_auto_shop_is_busy
724
+ assert @vehicle.repair
725
+ end
726
+
727
+ def test_should_not_allow_repair_if_auto_shop_is_available
728
+ @vehicle.auto_shop.fix_vehicle
729
+ assert !@vehicle.repair
730
+ end
731
+ end
732
+
733
+ class VehicleRepairedTest < Test::Unit::TestCase
734
+ def setup
735
+ @vehicle = Vehicle.new
736
+ @vehicle.ignite
737
+ @vehicle.shift_up
738
+ @vehicle.crash
739
+ @vehicle.repair
740
+ end
741
+
742
+ def test_should_be_in_parked_state
743
+ assert_equal 'parked', @vehicle.state
744
+ end
745
+
746
+ def test_should_not_have_a_busy_auto_shop
747
+ assert @vehicle.auto_shop.available?
748
+ end
749
+ end
750
+
751
+ class VehicleLockedTest < Test::Unit::TestCase
752
+ def setup
753
+ @vehicle = Vehicle.new
754
+ @vehicle.state = 'locked'
755
+ end
756
+
757
+ def test_should_be_parked_after_park
758
+ @vehicle.park
759
+ assert @vehicle.parked?
760
+ end
761
+
762
+ def test_should_be_parked_after_ignite
763
+ @vehicle.ignite
764
+ assert @vehicle.parked?
765
+ end
766
+
767
+ def test_should_be_parked_after_shift_up
768
+ @vehicle.shift_up
769
+ assert @vehicle.parked?
770
+ end
771
+
772
+ def test_should_be_parked_after_shift_down
773
+ @vehicle.shift_down
774
+ assert @vehicle.parked?
775
+ end
776
+ end
777
+
778
+ class VehicleWithParallelEventsTest < Test::Unit::TestCase
779
+ def setup
780
+ @vehicle = Vehicle.new
781
+ end
782
+
783
+ def test_should_fail_if_any_event_cannot_transition
784
+ assert !@vehicle.fire_events(:ignite, :cancel_insurance)
785
+ end
786
+
787
+ def test_should_be_successful_if_all_events_transition
788
+ assert @vehicle.fire_events(:ignite, :buy_insurance)
789
+ end
790
+
791
+ def test_should_not_save_if_skipping_action
792
+ assert @vehicle.fire_events(:ignite, :buy_insurance, false)
793
+ assert !@vehicle.saved
794
+ end
795
+
796
+ def test_should_raise_exception_if_any_event_cannot_transition_on_bang
797
+ exception = assert_raise(StateMachine::InvalidParallelTransition) { @vehicle.fire_events!(:ignite, :cancel_insurance) }
798
+ assert_equal @vehicle, exception.object
799
+ assert_equal [:ignite, :cancel_insurance], exception.events
800
+ end
801
+
802
+ def test_should_not_raise_exception_if_all_events_transition_on_bang
803
+ assert @vehicle.fire_events!(:ignite, :buy_insurance)
804
+ end
805
+
806
+ def test_should_not_save_if_skipping_action_on_bang
807
+ assert @vehicle.fire_events!(:ignite, :buy_insurance, false)
808
+ assert !@vehicle.saved
809
+ end
810
+ end
811
+
812
+ class VehicleWithEventAttributesTest < Test::Unit::TestCase
813
+ def setup
814
+ @vehicle = Vehicle.new
815
+ @vehicle.state_event = 'ignite'
816
+ end
817
+
818
+ def test_should_fail_if_event_is_invalid
819
+ @vehicle.state_event = 'invalid'
820
+ assert !@vehicle.save
821
+ assert_equal 'parked', @vehicle.state
822
+ end
823
+
824
+ def test_should_fail_if_event_has_no_transition
825
+ @vehicle.state_event = 'park'
826
+ assert !@vehicle.save
827
+ assert_equal 'parked', @vehicle.state
828
+ end
829
+
830
+ def test_should_return_original_action_value_on_success
831
+ assert_equal @vehicle, @vehicle.save
832
+ end
833
+
834
+ def test_should_transition_state_on_success
835
+ @vehicle.save
836
+ assert_equal 'idling', @vehicle.state
837
+ end
838
+ end
839
+
840
+ class MotorcycleTest < Test::Unit::TestCase
841
+ def setup
842
+ @motorcycle = Motorcycle.new
843
+ end
844
+
845
+ def test_should_be_in_idling_state
846
+ assert_equal 'idling', @motorcycle.state
847
+ end
848
+
849
+ def test_should_allow_park
850
+ assert @motorcycle.park
851
+ end
852
+
853
+ def test_should_not_allow_ignite
854
+ assert !@motorcycle.ignite
855
+ end
856
+
857
+ def test_should_allow_shift_up
858
+ assert @motorcycle.shift_up
859
+ end
860
+
861
+ def test_should_not_allow_shift_down
862
+ assert !@motorcycle.shift_down
863
+ end
864
+
865
+ def test_should_not_allow_crash
866
+ assert !@motorcycle.crash
867
+ end
868
+
869
+ def test_should_not_allow_repair
870
+ assert !@motorcycle.repair
871
+ end
872
+
873
+ def test_should_inherit_decibels_from_superclass
874
+ @motorcycle.park
875
+ assert_equal 0.0, @motorcycle.decibels
876
+ end
877
+
878
+ def test_should_use_decibels_defined_in_state
879
+ @motorcycle.shift_up
880
+ assert_equal 1.0, @motorcycle.decibels
881
+ end
882
+ end
883
+
884
+ class CarTest < Test::Unit::TestCase
885
+ def setup
886
+ @car = Car.new
887
+ end
888
+
889
+ def test_should_be_in_parked_state
890
+ assert_equal 'parked', @car.state
891
+ end
892
+
893
+ def test_should_not_have_the_seatbelt_on
894
+ assert !@car.seatbelt_on
895
+ end
896
+
897
+ def test_should_not_allow_park
898
+ assert !@car.park
899
+ end
900
+
901
+ def test_should_allow_ignite
902
+ assert @car.ignite
903
+ assert_equal 'idling', @car.state
904
+ end
905
+
906
+ def test_should_not_allow_idle
907
+ assert !@car.idle
908
+ end
909
+
910
+ def test_should_not_allow_shift_up
911
+ assert !@car.shift_up
912
+ end
913
+
914
+ def test_should_not_allow_shift_down
915
+ assert !@car.shift_down
916
+ end
917
+
918
+ def test_should_not_allow_crash
919
+ assert !@car.crash
920
+ end
921
+
922
+ def test_should_not_allow_repair
923
+ assert !@car.repair
924
+ end
925
+
926
+ def test_should_allow_reverse
927
+ assert @car.reverse
928
+ end
929
+ end
930
+
931
+ class CarBackingUpTest < Test::Unit::TestCase
932
+ def setup
933
+ @car = Car.new
934
+ @car.reverse
935
+ end
936
+
937
+ def test_should_be_in_backing_up_state
938
+ assert_equal 'backing_up', @car.state
939
+ end
940
+
941
+ def test_should_allow_park
942
+ assert @car.park
943
+ end
944
+
945
+ def test_should_not_allow_ignite
946
+ assert !@car.ignite
947
+ end
948
+
949
+ def test_should_allow_idle
950
+ assert @car.idle
951
+ end
952
+
953
+ def test_should_allow_shift_up
954
+ assert @car.shift_up
955
+ end
956
+
957
+ def test_should_not_allow_shift_down
958
+ assert !@car.shift_down
959
+ end
960
+
961
+ def test_should_not_allow_crash
962
+ assert !@car.crash
963
+ end
964
+
965
+ def test_should_not_allow_repair
966
+ assert !@car.repair
967
+ end
968
+
969
+ def test_should_not_allow_reverse
970
+ assert !@car.reverse
971
+ end
972
+ end
973
+
974
+ class AutoShopAvailableTest < Test::Unit::TestCase
975
+ def setup
976
+ @auto_shop = AutoShop.new
977
+ end
978
+
979
+ def test_should_be_in_available_state
980
+ assert_equal 'available', @auto_shop.state
981
+ end
982
+
983
+ def test_should_allow_tow_vehicle
984
+ assert @auto_shop.tow_vehicle
985
+ end
986
+
987
+ def test_should_not_allow_fix_vehicle
988
+ assert !@auto_shop.fix_vehicle
989
+ end
990
+ end
991
+
992
+ class AutoShopBusyTest < Test::Unit::TestCase
993
+ def setup
994
+ @auto_shop = AutoShop.new
995
+ @auto_shop.tow_vehicle
996
+ end
997
+
998
+ def test_should_be_in_busy_state
999
+ assert_equal 'busy', @auto_shop.state
1000
+ end
1001
+
1002
+ def test_should_have_incremented_number_of_customers
1003
+ assert_equal 1, @auto_shop.num_customers
1004
+ end
1005
+
1006
+ def test_should_not_allow_tow_vehicle
1007
+ assert !@auto_shop.tow_vehicle
1008
+ end
1009
+
1010
+ def test_should_allow_fix_vehicle
1011
+ assert @auto_shop.fix_vehicle
1012
+ end
1013
+ end
1014
+
1015
+ class TrafficLightStopTest < Test::Unit::TestCase
1016
+ def setup
1017
+ @light = TrafficLight.new
1018
+ @light.state = 'stop'
1019
+ end
1020
+
1021
+ def test_should_use_stop_color
1022
+ assert_equal 'red', @light.color
1023
+ end
1024
+
1025
+ def test_should_pass_arguments_through
1026
+ assert_equal 'RED', @light.color(:upcase!)
1027
+ end
1028
+
1029
+ def test_should_pass_block_through
1030
+ color = @light.color {|value| value.upcase!}
1031
+ assert_equal 'RED', color
1032
+ end
1033
+
1034
+ def test_should_use_stop_capture_violations
1035
+ assert_equal true, @light.capture_violations?
1036
+ end
1037
+ end
1038
+
1039
+ class TrafficLightProceedTest < Test::Unit::TestCase
1040
+ def setup
1041
+ @light = TrafficLight.new
1042
+ @light.state = 'proceed'
1043
+ end
1044
+
1045
+ def test_should_use_proceed_color
1046
+ assert_equal 'green', @light.color
1047
+ end
1048
+
1049
+ def test_should_use_proceed_capture_violations
1050
+ assert_equal false, @light.capture_violations?
1051
+ end
1052
+ end
1053
+
1054
+ class TrafficLightCautionTest < Test::Unit::TestCase
1055
+ def setup
1056
+ @light = TrafficLight.new
1057
+ @light.state = 'caution'
1058
+ end
1059
+
1060
+ def test_should_use_caution_color
1061
+ assert_equal 'yellow', @light.color
1062
+ end
1063
+
1064
+ def test_should_use_caution_capture_violations
1065
+ assert_equal true, @light.capture_violations?
1066
+ end
1067
+ end