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,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,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,1066 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+
3
+ class AutoShop
4
+ attr_accessor :num_customers
5
+
6
+ def initialize
7
+ @num_customers = 0
8
+ super
9
+ end
10
+
11
+ state_machine :initial => :available do
12
+ after_transition :available => any, :do => :increment_customers
13
+ after_transition :busy => any, :do => :decrement_customers
14
+
15
+ event :tow_vehicle do
16
+ transition :available => :busy
17
+ end
18
+
19
+ event :fix_vehicle do
20
+ transition :busy => :available
21
+ end
22
+ end
23
+
24
+ # Increments the number of customers in service
25
+ def increment_customers
26
+ self.num_customers += 1
27
+ end
28
+
29
+ # Decrements the number of customers in service
30
+ def decrement_customers
31
+ self.num_customers -= 1
32
+ end
33
+ end
34
+
35
+ class ModelBase
36
+ def save
37
+ @saved = true
38
+ self
39
+ end
40
+ end
41
+
42
+ class Vehicle < ModelBase
43
+ attr_accessor :auto_shop, :seatbelt_on, :insurance_premium, :force_idle, :callbacks, :saved, :time_elapsed, :last_transition_args
44
+
45
+ def initialize(attributes = {})
46
+ attributes = {
47
+ :auto_shop => AutoShop.new,
48
+ :seatbelt_on => false,
49
+ :insurance_premium => 50,
50
+ :force_idle => false,
51
+ :callbacks => [],
52
+ :saved => false
53
+ }.merge(attributes)
54
+
55
+ attributes.each {|attr, value| send("#{attr}=", value)}
56
+ super()
57
+ end
58
+
59
+ # Defines the state machine for the state of the vehicled
60
+ state_machine :initial => lambda {|vehicle| vehicle.force_idle ? :idling : :parked}, :action => :save do
61
+ before_transition {|vehicle, transition| vehicle.last_transition_args = transition.args}
62
+ before_transition :parked => any, :do => :put_on_seatbelt
63
+ before_transition any => :stalled, :do => :increase_insurance_premium
64
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.seatbelt_on = false}
65
+ after_transition :on => :crash, :do => :tow
66
+ after_transition :on => :repair, :do => :fix
67
+
68
+ # Callback tracking for initial state callbacks
69
+ after_transition any => :parked, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_parked'}
70
+ before_transition any => :idling, :do => lambda {|vehicle| vehicle.callbacks << 'before_enter_idling'}
71
+
72
+ around_transition do |vehicle, transition, block|
73
+ time = Time.now
74
+ block.call
75
+ vehicle.time_elapsed = Time.now - time
76
+ end
77
+
78
+ event all do
79
+ transition :locked => :parked
80
+ end
81
+
82
+ event :park do
83
+ transition [:idling, :first_gear] => :parked
84
+ end
85
+
86
+ event :ignite do
87
+ transition :stalled => :stalled
88
+ transition :parked => :idling
89
+ end
90
+
91
+ event :idle do
92
+ transition :first_gear => :idling
93
+ end
94
+
95
+ event :shift_up do
96
+ transition :idling => :first_gear, :first_gear => :second_gear, :second_gear => :third_gear
97
+ end
98
+
99
+ event :shift_down do
100
+ transition :third_gear => :second_gear
101
+ transition :second_gear => :first_gear
102
+ end
103
+
104
+ event :crash do
105
+ transition [:first_gear, :second_gear, :third_gear] => :stalled, :if => lambda {|vehicle| vehicle.auto_shop.available?}
106
+ end
107
+
108
+ event :repair do
109
+ transition :stalled => :parked, :if => :auto_shop_busy?
110
+ end
111
+ end
112
+
113
+ state_machine :insurance_state, :initial => :inactive, :namespace => 'insurance' do
114
+ event :buy do
115
+ transition :inactive => :active
116
+ end
117
+
118
+ event :cancel do
119
+ transition :active => :inactive
120
+ end
121
+ end
122
+
123
+ def save
124
+ super
125
+ end
126
+
127
+ def new_record?
128
+ @saved == false
129
+ end
130
+
131
+ def park
132
+ super
133
+ end
134
+
135
+ # Tows the vehicle to the auto shop
136
+ def tow
137
+ auto_shop.tow_vehicle
138
+ end
139
+
140
+ # Fixes the vehicle; it will no longer be in the auto shop
141
+ def fix
142
+ auto_shop.fix_vehicle
143
+ end
144
+
145
+ def decibels
146
+ 0.0
147
+ end
148
+
149
+ private
150
+ # Safety first! Puts on our seatbelt
151
+ def put_on_seatbelt
152
+ self.seatbelt_on = true
153
+ end
154
+
155
+ # We crashed! Increase the insurance premium on the vehicle
156
+ def increase_insurance_premium
157
+ self.insurance_premium += 100
158
+ end
159
+
160
+ # Is the auto shop currently servicing another customer?
161
+ def auto_shop_busy?
162
+ auto_shop.busy?
163
+ end
164
+ end
165
+
166
+ class Car < Vehicle
167
+ state_machine do
168
+ event :reverse do
169
+ transition [:parked, :idling, :first_gear] => :backing_up
170
+ end
171
+
172
+ event :park do
173
+ transition :backing_up => :parked
174
+ end
175
+
176
+ event :idle do
177
+ transition :backing_up => :idling
178
+ end
179
+
180
+ event :shift_up do
181
+ transition :backing_up => :first_gear
182
+ end
183
+ end
184
+ end
185
+
186
+ class Motorcycle < Vehicle
187
+ state_machine :initial => :idling do
188
+ state :first_gear do
189
+ def decibels
190
+ 1.0
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ class TrafficLight
197
+ state_machine :initial => :stop do
198
+ event :cycle do
199
+ transition :stop => :proceed, :proceed => :caution, :caution => :stop
200
+ end
201
+
202
+ state :stop do
203
+ def color(transform)
204
+ value = 'red'
205
+
206
+ if block_given?
207
+ yield value
208
+ else
209
+ value.send(transform)
210
+ end
211
+
212
+ value
213
+ end
214
+ end
215
+
216
+ state all - :proceed do
217
+ def capture_violations?
218
+ true
219
+ end
220
+ end
221
+
222
+ state :proceed do
223
+ def color(transform)
224
+ 'green'
225
+ end
226
+
227
+ def capture_violations?
228
+ false
229
+ end
230
+ end
231
+
232
+ state :caution do
233
+ def color(transform)
234
+ 'yellow'
235
+ end
236
+ end
237
+ end
238
+
239
+ def color(transform = :to_s)
240
+ super
241
+ end
242
+ end
243
+
244
+ class VehicleTest < Test::Unit::TestCase
245
+ def setup
246
+ @vehicle = Vehicle.new
247
+ end
248
+
249
+ def test_should_not_allow_access_to_subclass_events
250
+ assert !@vehicle.respond_to?(:reverse)
251
+ end
252
+
253
+ def test_should_have_human_state_names
254
+ assert_equal 'parked', Vehicle.human_state_name(:parked)
255
+ end
256
+
257
+ def test_should_have_human_state_event_names
258
+ assert_equal 'park', Vehicle.human_state_event_name(:park)
259
+ end
260
+ end
261
+
262
+ class VehicleUnsavedTest < Test::Unit::TestCase
263
+ def setup
264
+ @vehicle = Vehicle.new
265
+ end
266
+
267
+ def test_should_be_in_parked_state
268
+ assert_equal 'parked', @vehicle.state
269
+ end
270
+
271
+ def test_should_raise_exception_if_checking_invalid_state
272
+ assert_raise(IndexError) { @vehicle.state?(:invalid) }
273
+ end
274
+
275
+ def test_should_raise_exception_if_getting_name_of_invalid_state
276
+ @vehicle.state = 'invalid'
277
+ assert_raise(ArgumentError) { @vehicle.state_name }
278
+ end
279
+
280
+ def test_should_be_parked
281
+ assert @vehicle.parked?
282
+ assert @vehicle.state?(:parked)
283
+ assert_equal :parked, @vehicle.state_name
284
+ assert_equal 'parked', @vehicle.human_state_name
285
+ end
286
+
287
+ def test_should_not_be_idling
288
+ assert !@vehicle.idling?
289
+ end
290
+
291
+ def test_should_not_be_first_gear
292
+ assert !@vehicle.first_gear?
293
+ end
294
+
295
+ def test_should_not_be_second_gear
296
+ assert !@vehicle.second_gear?
297
+ end
298
+
299
+ def test_should_not_be_stalled
300
+ assert !@vehicle.stalled?
301
+ end
302
+
303
+ def test_should_not_be_able_to_park
304
+ assert !@vehicle.can_park?
305
+ end
306
+
307
+ def test_should_not_have_a_transition_for_park
308
+ assert_nil @vehicle.park_transition
309
+ end
310
+
311
+ def test_should_not_allow_park
312
+ assert !@vehicle.park
313
+ end
314
+
315
+ def test_should_be_able_to_ignite
316
+ assert @vehicle.can_ignite?
317
+ end
318
+
319
+ def test_should_have_a_transition_for_ignite
320
+ transition = @vehicle.ignite_transition
321
+ assert_not_nil transition
322
+ assert_equal 'parked', transition.from
323
+ assert_equal 'idling', transition.to
324
+ assert_equal :ignite, transition.event
325
+ assert_equal :state, transition.attribute
326
+ assert_equal @vehicle, transition.object
327
+ end
328
+
329
+ def test_should_have_a_list_of_possible_events
330
+ assert_equal [:ignite], @vehicle.state_events
331
+ end
332
+
333
+ def test_should_have_a_list_of_possible_transitions
334
+ assert_equal [{:object => @vehicle, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}], @vehicle.state_transitions.map {|transition| transition.attributes}
335
+ end
336
+
337
+ def test_should_have_a_list_of_possible_paths
338
+ assert_equal [[
339
+ StateMachine::Transition.new(@vehicle, Vehicle.state_machine, :ignite, :parked, :idling),
340
+ StateMachine::Transition.new(@vehicle, Vehicle.state_machine, :shift_up, :idling, :first_gear)
341
+ ]], @vehicle.state_paths(:to => :first_gear)
342
+ end
343
+
344
+ def test_should_allow_generic_event_to_fire
345
+ assert @vehicle.fire_state_event(:ignite)
346
+ assert_equal 'idling', @vehicle.state
347
+ end
348
+
349
+ def test_should_pass_arguments_through_to_generic_event_runner
350
+ @vehicle.fire_state_event(:ignite, 1, 2, 3)
351
+ assert_equal [1, 2, 3], @vehicle.last_transition_args
352
+ end
353
+
354
+ def test_should_allow_skipping_action_through_generic_event_runner
355
+ @vehicle.fire_state_event(:ignite, false)
356
+ assert_equal false, @vehicle.saved
357
+ end
358
+
359
+ def test_should_raise_error_with_invalid_event_through_generic_event_runer
360
+ assert_raise(IndexError) { @vehicle.fire_state_event(:invalid) }
361
+ end
362
+
363
+ def test_should_allow_ignite
364
+ assert @vehicle.ignite
365
+ assert_equal 'idling', @vehicle.state
366
+ end
367
+
368
+ def test_should_allow_ignite_with_skipped_action
369
+ assert @vehicle.ignite(false)
370
+ assert @vehicle.new_record?
371
+ end
372
+
373
+ def test_should_allow_ignite_bang
374
+ assert @vehicle.ignite!
375
+ end
376
+
377
+ def test_should_allow_ignite_bang_with_skipped_action
378
+ assert @vehicle.ignite!(false)
379
+ assert @vehicle.new_record?
380
+ end
381
+
382
+ def test_should_be_saved_after_successful_event
383
+ @vehicle.ignite
384
+ assert !@vehicle.new_record?
385
+ end
386
+
387
+ def test_should_not_allow_idle
388
+ assert !@vehicle.idle
389
+ end
390
+
391
+ def test_should_not_allow_shift_up
392
+ assert !@vehicle.shift_up
393
+ end
394
+
395
+ def test_should_not_allow_shift_down
396
+ assert !@vehicle.shift_down
397
+ end
398
+
399
+ def test_should_not_allow_crash
400
+ assert !@vehicle.crash
401
+ end
402
+
403
+ def test_should_not_allow_repair
404
+ assert !@vehicle.repair
405
+ end
406
+
407
+ def test_should_be_insurance_inactive
408
+ assert @vehicle.insurance_inactive?
409
+ end
410
+
411
+ def test_should_be_able_to_buy
412
+ assert @vehicle.can_buy_insurance?
413
+ end
414
+
415
+ def test_should_allow_buying_insurance
416
+ assert @vehicle.buy_insurance
417
+ end
418
+
419
+ def test_should_allow_buying_insurance_bang
420
+ assert @vehicle.buy_insurance!
421
+ end
422
+
423
+ def test_should_allow_ignite_buying_insurance_with_skipped_action
424
+ assert @vehicle.buy_insurance!(false)
425
+ assert @vehicle.new_record?
426
+ end
427
+
428
+ def test_should_not_be_insurance_active
429
+ assert !@vehicle.insurance_active?
430
+ end
431
+
432
+ def test_should_not_be_able_to_cancel
433
+ assert !@vehicle.can_cancel_insurance?
434
+ end
435
+
436
+ def test_should_not_allow_cancelling_insurance
437
+ assert !@vehicle.cancel_insurance
438
+ end
439
+ end
440
+
441
+ class VehicleParkedTest < Test::Unit::TestCase
442
+ def setup
443
+ @vehicle = Vehicle.new
444
+ end
445
+
446
+ def test_should_be_in_parked_state
447
+ assert_equal 'parked', @vehicle.state
448
+ end
449
+
450
+ def test_should_not_have_the_seatbelt_on
451
+ assert !@vehicle.seatbelt_on
452
+ end
453
+
454
+ def test_should_not_allow_park
455
+ assert !@vehicle.park
456
+ end
457
+
458
+ def test_should_allow_ignite
459
+ assert @vehicle.ignite
460
+ assert_equal 'idling', @vehicle.state
461
+ end
462
+
463
+ def test_should_not_allow_idle
464
+ assert !@vehicle.idle
465
+ end
466
+
467
+ def test_should_not_allow_shift_up
468
+ assert !@vehicle.shift_up
469
+ end
470
+
471
+ def test_should_not_allow_shift_down
472
+ assert !@vehicle.shift_down
473
+ end
474
+
475
+ def test_should_not_allow_crash
476
+ assert !@vehicle.crash
477
+ end
478
+
479
+ def test_should_not_allow_repair
480
+ assert !@vehicle.repair
481
+ end
482
+
483
+ def test_should_raise_exception_if_repair_not_allowed!
484
+ exception = assert_raise(StateMachine::InvalidTransition) {@vehicle.repair!}
485
+ assert_equal @vehicle, exception.object
486
+ assert_equal Vehicle.state_machine(:state), exception.machine
487
+ assert_equal :repair, exception.event
488
+ assert_equal 'parked', exception.from
489
+ end
490
+ end
491
+
492
+ class VehicleIdlingTest < Test::Unit::TestCase
493
+ def setup
494
+ @vehicle = Vehicle.new
495
+ @vehicle.ignite
496
+ end
497
+
498
+ def test_should_be_in_idling_state
499
+ assert_equal 'idling', @vehicle.state
500
+ end
501
+
502
+ def test_should_be_idling
503
+ assert @vehicle.idling?
504
+ end
505
+
506
+ def test_should_have_seatbelt_on
507
+ assert @vehicle.seatbelt_on
508
+ end
509
+
510
+ def test_should_track_time_elapsed
511
+ assert_not_nil @vehicle.time_elapsed
512
+ end
513
+
514
+ def test_should_allow_park
515
+ assert @vehicle.park
516
+ end
517
+
518
+ def test_should_call_park_with_bang_action
519
+ class << @vehicle
520
+ def park
521
+ super && 1
522
+ end
523
+ end
524
+
525
+ assert_equal 1, @vehicle.park!
526
+ end
527
+
528
+ def test_should_not_allow_idle
529
+ assert !@vehicle.idle
530
+ end
531
+
532
+ def test_should_allow_shift_up
533
+ assert @vehicle.shift_up
534
+ end
535
+
536
+ def test_should_not_allow_shift_down
537
+ assert !@vehicle.shift_down
538
+ end
539
+
540
+ def test_should_not_allow_crash
541
+ assert !@vehicle.crash
542
+ end
543
+
544
+ def test_should_not_allow_repair
545
+ assert !@vehicle.repair
546
+ end
547
+ end
548
+
549
+ class VehicleFirstGearTest < Test::Unit::TestCase
550
+ def setup
551
+ @vehicle = Vehicle.new
552
+ @vehicle.ignite
553
+ @vehicle.shift_up
554
+ end
555
+
556
+ def test_should_be_in_first_gear_state
557
+ assert_equal 'first_gear', @vehicle.state
558
+ end
559
+
560
+ def test_should_be_first_gear
561
+ assert @vehicle.first_gear?
562
+ end
563
+
564
+ def test_should_allow_park
565
+ assert @vehicle.park
566
+ end
567
+
568
+ def test_should_allow_idle
569
+ assert @vehicle.idle
570
+ end
571
+
572
+ def test_should_allow_shift_up
573
+ assert @vehicle.shift_up
574
+ end
575
+
576
+ def test_should_not_allow_shift_down
577
+ assert !@vehicle.shift_down
578
+ end
579
+
580
+ def test_should_allow_crash
581
+ assert @vehicle.crash
582
+ end
583
+
584
+ def test_should_not_allow_repair
585
+ assert !@vehicle.repair
586
+ end
587
+ end
588
+
589
+ class VehicleSecondGearTest < Test::Unit::TestCase
590
+ def setup
591
+ @vehicle = Vehicle.new
592
+ @vehicle.ignite
593
+ 2.times {@vehicle.shift_up}
594
+ end
595
+
596
+ def test_should_be_in_second_gear_state
597
+ assert_equal 'second_gear', @vehicle.state
598
+ end
599
+
600
+ def test_should_be_second_gear
601
+ assert @vehicle.second_gear?
602
+ end
603
+
604
+ def test_should_not_allow_park
605
+ assert !@vehicle.park
606
+ end
607
+
608
+ def test_should_not_allow_idle
609
+ assert !@vehicle.idle
610
+ end
611
+
612
+ def test_should_allow_shift_up
613
+ assert @vehicle.shift_up
614
+ end
615
+
616
+ def test_should_allow_shift_down
617
+ assert @vehicle.shift_down
618
+ end
619
+
620
+ def test_should_allow_crash
621
+ assert @vehicle.crash
622
+ end
623
+
624
+ def test_should_not_allow_repair
625
+ assert !@vehicle.repair
626
+ end
627
+ end
628
+
629
+ class VehicleThirdGearTest < Test::Unit::TestCase
630
+ def setup
631
+ @vehicle = Vehicle.new
632
+ @vehicle.ignite
633
+ 3.times {@vehicle.shift_up}
634
+ end
635
+
636
+ def test_should_be_in_third_gear_state
637
+ assert_equal 'third_gear', @vehicle.state
638
+ end
639
+
640
+ def test_should_be_third_gear
641
+ assert @vehicle.third_gear?
642
+ end
643
+
644
+ def test_should_not_allow_park
645
+ assert !@vehicle.park
646
+ end
647
+
648
+ def test_should_not_allow_idle
649
+ assert !@vehicle.idle
650
+ end
651
+
652
+ def test_should_not_allow_shift_up
653
+ assert !@vehicle.shift_up
654
+ end
655
+
656
+ def test_should_allow_shift_down
657
+ assert @vehicle.shift_down
658
+ end
659
+
660
+ def test_should_allow_crash
661
+ assert @vehicle.crash
662
+ end
663
+
664
+ def test_should_not_allow_repair
665
+ assert !@vehicle.repair
666
+ end
667
+ end
668
+
669
+ class VehicleStalledTest < Test::Unit::TestCase
670
+ def setup
671
+ @vehicle = Vehicle.new
672
+ @vehicle.ignite
673
+ @vehicle.shift_up
674
+ @vehicle.crash
675
+ end
676
+
677
+ def test_should_be_in_stalled_state
678
+ assert_equal 'stalled', @vehicle.state
679
+ end
680
+
681
+ def test_should_be_stalled
682
+ assert @vehicle.stalled?
683
+ end
684
+
685
+ def test_should_be_towed
686
+ assert @vehicle.auto_shop.busy?
687
+ assert_equal 1, @vehicle.auto_shop.num_customers
688
+ end
689
+
690
+ def test_should_have_an_increased_insurance_premium
691
+ assert_equal 150, @vehicle.insurance_premium
692
+ end
693
+
694
+ def test_should_not_allow_park
695
+ assert !@vehicle.park
696
+ end
697
+
698
+ def test_should_allow_ignite
699
+ assert @vehicle.ignite
700
+ end
701
+
702
+ def test_should_not_change_state_when_ignited
703
+ assert_equal 'stalled', @vehicle.state
704
+ end
705
+
706
+ def test_should_not_allow_idle
707
+ assert !@vehicle.idle
708
+ end
709
+
710
+ def test_should_now_allow_shift_up
711
+ assert !@vehicle.shift_up
712
+ end
713
+
714
+ def test_should_not_allow_shift_down
715
+ assert !@vehicle.shift_down
716
+ end
717
+
718
+ def test_should_not_allow_crash
719
+ assert !@vehicle.crash
720
+ end
721
+
722
+ def test_should_allow_repair_if_auto_shop_is_busy
723
+ assert @vehicle.repair
724
+ end
725
+
726
+ def test_should_not_allow_repair_if_auto_shop_is_available
727
+ @vehicle.auto_shop.fix_vehicle
728
+ assert !@vehicle.repair
729
+ end
730
+ end
731
+
732
+ class VehicleRepairedTest < Test::Unit::TestCase
733
+ def setup
734
+ @vehicle = Vehicle.new
735
+ @vehicle.ignite
736
+ @vehicle.shift_up
737
+ @vehicle.crash
738
+ @vehicle.repair
739
+ end
740
+
741
+ def test_should_be_in_parked_state
742
+ assert_equal 'parked', @vehicle.state
743
+ end
744
+
745
+ def test_should_not_have_a_busy_auto_shop
746
+ assert @vehicle.auto_shop.available?
747
+ end
748
+ end
749
+
750
+ class VehicleLockedTest < Test::Unit::TestCase
751
+ def setup
752
+ @vehicle = Vehicle.new
753
+ @vehicle.state = 'locked'
754
+ end
755
+
756
+ def test_should_be_parked_after_park
757
+ @vehicle.park
758
+ assert @vehicle.parked?
759
+ end
760
+
761
+ def test_should_be_parked_after_ignite
762
+ @vehicle.ignite
763
+ assert @vehicle.parked?
764
+ end
765
+
766
+ def test_should_be_parked_after_shift_up
767
+ @vehicle.shift_up
768
+ assert @vehicle.parked?
769
+ end
770
+
771
+ def test_should_be_parked_after_shift_down
772
+ @vehicle.shift_down
773
+ assert @vehicle.parked?
774
+ end
775
+ end
776
+
777
+ class VehicleWithParallelEventsTest < Test::Unit::TestCase
778
+ def setup
779
+ @vehicle = Vehicle.new
780
+ end
781
+
782
+ def test_should_fail_if_any_event_cannot_transition
783
+ assert !@vehicle.fire_events(:ignite, :cancel_insurance)
784
+ end
785
+
786
+ def test_should_be_successful_if_all_events_transition
787
+ assert @vehicle.fire_events(:ignite, :buy_insurance)
788
+ end
789
+
790
+ def test_should_not_save_if_skipping_action
791
+ assert @vehicle.fire_events(:ignite, :buy_insurance, false)
792
+ assert !@vehicle.saved
793
+ end
794
+
795
+ def test_should_raise_exception_if_any_event_cannot_transition_on_bang
796
+ exception = assert_raise(StateMachine::InvalidParallelTransition) { @vehicle.fire_events!(:ignite, :cancel_insurance) }
797
+ assert_equal @vehicle, exception.object
798
+ assert_equal [:ignite, :cancel_insurance], exception.events
799
+ end
800
+
801
+ def test_should_not_raise_exception_if_all_events_transition_on_bang
802
+ assert @vehicle.fire_events!(:ignite, :buy_insurance)
803
+ end
804
+
805
+ def test_should_not_save_if_skipping_action_on_bang
806
+ assert @vehicle.fire_events!(:ignite, :buy_insurance, false)
807
+ assert !@vehicle.saved
808
+ end
809
+ end
810
+
811
+ class VehicleWithEventAttributesTest < Test::Unit::TestCase
812
+ def setup
813
+ @vehicle = Vehicle.new
814
+ @vehicle.state_event = 'ignite'
815
+ end
816
+
817
+ def test_should_fail_if_event_is_invalid
818
+ @vehicle.state_event = 'invalid'
819
+ assert !@vehicle.save
820
+ assert_equal 'parked', @vehicle.state
821
+ end
822
+
823
+ def test_should_fail_if_event_has_no_transition
824
+ @vehicle.state_event = 'park'
825
+ assert !@vehicle.save
826
+ assert_equal 'parked', @vehicle.state
827
+ end
828
+
829
+ def test_should_return_original_action_value_on_success
830
+ assert_equal @vehicle, @vehicle.save
831
+ end
832
+
833
+ def test_should_transition_state_on_success
834
+ @vehicle.save
835
+ assert_equal 'idling', @vehicle.state
836
+ end
837
+ end
838
+
839
+ class MotorcycleTest < Test::Unit::TestCase
840
+ def setup
841
+ @motorcycle = Motorcycle.new
842
+ end
843
+
844
+ def test_should_be_in_idling_state
845
+ assert_equal 'idling', @motorcycle.state
846
+ end
847
+
848
+ def test_should_allow_park
849
+ assert @motorcycle.park
850
+ end
851
+
852
+ def test_should_not_allow_ignite
853
+ assert !@motorcycle.ignite
854
+ end
855
+
856
+ def test_should_allow_shift_up
857
+ assert @motorcycle.shift_up
858
+ end
859
+
860
+ def test_should_not_allow_shift_down
861
+ assert !@motorcycle.shift_down
862
+ end
863
+
864
+ def test_should_not_allow_crash
865
+ assert !@motorcycle.crash
866
+ end
867
+
868
+ def test_should_not_allow_repair
869
+ assert !@motorcycle.repair
870
+ end
871
+
872
+ def test_should_inherit_decibels_from_superclass
873
+ @motorcycle.park
874
+ assert_equal 0.0, @motorcycle.decibels
875
+ end
876
+
877
+ def test_should_use_decibels_defined_in_state
878
+ @motorcycle.shift_up
879
+ assert_equal 1.0, @motorcycle.decibels
880
+ end
881
+ end
882
+
883
+ class CarTest < Test::Unit::TestCase
884
+ def setup
885
+ @car = Car.new
886
+ end
887
+
888
+ def test_should_be_in_parked_state
889
+ assert_equal 'parked', @car.state
890
+ end
891
+
892
+ def test_should_not_have_the_seatbelt_on
893
+ assert !@car.seatbelt_on
894
+ end
895
+
896
+ def test_should_not_allow_park
897
+ assert !@car.park
898
+ end
899
+
900
+ def test_should_allow_ignite
901
+ assert @car.ignite
902
+ assert_equal 'idling', @car.state
903
+ end
904
+
905
+ def test_should_not_allow_idle
906
+ assert !@car.idle
907
+ end
908
+
909
+ def test_should_not_allow_shift_up
910
+ assert !@car.shift_up
911
+ end
912
+
913
+ def test_should_not_allow_shift_down
914
+ assert !@car.shift_down
915
+ end
916
+
917
+ def test_should_not_allow_crash
918
+ assert !@car.crash
919
+ end
920
+
921
+ def test_should_not_allow_repair
922
+ assert !@car.repair
923
+ end
924
+
925
+ def test_should_allow_reverse
926
+ assert @car.reverse
927
+ end
928
+ end
929
+
930
+ class CarBackingUpTest < Test::Unit::TestCase
931
+ def setup
932
+ @car = Car.new
933
+ @car.reverse
934
+ end
935
+
936
+ def test_should_be_in_backing_up_state
937
+ assert_equal 'backing_up', @car.state
938
+ end
939
+
940
+ def test_should_allow_park
941
+ assert @car.park
942
+ end
943
+
944
+ def test_should_not_allow_ignite
945
+ assert !@car.ignite
946
+ end
947
+
948
+ def test_should_allow_idle
949
+ assert @car.idle
950
+ end
951
+
952
+ def test_should_allow_shift_up
953
+ assert @car.shift_up
954
+ end
955
+
956
+ def test_should_not_allow_shift_down
957
+ assert !@car.shift_down
958
+ end
959
+
960
+ def test_should_not_allow_crash
961
+ assert !@car.crash
962
+ end
963
+
964
+ def test_should_not_allow_repair
965
+ assert !@car.repair
966
+ end
967
+
968
+ def test_should_not_allow_reverse
969
+ assert !@car.reverse
970
+ end
971
+ end
972
+
973
+ class AutoShopAvailableTest < Test::Unit::TestCase
974
+ def setup
975
+ @auto_shop = AutoShop.new
976
+ end
977
+
978
+ def test_should_be_in_available_state
979
+ assert_equal 'available', @auto_shop.state
980
+ end
981
+
982
+ def test_should_allow_tow_vehicle
983
+ assert @auto_shop.tow_vehicle
984
+ end
985
+
986
+ def test_should_not_allow_fix_vehicle
987
+ assert !@auto_shop.fix_vehicle
988
+ end
989
+ end
990
+
991
+ class AutoShopBusyTest < Test::Unit::TestCase
992
+ def setup
993
+ @auto_shop = AutoShop.new
994
+ @auto_shop.tow_vehicle
995
+ end
996
+
997
+ def test_should_be_in_busy_state
998
+ assert_equal 'busy', @auto_shop.state
999
+ end
1000
+
1001
+ def test_should_have_incremented_number_of_customers
1002
+ assert_equal 1, @auto_shop.num_customers
1003
+ end
1004
+
1005
+ def test_should_not_allow_tow_vehicle
1006
+ assert !@auto_shop.tow_vehicle
1007
+ end
1008
+
1009
+ def test_should_allow_fix_vehicle
1010
+ assert @auto_shop.fix_vehicle
1011
+ end
1012
+ end
1013
+
1014
+ class TrafficLightStopTest < Test::Unit::TestCase
1015
+ def setup
1016
+ @light = TrafficLight.new
1017
+ @light.state = 'stop'
1018
+ end
1019
+
1020
+ def test_should_use_stop_color
1021
+ assert_equal 'red', @light.color
1022
+ end
1023
+
1024
+ def test_should_pass_arguments_through
1025
+ assert_equal 'RED', @light.color(:upcase!)
1026
+ end
1027
+
1028
+ def test_should_pass_block_through
1029
+ color = @light.color {|value| value.upcase!}
1030
+ assert_equal 'RED', color
1031
+ end
1032
+
1033
+ def test_should_use_stop_capture_violations
1034
+ assert_equal true, @light.capture_violations?
1035
+ end
1036
+ end
1037
+
1038
+ class TrafficLightProceedTest < Test::Unit::TestCase
1039
+ def setup
1040
+ @light = TrafficLight.new
1041
+ @light.state = 'proceed'
1042
+ end
1043
+
1044
+ def test_should_use_proceed_color
1045
+ assert_equal 'green', @light.color
1046
+ end
1047
+
1048
+ def test_should_use_proceed_capture_violations
1049
+ assert_equal false, @light.capture_violations?
1050
+ end
1051
+ end
1052
+
1053
+ class TrafficLightCautionTest < Test::Unit::TestCase
1054
+ def setup
1055
+ @light = TrafficLight.new
1056
+ @light.state = 'caution'
1057
+ end
1058
+
1059
+ def test_should_use_caution_color
1060
+ assert_equal 'yellow', @light.color
1061
+ end
1062
+
1063
+ def test_should_use_caution_capture_violations
1064
+ assert_equal true, @light.capture_violations?
1065
+ end
1066
+ end