state_machines 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (454) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +1 -1
  3. data/README.md +18 -13
  4. data/lib/state_machines/branch.rb +81 -79
  5. data/lib/state_machines/callback.rb +22 -21
  6. data/lib/state_machines/eval_helpers.rb +15 -15
  7. data/lib/state_machines/event.rb +31 -28
  8. data/lib/state_machines/event_collection.rb +26 -25
  9. data/lib/state_machines/extensions.rb +2 -2
  10. data/lib/state_machines/helper_module.rb +1 -1
  11. data/lib/state_machines/integrations/base.rb +2 -5
  12. data/lib/state_machines/integrations.rb +9 -13
  13. data/lib/state_machines/machine.rb +386 -379
  14. data/lib/state_machines/machine_collection.rb +11 -10
  15. data/lib/state_machines/macro_methods.rb +100 -100
  16. data/lib/state_machines/matcher.rb +23 -23
  17. data/lib/state_machines/matcher_helpers.rb +11 -11
  18. data/lib/state_machines/node_collection.rb +15 -13
  19. data/lib/state_machines/path.rb +56 -55
  20. data/lib/state_machines/path_collection.rb +38 -38
  21. data/lib/state_machines/state.rb +28 -22
  22. data/lib/state_machines/state_collection.rb +21 -20
  23. data/lib/state_machines/state_context.rb +33 -35
  24. data/lib/state_machines/transition.rb +180 -178
  25. data/lib/state_machines/transition_collection.rb +170 -168
  26. data/lib/state_machines/version.rb +1 -1
  27. metadata +5 -852
  28. data/.gitignore +0 -21
  29. data/.rspec +0 -3
  30. data/.travis.yml +0 -14
  31. data/Changelog.md +0 -16
  32. data/Contributors.md +0 -39
  33. data/Gemfile +0 -8
  34. data/Rakefile +0 -12
  35. data/Testing.md +0 -0
  36. data/state_machines.gemspec +0 -23
  37. data/test/files/integrations/event_on_failure_integration.rb +0 -10
  38. data/test/files/integrations/vehicle.rb +0 -7
  39. data/test/files/models/auto_shop.rb +0 -31
  40. data/test/files/models/car.rb +0 -21
  41. data/test/files/models/model_base.rb +0 -6
  42. data/test/files/models/motorcycle.rb +0 -11
  43. data/test/files/models/traffic_light.rb +0 -47
  44. data/test/files/models/vehicle.rb +0 -127
  45. data/test/files/node.rb +0 -5
  46. data/test/files/switch.rb +0 -15
  47. data/test/functional/auto_shop_available_test.rb +0 -20
  48. data/test/functional/auto_shop_busy_test.rb +0 -25
  49. data/test/functional/car_backing_up_test.rb +0 -45
  50. data/test/functional/car_test.rb +0 -49
  51. data/test/functional/motorcycle_test.rb +0 -46
  52. data/test/functional/traffic_light_caution_test.rb +0 -17
  53. data/test/functional/traffic_light_proceed_test.rb +0 -17
  54. data/test/functional/traffic_light_stop_test.rb +0 -26
  55. data/test/functional/vehicle_first_gear_test.rb +0 -42
  56. data/test/functional/vehicle_idling_test.rb +0 -59
  57. data/test/functional/vehicle_locked_test.rb +0 -29
  58. data/test/functional/vehicle_parked_test.rb +0 -53
  59. data/test/functional/vehicle_repaired_test.rb +0 -20
  60. data/test/functional/vehicle_second_gear_test.rb +0 -42
  61. data/test/functional/vehicle_stalled_test.rb +0 -65
  62. data/test/functional/vehicle_test.rb +0 -20
  63. data/test/functional/vehicle_third_gear_test.rb +0 -42
  64. data/test/functional/vehicle_unsaved_test.rb +0 -181
  65. data/test/functional/vehicle_with_event_attributes_test.rb +0 -30
  66. data/test/functional/vehicle_with_parallel_events_test.rb +0 -36
  67. data/test/test_helper.rb +0 -15
  68. data/test/unit/assertions/assert_exclusive_keys_test.rb +0 -22
  69. data/test/unit/assertions/assert_valid_key_test.rb +0 -12
  70. data/test/unit/branch/branch_test.rb +0 -28
  71. data/test/unit/branch/branch_with_conflicting_conditionals_test.rb +0 -27
  72. data/test/unit/branch/branch_with_conflicting_from_requirements_test.rb +0 -8
  73. data/test/unit/branch/branch_with_conflicting_on_requirements_test.rb +0 -8
  74. data/test/unit/branch/branch_with_conflicting_to_requirements_test.rb +0 -8
  75. data/test/unit/branch/branch_with_different_requirements_test.rb +0 -41
  76. data/test/unit/branch/branch_with_except_from_matcher_requirement_test.rb +0 -8
  77. data/test/unit/branch/branch_with_except_from_requirement_test.rb +0 -36
  78. data/test/unit/branch/branch_with_except_on_matcher_requirement_test.rb +0 -8
  79. data/test/unit/branch/branch_with_except_on_requirement_test.rb +0 -36
  80. data/test/unit/branch/branch_with_except_to_matcher_requirement_test.rb +0 -8
  81. data/test/unit/branch/branch_with_except_to_requirement_test.rb +0 -36
  82. data/test/unit/branch/branch_with_from_matcher_requirement_test.rb +0 -20
  83. data/test/unit/branch/branch_with_from_requirement_test.rb +0 -45
  84. data/test/unit/branch/branch_with_if_conditional_test.rb +0 -27
  85. data/test/unit/branch/branch_with_implicit_and_explicit_requirements_test.rb +0 -23
  86. data/test/unit/branch/branch_with_implicit_from_requirement_matcher_test.rb +0 -20
  87. data/test/unit/branch/branch_with_implicit_requirement_test.rb +0 -20
  88. data/test/unit/branch/branch_with_implicit_to_requirement_matcher_test.rb +0 -16
  89. data/test/unit/branch/branch_with_multiple_except_from_requirements_test.rb +0 -20
  90. data/test/unit/branch/branch_with_multiple_except_on_requirements_test.rb +0 -16
  91. data/test/unit/branch/branch_with_multiple_except_to_requirements_test.rb +0 -20
  92. data/test/unit/branch/branch_with_multiple_from_requirements_test.rb +0 -16
  93. data/test/unit/branch/branch_with_multiple_if_conditionals_test.rb +0 -20
  94. data/test/unit/branch/branch_with_multiple_implicit_requirements_test.rb +0 -53
  95. data/test/unit/branch/branch_with_multiple_on_requirements_test.rb +0 -20
  96. data/test/unit/branch/branch_with_multiple_to_requirements_test.rb +0 -20
  97. data/test/unit/branch/branch_with_multiple_unless_conditionals_test.rb +0 -20
  98. data/test/unit/branch/branch_with_nil_requirements_test.rb +0 -28
  99. data/test/unit/branch/branch_with_no_requirements_test.rb +0 -36
  100. data/test/unit/branch/branch_with_on_matcher_requirement_test.rb +0 -16
  101. data/test/unit/branch/branch_with_on_requirement_test.rb +0 -45
  102. data/test/unit/branch/branch_with_to_matcher_requirement_test.rb +0 -20
  103. data/test/unit/branch/branch_with_to_requirement_test.rb +0 -45
  104. data/test/unit/branch/branch_with_unless_conditional_test.rb +0 -27
  105. data/test/unit/branch/branch_without_guards_test.rb +0 -27
  106. data/test/unit/callback/callback_by_default_test.rb +0 -25
  107. data/test/unit/callback/callback_test.rb +0 -53
  108. data/test/unit/callback/callback_with_application_bound_object_test.rb +0 -23
  109. data/test/unit/callback/callback_with_application_terminator_test.rb +0 -24
  110. data/test/unit/callback/callback_with_arguments_test.rb +0 -14
  111. data/test/unit/callback/callback_with_around_type_and_arguments_test.rb +0 -25
  112. data/test/unit/callback/callback_with_around_type_and_block_test.rb +0 -44
  113. data/test/unit/callback/callback_with_around_type_and_bound_method_test.rb +0 -23
  114. data/test/unit/callback/callback_with_around_type_and_multiple_methods_test.rb +0 -93
  115. data/test/unit/callback/callback_with_around_type_and_terminator_test.rb +0 -17
  116. data/test/unit/callback/callback_with_block_test.rb +0 -20
  117. data/test/unit/callback/callback_with_bound_method_and_arguments_test.rb +0 -28
  118. data/test/unit/callback/callback_with_bound_method_test.rb +0 -35
  119. data/test/unit/callback/callback_with_do_method_test.rb +0 -18
  120. data/test/unit/callback/callback_with_explicit_requirements_test.rb +0 -32
  121. data/test/unit/callback/callback_with_if_condition_test.rb +0 -17
  122. data/test/unit/callback/callback_with_implicit_requirements_test.rb +0 -32
  123. data/test/unit/callback/callback_with_method_argument_test.rb +0 -18
  124. data/test/unit/callback/callback_with_mixed_methods_test.rb +0 -31
  125. data/test/unit/callback/callback_with_multiple_bound_methods_test.rb +0 -21
  126. data/test/unit/callback/callback_with_multiple_do_methods_test.rb +0 -29
  127. data/test/unit/callback/callback_with_multiple_method_arguments_test.rb +0 -29
  128. data/test/unit/callback/callback_with_terminator_test.rb +0 -22
  129. data/test/unit/callback/callback_with_unbound_method_test.rb +0 -14
  130. data/test/unit/callback/callback_with_unless_condition_test.rb +0 -17
  131. data/test/unit/callback/callback_without_arguments_test.rb +0 -14
  132. data/test/unit/callback/callback_without_terminator_test.rb +0 -12
  133. data/test/unit/error/error_by_default_test.rb +0 -21
  134. data/test/unit/error/error_with_message_test.rb +0 -23
  135. data/test/unit/eval_helper/eval_helpers_base_test.rb +0 -8
  136. data/test/unit/eval_helper/eval_helpers_proc_block_and_explicit_arguments_test.rb +0 -14
  137. data/test/unit/eval_helper/eval_helpers_proc_block_and_implicit_arguments_test.rb +0 -14
  138. data/test/unit/eval_helper/eval_helpers_proc_test.rb +0 -13
  139. data/test/unit/eval_helper/eval_helpers_proc_with_arguments_test.rb +0 -13
  140. data/test/unit/eval_helper/eval_helpers_proc_with_block_test.rb +0 -13
  141. data/test/unit/eval_helper/eval_helpers_proc_with_block_without_arguments_test.rb +0 -18
  142. data/test/unit/eval_helper/eval_helpers_proc_with_block_without_object_test.rb +0 -14
  143. data/test/unit/eval_helper/eval_helpers_proc_without_arguments_test.rb +0 -19
  144. data/test/unit/eval_helper/eval_helpers_string_test.rb +0 -25
  145. data/test/unit/eval_helper/eval_helpers_string_with_block_test.rb +0 -12
  146. data/test/unit/eval_helper/eval_helpers_symbol_method_missing_test.rb +0 -20
  147. data/test/unit/eval_helper/eval_helpers_symbol_private_test.rb +0 -17
  148. data/test/unit/eval_helper/eval_helpers_symbol_protected_test.rb +0 -17
  149. data/test/unit/eval_helper/eval_helpers_symbol_tainted_method_test.rb +0 -18
  150. data/test/unit/eval_helper/eval_helpers_symbol_test.rb +0 -16
  151. data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_and_block_test.rb +0 -16
  152. data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_test.rb +0 -16
  153. data/test/unit/eval_helper/eval_helpers_symbol_with_block_test.rb +0 -16
  154. data/test/unit/eval_helper/eval_helpers_test.rb +0 -13
  155. data/test/unit/event/event_after_being_copied_test.rb +0 -17
  156. data/test/unit/event/event_by_default_test.rb +0 -60
  157. data/test/unit/event/event_context_test.rb +0 -16
  158. data/test/unit/event/event_on_failure_test.rb +0 -44
  159. data/test/unit/event/event_test.rb +0 -34
  160. data/test/unit/event/event_transitions_test.rb +0 -62
  161. data/test/unit/event/event_with_conflicting_helpers_after_definition_test.rb +0 -79
  162. data/test/unit/event/event_with_conflicting_helpers_before_definition_test.rb +0 -58
  163. data/test/unit/event/event_with_conflicting_machine_test.rb +0 -48
  164. data/test/unit/event/event_with_dynamic_human_name_test.rb +0 -26
  165. data/test/unit/event/event_with_human_name_test.rb +0 -13
  166. data/test/unit/event/event_with_invalid_current_state_test.rb +0 -30
  167. data/test/unit/event/event_with_machine_action_test.rb +0 -33
  168. data/test/unit/event/event_with_marshalling_test.rb +0 -47
  169. data/test/unit/event/event_with_matching_disabled_transitions_test.rb +0 -115
  170. data/test/unit/event/event_with_matching_enabled_transitions_test.rb +0 -75
  171. data/test/unit/event/event_with_multiple_transitions_test.rb +0 -61
  172. data/test/unit/event/event_with_namespace_test.rb +0 -34
  173. data/test/unit/event/event_with_transition_with_blacklisted_to_state_test.rb +0 -60
  174. data/test/unit/event/event_with_transition_with_loopback_state_test.rb +0 -36
  175. data/test/unit/event/event_with_transition_with_nil_to_state_test.rb +0 -36
  176. data/test/unit/event/event_with_transition_with_whitelisted_to_state_test.rb +0 -51
  177. data/test/unit/event/event_with_transition_without_to_state_test.rb +0 -36
  178. data/test/unit/event/event_with_transitions_test.rb +0 -32
  179. data/test/unit/event/event_without_matching_transitions_test.rb +0 -41
  180. data/test/unit/event/event_without_transitions_test.rb +0 -28
  181. data/test/unit/event/invalid_event_test.rb +0 -20
  182. data/test/unit/event_collection/event_collection_attribute_with_machine_action_test.rb +0 -62
  183. data/test/unit/event_collection/event_collection_attribute_with_namespaced_machine_test.rb +0 -36
  184. data/test/unit/event_collection/event_collection_by_default_test.rb +0 -26
  185. data/test/unit/event_collection/event_collection_test.rb +0 -39
  186. data/test/unit/event_collection/event_collection_with_custom_machine_attribute_test.rb +0 -31
  187. data/test/unit/event_collection/event_collection_with_events_with_transitions_test.rb +0 -76
  188. data/test/unit/event_collection/event_collection_with_multiple_events_test.rb +0 -27
  189. data/test/unit/event_collection/event_collection_with_validations_test.rb +0 -74
  190. data/test/unit/event_collection/event_collection_without_machine_action_test.rb +0 -18
  191. data/test/unit/event_collection/event_string_collection_test.rb +0 -31
  192. data/test/unit/helper_module_test.rb +0 -17
  193. data/test/unit/integrations/integration_finder_test.rb +0 -16
  194. data/test/unit/integrations/integration_matcher_test.rb +0 -27
  195. data/test/unit/invalid_transition/invalid_parallel_transition_test.rb +0 -18
  196. data/test/unit/invalid_transition/invalid_transition_test.rb +0 -47
  197. data/test/unit/invalid_transition/invalid_transition_with_integration_test.rb +0 -45
  198. data/test/unit/invalid_transition/invalid_transition_with_namespace_test.rb +0 -32
  199. data/test/unit/machine/machine_after_being_copied_test.rb +0 -62
  200. data/test/unit/machine/machine_after_changing_initial_state.rb +0 -28
  201. data/test/unit/machine/machine_after_changing_owner_class_test.rb +0 -31
  202. data/test/unit/machine/machine_by_default_test.rb +0 -160
  203. data/test/unit/machine/machine_finder_custom_options_test.rb +0 -17
  204. data/test/unit/machine/machine_finder_with_existing_machine_on_superclass_test.rb +0 -85
  205. data/test/unit/machine/machine_finder_with_existing_on_same_class_test.rb +0 -23
  206. data/test/unit/machine/machine_finder_without_existing_machine_test.rb +0 -25
  207. data/test/unit/machine/machine_persistence_test.rb +0 -52
  208. data/test/unit/machine/machine_state_initialization_test.rb +0 -56
  209. data/test/unit/machine/machine_test.rb +0 -30
  210. data/test/unit/machine/machine_with_action_already_overridden_test.rb +0 -23
  211. data/test/unit/machine/machine_with_action_defined_in_class_test.rb +0 -37
  212. data/test/unit/machine/machine_with_action_defined_in_included_module_test.rb +0 -46
  213. data/test/unit/machine/machine_with_action_defined_in_superclass_test.rb +0 -43
  214. data/test/unit/machine/machine_with_action_undefined_test.rb +0 -33
  215. data/test/unit/machine/machine_with_cached_state_test.rb +0 -20
  216. data/test/unit/machine/machine_with_class_helpers_test.rb +0 -179
  217. data/test/unit/machine/machine_with_conflicting_helpers_after_definition_test.rb +0 -244
  218. data/test/unit/machine/machine_with_conflicting_helpers_before_definition_test.rb +0 -175
  219. data/test/unit/machine/machine_with_custom_action_test.rb +0 -11
  220. data/test/unit/machine/machine_with_custom_attribute_test.rb +0 -103
  221. data/test/unit/machine/machine_with_custom_initialize_test.rb +0 -24
  222. data/test/unit/machine/machine_with_custom_integration_test.rb +0 -72
  223. data/test/unit/machine/machine_with_custom_invalidation_test.rb +0 -39
  224. data/test/unit/machine/machine_with_custom_name_test.rb +0 -57
  225. data/test/unit/machine/machine_with_custom_plural_test.rb +0 -52
  226. data/test/unit/machine/machine_with_dynamic_initial_state_test.rb +0 -65
  227. data/test/unit/machine/machine_with_event_matchers_test.rb +0 -41
  228. data/test/unit/machine/machine_with_events_test.rb +0 -52
  229. data/test/unit/machine/machine_with_events_with_custom_human_names_test.rb +0 -18
  230. data/test/unit/machine/machine_with_events_with_transitions_test.rb +0 -37
  231. data/test/unit/machine/machine_with_existing_event_test.rb +0 -17
  232. data/test/unit/machine/machine_with_existing_machines_on_owner_class_test.rb +0 -20
  233. data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_class_test.rb +0 -71
  234. data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_subclass_test.rb +0 -31
  235. data/test/unit/machine/machine_with_existing_state_test.rb +0 -27
  236. data/test/unit/machine/machine_with_failure_callbacks_test.rb +0 -48
  237. data/test/unit/machine/machine_with_helpers_test.rb +0 -14
  238. data/test/unit/machine/machine_with_initial_state_with_value_and_owner_default.rb +0 -25
  239. data/test/unit/machine/machine_with_initialize_and_super_test.rb +0 -17
  240. data/test/unit/machine/machine_with_initialize_arguments_and_block_test.rb +0 -31
  241. data/test/unit/machine/machine_with_initialize_without_super_test.rb +0 -17
  242. data/test/unit/machine/machine_with_instance_helpers_test.rb +0 -179
  243. data/test/unit/machine/machine_with_integration_test.rb +0 -72
  244. data/test/unit/machine/machine_with_multiple_events_test.rb +0 -32
  245. data/test/unit/machine/machine_with_namespace_test.rb +0 -48
  246. data/test/unit/machine/machine_with_nil_action_test.rb +0 -27
  247. data/test/unit/machine/machine_with_other_states.rb +0 -22
  248. data/test/unit/machine/machine_with_owner_subclass_test.rb +0 -18
  249. data/test/unit/machine/machine_with_paths_test.rb +0 -25
  250. data/test/unit/machine/machine_with_private_action_test.rb +0 -43
  251. data/test/unit/machine/machine_with_state_matchers_test.rb +0 -41
  252. data/test/unit/machine/machine_with_state_with_matchers_test.rb +0 -19
  253. data/test/unit/machine/machine_with_states_test.rb +0 -55
  254. data/test/unit/machine/machine_with_states_with_behaviors_test.rb +0 -23
  255. data/test/unit/machine/machine_with_states_with_custom_human_names_test.rb +0 -18
  256. data/test/unit/machine/machine_with_states_with_custom_values_test.rb +0 -21
  257. data/test/unit/machine/machine_with_states_with_runtime_dependencies_test.rb +0 -19
  258. data/test/unit/machine/machine_with_static_initial_state_test.rb +0 -49
  259. data/test/unit/machine/machine_with_superclass_conflicting_helpers_after_definition_test.rb +0 -36
  260. data/test/unit/machine/machine_with_transition_callbacks_test.rb +0 -144
  261. data/test/unit/machine/machine_with_transitions_test.rb +0 -87
  262. data/test/unit/machine/machine_without_initialization_test.rb +0 -31
  263. data/test/unit/machine/machine_without_initialize_test.rb +0 -14
  264. data/test/unit/machine/machine_without_integration_test.rb +0 -31
  265. data/test/unit/machine_collection/machine_collection_by_default_test.rb +0 -11
  266. data/test/unit/machine_collection/machine_collection_fire_attributes_with_validations_test.rb +0 -72
  267. data/test/unit/machine_collection/machine_collection_fire_test.rb +0 -80
  268. data/test/unit/machine_collection/machine_collection_fire_with_transactions_test.rb +0 -54
  269. data/test/unit/machine_collection/machine_collection_fire_with_validations_test.rb +0 -76
  270. data/test/unit/machine_collection/machine_collection_state_initialization_test.rb +0 -111
  271. data/test/unit/machine_collection/machine_collection_transitions_with_blank_events_test.rb +0 -25
  272. data/test/unit/machine_collection/machine_collection_transitions_with_custom_options_test.rb +0 -20
  273. data/test/unit/machine_collection/machine_collection_transitions_with_different_actions_test.rb +0 -26
  274. data/test/unit/machine_collection/machine_collection_transitions_with_exisiting_transitions_test.rb +0 -25
  275. data/test/unit/machine_collection/machine_collection_transitions_with_invalid_events_test.rb +0 -25
  276. data/test/unit/machine_collection/machine_collection_transitions_with_same_actions_test.rb +0 -31
  277. data/test/unit/machine_collection/machine_collection_transitions_with_transition_test.rb +0 -26
  278. data/test/unit/machine_collection/machine_collection_transitions_without_events_test.rb +0 -25
  279. data/test/unit/machine_collection/machine_collection_transitions_without_transition_test.rb +0 -27
  280. data/test/unit/matcher/all_matcher_test.rb +0 -29
  281. data/test/unit/matcher/blacklist_matcher_test.rb +0 -30
  282. data/test/unit/matcher/loopback_matcher_test.rb +0 -27
  283. data/test/unit/matcher/matcher_by_default_test.rb +0 -15
  284. data/test/unit/matcher/matcher_with_multiple_values_test.rb +0 -15
  285. data/test/unit/matcher/matcher_with_value_test.rb +0 -15
  286. data/test/unit/matcher/whitelist_matcher_test.rb +0 -30
  287. data/test/unit/matcher_helpers/matcher_helpers_all_test.rb +0 -14
  288. data/test/unit/matcher_helpers/matcher_helpers_any_test.rb +0 -14
  289. data/test/unit/matcher_helpers/matcher_helpers_same_test.rb +0 -13
  290. data/test/unit/node_collection/node_collection_after_being_copied_test.rb +0 -46
  291. data/test/unit/node_collection/node_collection_after_update_test.rb +0 -36
  292. data/test/unit/node_collection/node_collection_by_default_test.rb +0 -22
  293. data/test/unit/node_collection/node_collection_test.rb +0 -23
  294. data/test/unit/node_collection/node_collection_with_indices_test.rb +0 -42
  295. data/test/unit/node_collection/node_collection_with_matcher_contexts_test.rb +0 -25
  296. data/test/unit/node_collection/node_collection_with_nodes_test.rb +0 -46
  297. data/test/unit/node_collection/node_collection_with_numeric_index_test.rb +0 -24
  298. data/test/unit/node_collection/node_collection_with_postdefined_contexts_test.rb +0 -22
  299. data/test/unit/node_collection/node_collection_with_predefined_contexts_test.rb +0 -23
  300. data/test/unit/node_collection/node_collection_with_string_index_test.rb +0 -20
  301. data/test/unit/node_collection/node_collection_with_symbol_index_test.rb +0 -20
  302. data/test/unit/node_collection/node_collection_without_indices_test.rb +0 -30
  303. data/test/unit/path/path_by_default_test.rb +0 -54
  304. data/test/unit/path/path_test.rb +0 -14
  305. data/test/unit/path/path_with_available_transitions_after_reaching_target_test.rb +0 -40
  306. data/test/unit/path/path_with_available_transitions_test.rb +0 -54
  307. data/test/unit/path/path_with_deep_target_reached_test.rb +0 -50
  308. data/test/unit/path/path_with_deep_target_test.rb +0 -40
  309. data/test/unit/path/path_with_duplicates_test.rb +0 -32
  310. data/test/unit/path/path_with_encountered_transitions_test.rb +0 -34
  311. data/test/unit/path/path_with_guarded_transitions_test.rb +0 -42
  312. data/test/unit/path/path_with_reached_target_test.rb +0 -35
  313. data/test/unit/path/path_with_transitions_test.rb +0 -54
  314. data/test/unit/path/path_with_unreached_target_test.rb +0 -31
  315. data/test/unit/path/path_without_transitions_test.rb +0 -24
  316. data/test/unit/path_collection/path_collection_by_default_test.rb +0 -46
  317. data/test/unit/path_collection/path_collection_test.rb +0 -24
  318. data/test/unit/path_collection/path_collection_with_deep_paths_test.rb +0 -43
  319. data/test/unit/path_collection/path_collection_with_duplicate_nodes_test.rb +0 -31
  320. data/test/unit/path_collection/path_collection_with_from_state_test.rb +0 -27
  321. data/test/unit/path_collection/path_collection_with_paths_test.rb +0 -47
  322. data/test/unit/path_collection/path_collection_with_to_state_test.rb +0 -29
  323. data/test/unit/path_collection/path_with_guarded_paths_test.rb +0 -25
  324. data/test/unit/state/state_after_being_copied_test.rb +0 -19
  325. data/test/unit/state/state_by_default_test.rb +0 -41
  326. data/test/unit/state/state_final_test.rb +0 -28
  327. data/test/unit/state/state_initial_test.rb +0 -13
  328. data/test/unit/state/state_not_final_test.rb +0 -32
  329. data/test/unit/state/state_not_initial_test.rb +0 -13
  330. data/test/unit/state/state_test.rb +0 -44
  331. data/test/unit/state/state_with_cached_lambda_value_test.rb +0 -29
  332. data/test/unit/state/state_with_conflicting_helpers_after_definition_test.rb +0 -38
  333. data/test/unit/state/state_with_conflicting_helpers_before_definition_test.rb +0 -29
  334. data/test/unit/state/state_with_conflicting_machine_name_test.rb +0 -20
  335. data/test/unit/state/state_with_conflicting_machine_test.rb +0 -37
  336. data/test/unit/state/state_with_context_test.rb +0 -60
  337. data/test/unit/state/state_with_dynamic_human_name_test.rb +0 -25
  338. data/test/unit/state/state_with_existing_context_method_test.rb +0 -24
  339. data/test/unit/state/state_with_human_name_test.rb +0 -13
  340. data/test/unit/state/state_with_integer_value_test.rb +0 -32
  341. data/test/unit/state/state_with_invalid_method_call_test.rb +0 -21
  342. data/test/unit/state/state_with_lambda_value_test.rb +0 -37
  343. data/test/unit/state/state_with_matcher_test.rb +0 -18
  344. data/test/unit/state/state_with_multiple_contexts_test.rb +0 -57
  345. data/test/unit/state/state_with_name_test.rb +0 -43
  346. data/test/unit/state/state_with_namespace_test.rb +0 -22
  347. data/test/unit/state/state_with_nil_value_test.rb +0 -35
  348. data/test/unit/state/state_with_redefined_context_method_test.rb +0 -45
  349. data/test/unit/state/state_with_symbolic_value_test.rb +0 -32
  350. data/test/unit/state/state_with_valid_inherited_method_call_for_current_state_test.rb +0 -40
  351. data/test/unit/state/state_with_valid_method_call_for_current_state_test.rb +0 -33
  352. data/test/unit/state/state_with_valid_method_call_for_different_state_test.rb +0 -41
  353. data/test/unit/state/state_without_cached_lambda_value_test.rb +0 -25
  354. data/test/unit/state/state_without_name_test.rb +0 -39
  355. data/test/unit/state_collection/state_collection_by_default_test.rb +0 -21
  356. data/test/unit/state_collection/state_collection_string_test.rb +0 -35
  357. data/test/unit/state_collection/state_collection_test.rb +0 -74
  358. data/test/unit/state_collection/state_collection_with_custom_state_values_test.rb +0 -29
  359. data/test/unit/state_collection/state_collection_with_event_transitions_test.rb +0 -39
  360. data/test/unit/state_collection/state_collection_with_initial_state_test.rb +0 -40
  361. data/test/unit/state_collection/state_collection_with_namespace_test.rb +0 -21
  362. data/test/unit/state_collection/state_collection_with_state_behaviors_test.rb +0 -40
  363. data/test/unit/state_collection/state_collection_with_state_matchers_test.rb +0 -29
  364. data/test/unit/state_collection/state_collection_with_transition_callbacks_test.rb +0 -40
  365. data/test/unit/state_context/state_context_proxy_test.rb +0 -26
  366. data/test/unit/state_context/state_context_proxy_with_if_and_unless_conditions_test.rb +0 -42
  367. data/test/unit/state_context/state_context_proxy_with_if_condition_test.rb +0 -64
  368. data/test/unit/state_context/state_context_proxy_with_multiple_if_conditions_test.rb +0 -32
  369. data/test/unit/state_context/state_context_proxy_with_multiple_unless_conditions_test.rb +0 -32
  370. data/test/unit/state_context/state_context_proxy_with_unless_condition_test.rb +0 -64
  371. data/test/unit/state_context/state_context_proxy_without_conditions_test.rb +0 -31
  372. data/test/unit/state_context/state_context_test.rb +0 -28
  373. data/test/unit/state_context/state_context_transition_test.rb +0 -104
  374. data/test/unit/state_context/state_context_with_matching_transition_test.rb +0 -27
  375. data/test/unit/state_machine/state_machine_by_default_test.rb +0 -12
  376. data/test/unit/state_machine/state_machine_test.rb +0 -20
  377. data/test/unit/transition/transition_after_being_performed_test.rb +0 -48
  378. data/test/unit/transition/transition_after_being_persisted_test.rb +0 -46
  379. data/test/unit/transition/transition_after_being_rolled_back_test.rb +0 -35
  380. data/test/unit/transition/transition_equality_test.rb +0 -52
  381. data/test/unit/transition/transition_loopback_test.rb +0 -18
  382. data/test/unit/transition/transition_test.rb +0 -96
  383. data/test/unit/transition/transition_transient_test.rb +0 -20
  384. data/test/unit/transition/transition_with_action_test.rb +0 -27
  385. data/test/unit/transition/transition_with_after_callbacks_skipped_test.rb +0 -127
  386. data/test/unit/transition/transition_with_after_callbacks_test.rb +0 -93
  387. data/test/unit/transition/transition_with_around_callbacks_test.rb +0 -141
  388. data/test/unit/transition/transition_with_before_callbacks_skipped_test.rb +0 -30
  389. data/test/unit/transition/transition_with_before_callbacks_test.rb +0 -104
  390. data/test/unit/transition/transition_with_custom_machine_attribute_test.rb +0 -28
  391. data/test/unit/transition/transition_with_different_states_test.rb +0 -18
  392. data/test/unit/transition/transition_with_dynamic_to_value_test.rb +0 -19
  393. data/test/unit/transition/transition_with_failure_callbacks_test.rb +0 -84
  394. data/test/unit/transition/transition_with_invalid_nodes_test.rb +0 -29
  395. data/test/unit/transition/transition_with_mixed_callbacks_test.rb +0 -105
  396. data/test/unit/transition/transition_with_multiple_after_callbacks_test.rb +0 -40
  397. data/test/unit/transition/transition_with_multiple_around_callbacks_test.rb +0 -114
  398. data/test/unit/transition/transition_with_multiple_before_callbacks_test.rb +0 -40
  399. data/test/unit/transition/transition_with_multiple_failure_callbacks_test.rb +0 -40
  400. data/test/unit/transition/transition_with_namespace_test.rb +0 -47
  401. data/test/unit/transition/transition_with_perform_arguments_test.rb +0 -35
  402. data/test/unit/transition/transition_with_transactions_test.rb +0 -42
  403. data/test/unit/transition/transition_without_callbacks_test.rb +0 -33
  404. data/test/unit/transition/transition_without_reading_state_test.rb +0 -22
  405. data/test/unit/transition/transition_without_running_action_test.rb +0 -47
  406. data/test/unit/transition_collection/attribute_transition_collection_by_default_test.rb +0 -23
  407. data/test/unit/transition_collection/attribute_transition_collection_marshalling_test.rb +0 -64
  408. data/test/unit/transition_collection/attribute_transition_collection_with_action_error_test.rb +0 -44
  409. data/test/unit/transition_collection/attribute_transition_collection_with_action_failed_test.rb +0 -44
  410. data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_error_test.rb +0 -32
  411. data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_halt_test.rb +0 -33
  412. data/test/unit/transition_collection/attribute_transition_collection_with_around_after_yield_callback_error_test.rb +0 -32
  413. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_error_test.rb +0 -32
  414. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_halt_test.rb +0 -33
  415. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_before_yield_halt_test.rb +0 -33
  416. data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_error_test.rb +0 -32
  417. data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_halt_test.rb +0 -33
  418. data/test/unit/transition_collection/attribute_transition_collection_with_callbacks_test.rb +0 -68
  419. data/test/unit/transition_collection/attribute_transition_collection_with_event_transitions_test.rb +0 -41
  420. data/test/unit/transition_collection/attribute_transition_collection_with_events_test.rb +0 -44
  421. data/test/unit/transition_collection/attribute_transition_collection_with_skipped_after_callbacks_test.rb +0 -42
  422. data/test/unit/transition_collection/transition_collection_by_default_test.rb +0 -23
  423. data/test/unit/transition_collection/transition_collection_empty_with_block_test.rb +0 -23
  424. data/test/unit/transition_collection/transition_collection_empty_without_block_test.rb +0 -12
  425. data/test/unit/transition_collection/transition_collection_invalid_test.rb +0 -21
  426. data/test/unit/transition_collection/transition_collection_partial_invalid_test.rb +0 -69
  427. data/test/unit/transition_collection/transition_collection_test.rb +0 -26
  428. data/test/unit/transition_collection/transition_collection_valid_test.rb +0 -57
  429. data/test/unit/transition_collection/transition_collection_with_action_error_test.rb +0 -66
  430. data/test/unit/transition_collection/transition_collection_with_action_failed_test.rb +0 -60
  431. data/test/unit/transition_collection/transition_collection_with_action_hook_and_block_test.rb +0 -17
  432. data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_action_test.rb +0 -17
  433. data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_after_callbacks_test.rb +0 -37
  434. data/test/unit/transition_collection/transition_collection_with_action_hook_base_test.rb +0 -34
  435. data/test/unit/transition_collection/transition_collection_with_action_hook_error_test.rb +0 -29
  436. data/test/unit/transition_collection/transition_collection_with_action_hook_invalid_test.rb +0 -17
  437. data/test/unit/transition_collection/transition_collection_with_action_hook_multiple_test.rb +0 -79
  438. data/test/unit/transition_collection/transition_collection_with_action_hook_test.rb +0 -45
  439. data/test/unit/transition_collection/transition_collection_with_action_hook_with_different_actions_test.rb +0 -48
  440. data/test/unit/transition_collection/transition_collection_with_action_hook_with_nil_action_test.rb +0 -42
  441. data/test/unit/transition_collection/transition_collection_with_after_callback_halt_test.rb +0 -51
  442. data/test/unit/transition_collection/transition_collection_with_before_callback_halt_test.rb +0 -47
  443. data/test/unit/transition_collection/transition_collection_with_block_test.rb +0 -46
  444. data/test/unit/transition_collection/transition_collection_with_callbacks_test.rb +0 -135
  445. data/test/unit/transition_collection/transition_collection_with_different_actions_test.rb +0 -189
  446. data/test/unit/transition_collection/transition_collection_with_duplicate_actions_test.rb +0 -48
  447. data/test/unit/transition_collection/transition_collection_with_empty_actions_test.rb +0 -41
  448. data/test/unit/transition_collection/transition_collection_with_mixed_actions_test.rb +0 -41
  449. data/test/unit/transition_collection/transition_collection_with_skipped_actions_and_block_test.rb +0 -34
  450. data/test/unit/transition_collection/transition_collection_with_skipped_actions_test.rb +0 -69
  451. data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_and_around_callbacks_test.rb +0 -53
  452. data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_test.rb +0 -34
  453. data/test/unit/transition_collection/transition_collection_with_transactions_test.rb +0 -65
  454. data/test/unit/transition_collection/transition_collection_without_transactions_test.rb +0 -29
@@ -2,327 +2,327 @@ module StateMachines
2
2
  # Represents a state machine for a particular attribute. State machines
3
3
  # consist of states, events and a set of transitions that define how the
4
4
  # state changes after a particular event is fired.
5
- #
5
+ #
6
6
  # A state machine will not know all of the possible states for an object
7
7
  # unless they are referenced *somewhere* in the state machine definition.
8
8
  # As a result, any unused states should be defined with the +other_states+
9
9
  # or +state+ helper.
10
- #
10
+ #
11
11
  # == Actions
12
- #
12
+ #
13
13
  # When an action is configured for a state machine, it is invoked when an
14
14
  # object transitions via an event. The success of the event becomes
15
15
  # dependent on the success of the action. If the action is successful, then
16
16
  # the transitioned state remains persisted. However, if the action fails
17
17
  # (by returning false), the transitioned state will be rolled back.
18
- #
18
+ #
19
19
  # For example,
20
- #
20
+ #
21
21
  # class Vehicle
22
22
  # attr_accessor :fail, :saving_state
23
- #
23
+ #
24
24
  # state_machine :initial => :parked, :action => :save do
25
25
  # event :ignite do
26
26
  # transition :parked => :idling
27
27
  # end
28
- #
28
+ #
29
29
  # event :park do
30
30
  # transition :idling => :parked
31
31
  # end
32
32
  # end
33
- #
33
+ #
34
34
  # def save
35
35
  # @saving_state = state
36
36
  # fail != true
37
37
  # end
38
38
  # end
39
- #
39
+ #
40
40
  # vehicle = Vehicle.new # => #<Vehicle:0xb7c27024 @state="parked">
41
41
  # vehicle.save # => true
42
42
  # vehicle.saving_state # => "parked" # The state was "parked" was save was called
43
- #
43
+ #
44
44
  # # Successful event
45
45
  # vehicle.ignite # => true
46
46
  # vehicle.saving_state # => "idling" # The state was "idling" when save was called
47
47
  # vehicle.state # => "idling"
48
- #
48
+ #
49
49
  # # Failed event
50
50
  # vehicle.fail = true
51
51
  # vehicle.park # => false
52
52
  # vehicle.saving_state # => "parked"
53
53
  # vehicle.state # => "idling"
54
- #
54
+ #
55
55
  # As shown, even though the state is set prior to calling the +save+ action
56
56
  # on the object, it will be rolled back to the original state if the action
57
57
  # fails. *Note* that this will also be the case if an exception is raised
58
58
  # while calling the action.
59
- #
59
+ #
60
60
  # === Indirect transitions
61
- #
61
+ #
62
62
  # In addition to the action being run as the _result_ of an event, the action
63
63
  # can also be used to run events itself. For example, using the above as an
64
64
  # example:
65
- #
65
+ #
66
66
  # vehicle = Vehicle.new # => #<Vehicle:0xb7c27024 @state="parked">
67
- #
67
+ #
68
68
  # vehicle.state_event = 'ignite'
69
69
  # vehicle.save # => true
70
70
  # vehicle.state # => "idling"
71
71
  # vehicle.state_event # => nil
72
- #
72
+ #
73
73
  # As can be seen, the +save+ action automatically invokes the event stored in
74
74
  # the +state_event+ attribute (<tt>:ignite</tt> in this case).
75
- #
75
+ #
76
76
  # One important note about using this technique for running transitions is
77
77
  # that if the class in which the state machine is defined *also* defines the
78
78
  # action being invoked (and not a superclass), then it must manually run the
79
79
  # StateMachine hook that checks for event attributes.
80
- #
80
+ #
81
81
  # For example, in ActiveRecord, DataMapper, Mongoid, MongoMapper, and Sequel,
82
82
  # the default action (+save+) is already defined in a base class. As a result,
83
83
  # when a state machine is defined in a model / resource, StateMachine can
84
84
  # automatically hook into the +save+ action.
85
- #
85
+ #
86
86
  # On the other hand, the Vehicle class from above defined its own +save+
87
87
  # method (and there is no +save+ method in its superclass). As a result, it
88
88
  # must be modified like so:
89
- #
89
+ #
90
90
  # def save
91
91
  # self.class.state_machines.transitions(self, :save).perform do
92
92
  # @saving_state = state
93
93
  # fail != true
94
94
  # end
95
95
  # end
96
- #
96
+ #
97
97
  # This will add in the functionality for firing the event stored in the
98
98
  # +state_event+ attribute.
99
- #
99
+ #
100
100
  # == Callbacks
101
- #
101
+ #
102
102
  # Callbacks are supported for hooking before and after every possible
103
103
  # transition in the machine. Each callback is invoked in the order in which
104
104
  # it was defined. See StateMachines::Machine#before_transition and
105
105
  # StateMachines::Machine#after_transition for documentation on how to define
106
106
  # new callbacks.
107
- #
107
+ #
108
108
  # *Note* that callbacks only get executed within the context of an event. As
109
109
  # a result, if a class has an initial state when it's created, any callbacks
110
110
  # that would normally get executed when the object enters that state will
111
111
  # *not* get triggered.
112
- #
112
+ #
113
113
  # For example,
114
- #
114
+ #
115
115
  # class Vehicle
116
- # state_machine :initial => :parked do
116
+ # state_machine initial: :parked do
117
117
  # after_transition all => :parked do
118
118
  # raise ArgumentError
119
119
  # end
120
120
  # ...
121
121
  # end
122
122
  # end
123
- #
123
+ #
124
124
  # vehicle = Vehicle.new # => #<Vehicle id: 1, state: "parked">
125
125
  # vehicle.save # => true (no exception raised)
126
- #
126
+ #
127
127
  # If you need callbacks to get triggered when an object is created, this
128
128
  # should be done by one of the following techniques:
129
129
  # * Use a <tt>before :create</tt> or equivalent hook:
130
- #
130
+ #
131
131
  # class Vehicle
132
132
  # before :create, :track_initial_transition
133
- #
133
+ #
134
134
  # state_machine do
135
135
  # ...
136
136
  # end
137
137
  # end
138
- #
138
+ #
139
139
  # * Set an initial state and use the correct event to create the
140
140
  # object with the proper state, resulting in callbacks being triggered and
141
141
  # the object getting persisted (note that the <tt>:pending</tt> state is
142
142
  # actually stored as nil):
143
- #
143
+ #
144
144
  # class Vehicle
145
- # state_machine :initial => :pending
146
- # after_transition :pending => :parked, :do => :track_initial_transition
147
- #
145
+ # state_machine initial: :pending
146
+ # after_transition pending: :parked, do: :track_initial_transition
147
+ #
148
148
  # event :park do
149
- # transition :pending => :parked
149
+ # transition pending: :parked
150
150
  # end
151
- #
152
- # state :pending, :value => nil
151
+ #
152
+ # state :pending, value: nil
153
153
  # end
154
154
  # end
155
- #
155
+ #
156
156
  # vehicle = Vehicle.new
157
157
  # vehicle.park
158
- #
158
+ #
159
159
  # * Use a default event attribute that will automatically trigger when the
160
160
  # configured action gets run (note that the <tt>:pending</tt> state is
161
161
  # actually stored as nil):
162
- #
162
+ #
163
163
  # class Vehicle < ActiveRecord::Base
164
- # state_machine :initial => :pending
165
- # after_transition :pending => :parked, :do => :track_initial_transition
166
- #
164
+ # state_machine initial: :pending
165
+ # after_transition pending: :parked, do: :track_initial_transition
166
+ #
167
167
  # event :park do
168
- # transition :pending => :parked
168
+ # transition pending: :parked
169
169
  # end
170
- #
171
- # state :pending, :value => nil
170
+ #
171
+ # state :pending, value: nil
172
172
  # end
173
- #
173
+ #
174
174
  # def initialize(*)
175
175
  # super
176
176
  # self.state_event = 'park'
177
177
  # end
178
178
  # end
179
- #
179
+ #
180
180
  # vehicle = Vehicle.new
181
181
  # vehicle.save
182
- #
182
+ #
183
183
  # === Canceling callbacks
184
- #
184
+ #
185
185
  # Callbacks can be canceled by throwing :halt at any point during the
186
186
  # callback. For example,
187
- #
187
+ #
188
188
  # ...
189
189
  # throw :halt
190
190
  # ...
191
- #
191
+ #
192
192
  # If a +before+ callback halts the chain, the associated transition and all
193
193
  # later callbacks are canceled. If an +after+ callback halts the chain,
194
194
  # the later callbacks are canceled, but the transition is still successful.
195
- #
195
+ #
196
196
  # These same rules apply to +around+ callbacks with the exception that any
197
197
  # +around+ callback that doesn't yield will essentially result in :halt being
198
198
  # thrown. Any code executed after the yield will behave in the same way as
199
199
  # +after+ callbacks.
200
- #
200
+ #
201
201
  # *Note* that if a +before+ callback fails and the bang version of an event
202
202
  # was invoked, an exception will be raised instead of returning false. For
203
203
  # example,
204
- #
204
+ #
205
205
  # class Vehicle
206
206
  # state_machine :initial => :parked do
207
207
  # before_transition any => :idling, :do => lambda {|vehicle| throw :halt}
208
208
  # ...
209
209
  # end
210
210
  # end
211
- #
211
+ #
212
212
  # vehicle = Vehicle.new
213
213
  # vehicle.park # => false
214
214
  # vehicle.park! # => StateMachines::InvalidTransition: Cannot transition state via :park from "idling"
215
- #
215
+ #
216
216
  # == Observers
217
- #
217
+ #
218
218
  # Observers, in the sense of external classes and *not* Ruby's Observable
219
219
  # mechanism, can hook into state machines as well. Such observers use the
220
220
  # same callback api that's used internally.
221
- #
221
+ #
222
222
  # Below are examples of defining observers for the following state machine:
223
- #
223
+ #
224
224
  # class Vehicle
225
225
  # state_machine do
226
226
  # event :park do
227
- # transition :idling => :parked
227
+ # transition idling: :parked
228
228
  # end
229
229
  # ...
230
230
  # end
231
231
  # ...
232
232
  # end
233
- #
233
+ #
234
234
  # Event/Transition behaviors:
235
- #
235
+ #
236
236
  # class VehicleObserver
237
237
  # def self.before_park(vehicle, transition)
238
238
  # logger.info "#{vehicle} instructed to park... state is: #{transition.from}, state will be: #{transition.to}"
239
239
  # end
240
- #
240
+ #
241
241
  # def self.after_park(vehicle, transition, result)
242
242
  # logger.info "#{vehicle} instructed to park... state was: #{transition.from}, state is: #{transition.to}"
243
243
  # end
244
- #
244
+ #
245
245
  # def self.before_transition(vehicle, transition)
246
246
  # logger.info "#{vehicle} instructed to #{transition.event}... #{transition.attribute} is: #{transition.from}, #{transition.attribute} will be: #{transition.to}"
247
247
  # end
248
- #
248
+ #
249
249
  # def self.after_transition(vehicle, transition)
250
250
  # logger.info "#{vehicle} instructed to #{transition.event}... #{transition.attribute} was: #{transition.from}, #{transition.attribute} is: #{transition.to}"
251
251
  # end
252
- #
252
+ #
253
253
  # def self.around_transition(vehicle, transition)
254
254
  # logger.info Benchmark.measure { yield }
255
255
  # end
256
256
  # end
257
- #
257
+ #
258
258
  # Vehicle.state_machine do
259
259
  # before_transition :on => :park, :do => VehicleObserver.method(:before_park)
260
260
  # before_transition VehicleObserver.method(:before_transition)
261
- #
261
+ #
262
262
  # after_transition :on => :park, :do => VehicleObserver.method(:after_park)
263
263
  # after_transition VehicleObserver.method(:after_transition)
264
- #
264
+ #
265
265
  # around_transition VehicleObserver.method(:around_transition)
266
266
  # end
267
- #
267
+ #
268
268
  # One common callback is to record transitions for all models in the system
269
269
  # for auditing/debugging purposes. Below is an example of an observer that
270
270
  # can easily automate this process for all models:
271
- #
271
+ #
272
272
  # class StateMachineObserver
273
273
  # def self.before_transition(object, transition)
274
274
  # Audit.log_transition(object.attributes)
275
275
  # end
276
276
  # end
277
- #
277
+ #
278
278
  # [Vehicle, Switch, Project].each do |klass|
279
279
  # klass.state_machines.each do |attribute, machine|
280
280
  # machine.before_transition StateMachineObserver.method(:before_transition)
281
281
  # end
282
282
  # end
283
- #
283
+ #
284
284
  # Additional observer-like behavior may be exposed by the various integrations
285
285
  # available. See below for more information on integrations.
286
- #
286
+ #
287
287
  # == Overriding instance / class methods
288
- #
288
+ #
289
289
  # Hooking in behavior to the generated instance / class methods from the
290
290
  # state machine, events, and states is very simple because of the way these
291
291
  # methods are generated on the class. Using the class's ancestors, the
292
292
  # original generated method can be referred to via +super+. For example,
293
- #
293
+ #
294
294
  # class Vehicle
295
295
  # state_machine do
296
296
  # event :park do
297
297
  # ...
298
298
  # end
299
299
  # end
300
- #
300
+ #
301
301
  # def park(*args)
302
302
  # logger.info "..."
303
303
  # super
304
304
  # end
305
305
  # end
306
- #
306
+ #
307
307
  # In the above example, the +park+ instance method that's generated on the
308
308
  # Vehicle class (by the associated event) is overridden with custom behavior.
309
309
  # Once this behavior is complete, the original method from the state machine
310
310
  # is invoked by simply calling +super+.
311
- #
311
+ #
312
312
  # The same technique can be used for +state+, +state_name+, and all other
313
313
  # instance *and* class methods on the Vehicle class.
314
314
  #
315
315
  # == Method conflicts
316
- #
316
+ #
317
317
  # By default state_machine does not redefine methods that exist on
318
318
  # superclasses (*including* Object) or any modules (*including* Kernel) that
319
319
  # were included before it was defined. This is in order to ensure that
320
320
  # existing behavior on the class is not broken by the inclusion of
321
321
  # state_machine.
322
- #
322
+ #
323
323
  # If a conflicting method is detected, state_machine will generate a warning.
324
324
  # For example, consider the following class:
325
- #
325
+ #
326
326
  # class Vehicle
327
327
  # state_machine do
328
328
  # event :open do
@@ -330,67 +330,67 @@ module StateMachines
330
330
  # end
331
331
  # end
332
332
  # end
333
- #
333
+ #
334
334
  # In the above class, an event named "open" is defined for its state machine.
335
335
  # However, "open" is already defined as an instance method in Ruby's Kernel
336
336
  # module that gets included in every Object. As a result, state_machine will
337
337
  # generate the following warning:
338
- #
338
+ #
339
339
  # Instance method "open" is already defined in Object, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true.
340
- #
340
+ #
341
341
  # Even though you may not be using Kernel's implementation of the "open"
342
342
  # instance method, state_machine isn't aware of this and, as a result, stays
343
343
  # safe and just skips redefining the method.
344
- #
344
+ #
345
345
  # As with almost all helpers methods defined by state_machine in your class,
346
346
  # there are generic methods available for working around this method conflict.
347
347
  # In the example above, you can invoke the "open" event like so:
348
- #
348
+ #
349
349
  # vehicle = Vehicle.new # => #<Vehicle:0xb72686b4 @state=nil>
350
350
  # vehicle.fire_events(:open) # => true
351
- #
351
+ #
352
352
  # # This will not work
353
353
  # vehicle.open # => NoMethodError: private method `open' called for #<Vehicle:0xb72686b4 @state=nil>
354
- #
354
+ #
355
355
  # If you want to take on the risk of overriding existing methods and just
356
356
  # ignore method conflicts altogether, you can do so by setting the following
357
357
  # configuration:
358
- #
358
+ #
359
359
  # StateMachines::Machine.ignore_method_conflicts = true
360
- #
360
+ #
361
361
  # This will allow you to define events like "open" as described above and
362
362
  # still generate the "open" instance helper method. For example:
363
- #
363
+ #
364
364
  # StateMachines::Machine.ignore_method_conflicts = true
365
- #
365
+ #
366
366
  # class Vehicle
367
367
  # state_machine do
368
368
  # event :open do
369
369
  # ...
370
370
  # end
371
371
  # end
372
- #
372
+ #
373
373
  # vehicle = Vehicle.new # => #<Vehicle:0xb72686b4 @state=nil>
374
374
  # vehicle.open # => true
375
- #
375
+ #
376
376
  # By default, state_machine helps prevent you from making mistakes and
377
377
  # accidentally overriding methods that you didn't intend to. Once you
378
378
  # understand this and what the consequences are, setting the
379
379
  # +ignore_method_conflicts+ option is a perfectly reasonable workaround.
380
- #
380
+ #
381
381
  # == Integrations
382
- #
382
+ #
383
383
  # By default, state machines are library-agnostic, meaning that they work
384
384
  # on any Ruby class and have no external dependencies. However, there are
385
385
  # certain libraries which expose additional behavior that can be taken
386
386
  # advantage of by state machines.
387
- #
387
+ #
388
388
  # This library is built to work out of the box with a few popular Ruby
389
389
  # libraries that allow for additional behavior to provide a cleaner and
390
390
  # smoother experience. This is especially the case for objects backed by a
391
391
  # database that may allow for transactions, persistent storage,
392
392
  # search/filters, callbacks, etc.
393
- #
393
+ #
394
394
  # When a state machine is defined for classes using any of the above libraries,
395
395
  # it will try to automatically determine the integration to use (Agnostic,
396
396
  # ActiveModel, ActiveRecord, DataMapper, Mongoid, MongoMapper, or Sequel)
@@ -405,12 +405,12 @@ module StateMachines
405
405
  class << self
406
406
  # Attempts to find or create a state machine for the given class. For
407
407
  # example,
408
- #
408
+ #
409
409
  # StateMachines::Machine.find_or_create(Vehicle)
410
410
  # StateMachines::Machine.find_or_create(Vehicle, :initial => :parked)
411
411
  # StateMachines::Machine.find_or_create(Vehicle, :status)
412
412
  # StateMachines::Machine.find_or_create(Vehicle, :status, :initial => :parked)
413
- #
413
+ #
414
414
  # If a machine of the given name already exists in one of the class's
415
415
  # superclasses, then a copy of that machine will be created and stored
416
416
  # in the new owner class (the original will remain unchanged).
@@ -419,7 +419,11 @@ module StateMachines
419
419
  name = args.first || :state
420
420
 
421
421
  # Find an existing machine
422
- if owner_class.respond_to?(:state_machines) && machine = owner_class.state_machines[name]
422
+ machine = owner_class.respond_to?(:state_machines) &&
423
+ (args.first && owner_class.state_machines[name] || !args.first &&
424
+ owner_class.state_machines.values.first) || nil
425
+
426
+ if machine
423
427
  # Only create a new copy if changes are being made to the machine in
424
428
  # a subclass
425
429
  if machine.owner_class != owner_class && (options.any? || block_given?)
@@ -448,9 +452,9 @@ module StateMachines
448
452
  attr_accessor :ignore_method_conflicts
449
453
  end
450
454
  @default_messages = {
451
- :invalid => 'is invalid',
452
- :invalid_event => 'cannot transition when %s',
453
- :invalid_transition => 'cannot transition via "%1$s"'
455
+ invalid: 'is invalid',
456
+ invalid_event: 'cannot transition when %s',
457
+ invalid_transition: 'cannot transition via "%1$s"'
454
458
  }
455
459
 
456
460
  # Whether to ignore any conflicts that are detected for helper methods that
@@ -475,12 +479,12 @@ module StateMachines
475
479
  # * Event transitions (:to, :from, and :except_from options)
476
480
  # * Transition callbacks (:to, :from, :except_to, and :except_from options)
477
481
  # * Unreferenced states (using +other_states+ helper)
478
- #
482
+ #
479
483
  # These are sorted, by default, in the order in which they were referenced.
480
484
  attr_reader :states
481
485
 
482
486
  # The callbacks to invoke before/after a transition is performed
483
- #
487
+ #
484
488
  # Maps :before => callbacks and :after => callbacks
485
489
  attr_reader :callbacks
486
490
 
@@ -513,14 +517,14 @@ module StateMachines
513
517
  end
514
518
 
515
519
  # Add machine-wide defaults
516
- options = {:use_transactions => true, :initialize => true}.merge(options)
520
+ options = {use_transactions: true, initialize: true}.merge(options)
517
521
 
518
522
  # Set machine configuration
519
523
  @name = args.first || :state
520
524
  @attribute = options[:attribute] || @name
521
525
  @events = EventCollection.new(self)
522
526
  @states = StateCollection.new(self)
523
- @callbacks = {:before => [], :after => [], :failure => []}
527
+ @callbacks = {before: [], after: [], failure: []}
524
528
  @namespace = options[:namespace]
525
529
  @messages = options[:messages] || {}
526
530
  @action = options[:action]
@@ -552,7 +556,7 @@ module StateMachines
552
556
  @events.machine = self
553
557
  @states = @states.dup
554
558
  @states.machine = self
555
- @callbacks = {:before => @callbacks[:before].dup, :after => @callbacks[:after].dup, :failure => @callbacks[:failure].dup}
559
+ @callbacks = {before: @callbacks[:before].dup, after: @callbacks[:after].dup, failure: @callbacks[:failure].dup}
556
560
  end
557
561
 
558
562
  # Sets the class which is the owner of this state machine. Any methods
@@ -562,7 +566,7 @@ module StateMachines
562
566
  @owner_class = klass
563
567
 
564
568
  # Create modules for extending the class with state/event-specific methods
565
- @helper_modules = helper_modules = {:instance => HelperModule.new(self, :instance), :class => HelperModule.new(self, :class)}
569
+ @helper_modules = helper_modules = {instance: HelperModule.new(self, :instance), class: HelperModule.new(self, :class)}
566
570
  owner_class.class_eval do
567
571
  extend helper_modules[:class]
568
572
  include helper_modules[:instance]
@@ -608,35 +612,35 @@ module StateMachines
608
612
  # Gets the initial state of the machine for the given object. If a dynamic
609
613
  # initial state was configured for this machine, then the object will be
610
614
  # passed into the lambda block to help determine the actual state.
611
- #
615
+ #
612
616
  # == Examples
613
- #
617
+ #
614
618
  # With a static initial state:
615
- #
619
+ #
616
620
  # class Vehicle
617
621
  # state_machine :initial => :parked do
618
622
  # ...
619
623
  # end
620
624
  # end
621
- #
625
+ #
622
626
  # vehicle = Vehicle.new
623
627
  # Vehicle.state_machine.initial_state(vehicle) # => #<StateMachines::State name=:parked value="parked" initial=true>
624
- #
628
+ #
625
629
  # With a dynamic initial state:
626
- #
630
+ #
627
631
  # class Vehicle
628
632
  # attr_accessor :force_idle
629
- #
633
+ #
630
634
  # state_machine :initial => lambda {|vehicle| vehicle.force_idle ? :idling : :parked} do
631
635
  # ...
632
636
  # end
633
637
  # end
634
- #
638
+ #
635
639
  # vehicle = Vehicle.new
636
- #
640
+ #
637
641
  # vehicle.force_idle = true
638
642
  # Vehicle.state_machine.initial_state(vehicle) # => #<StateMachines::State name=:idling value="idling" initial=false>
639
- #
643
+ #
640
644
  # vehicle.force_idle = false
641
645
  # Vehicle.state_machine.initial_state(vehicle) # => #<StateMachines::State name=:parked value="parked" initial=false>
642
646
  def initial_state(object)
@@ -650,7 +654,7 @@ module StateMachines
650
654
 
651
655
  # Initializes the state on the given object. Initial values are only set if
652
656
  # the machine's attribute hasn't been previously initialized.
653
- #
657
+ #
654
658
  # Configuration options:
655
659
  # * <tt>:force</tt> - Whether to initialize the state regardless of its
656
660
  # current value
@@ -661,7 +665,7 @@ module StateMachines
661
665
  if state && (options[:force] || initialize_state?(object))
662
666
  value = state.value
663
667
 
664
- if hash = options[:to]
668
+ if (hash = options[:to])
665
669
  hash[attribute.to_s] = value
666
670
  else
667
671
  write(object, :state, value)
@@ -678,61 +682,61 @@ module StateMachines
678
682
  # Defines a new helper method in an instance or class scope with the given
679
683
  # name. If the method is already defined in the scope, then this will not
680
684
  # override it.
681
- #
685
+ #
682
686
  # If passing in a block, there are two side effects to be aware of
683
687
  # 1. The method cannot be chained, meaning that the block cannot call +super+
684
688
  # 2. If the method is already defined in an ancestor, then it will not get
685
689
  # overridden and a warning will be output.
686
- #
690
+ #
687
691
  # Example:
688
- #
692
+ #
689
693
  # # Instance helper
690
694
  # machine.define_helper(:instance, :state_name) do |machine, object|
691
695
  # machine.states.match(object).name
692
696
  # end
693
- #
697
+ #
694
698
  # # Class helper
695
699
  # machine.define_helper(:class, :state_machine_name) do |machine, klass|
696
700
  # "State"
697
701
  # end
698
- #
702
+ #
699
703
  # You can also define helpers using string evaluation like so:
700
- #
704
+ #
701
705
  # # Instance helper
702
706
  # machine.define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
703
707
  # def state_name
704
708
  # self.class.state_machine(:state).states.match(self).name
705
709
  # end
706
710
  # end_eval
707
- #
711
+ #
708
712
  # # Class helper
709
713
  # machine.define_helper :class, <<-end_eval, __FILE__, __LINE__ + 1
710
714
  # def state_machine_name
711
715
  # "State"
712
716
  # end
713
717
  # end_eval
714
- def define_helper(scope, method, *args, &block)
718
+ def define_helper(scope, method, *args, **kwargs, &block)
715
719
  helper_module = @helper_modules.fetch(scope)
716
720
 
717
721
  if block_given?
718
- if !self.class.ignore_method_conflicts && conflicting_ancestor = owner_class_ancestor_has_method?(scope, method)
722
+ if !self.class.ignore_method_conflicts && (conflicting_ancestor = owner_class_ancestor_has_method?(scope, method))
719
723
  ancestor_name = conflicting_ancestor.name && !conflicting_ancestor.name.empty? ? conflicting_ancestor.name : conflicting_ancestor.to_s
720
724
  warn "#{scope == :class ? 'Class' : 'Instance'} method \"#{method}\" is already defined in #{ancestor_name}, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true."
721
725
  else
722
726
  name = self.name
723
727
  helper_module.class_eval do
724
- define_method(method) do |*block_args|
725
- block.call((scope == :instance ? self.class : self).state_machine(name), self, *block_args)
728
+ define_method(method) do |*block_args, **block_kwargs|
729
+ block.call((scope == :instance ? self.class : self).state_machine(name), self, *block_args, **block_kwargs)
726
730
  end
727
731
  end
728
732
  end
729
733
  else
730
- helper_module.class_eval(method, *args)
734
+ helper_module.class_eval(method, *args, **kwargs)
731
735
  end
732
736
  end
733
737
 
734
738
  # Customizes the definition of one or more states in the machine.
735
- #
739
+ #
736
740
  # Configuration options:
737
741
  # * <tt>:value</tt> - The actual value to store when an object transitions
738
742
  # to the state. Default is the name (stringified).
@@ -744,13 +748,13 @@ module StateMachines
744
748
  # * <tt>:human_name</tt> - The human-readable version of this state's name.
745
749
  # By default, this is either defined by the integration or stringifies the
746
750
  # name and converts underscores to spaces.
747
- #
751
+ #
748
752
  # == Customizing the stored value
749
- #
753
+ #
750
754
  # Whenever a state is automatically discovered in the state machine, its
751
755
  # default value is assumed to be the stringified version of the name. For
752
756
  # example,
753
- #
757
+ #
754
758
  # class Vehicle
755
759
  # state_machine :initial => :parked do
756
760
  # event :ignite do
@@ -758,124 +762,124 @@ module StateMachines
758
762
  # end
759
763
  # end
760
764
  # end
761
- #
765
+ #
762
766
  # In the above state machine, there are two states automatically discovered:
763
767
  # :parked and :idling. These states, by default, will store their stringified
764
768
  # equivalents when an object moves into that state (e.g. "parked" / "idling").
765
- #
769
+ #
766
770
  # For legacy systems or when tying state machines into existing frameworks,
767
771
  # it's oftentimes necessary to need to store a different value for a state
768
772
  # than the default. In order to continue taking advantage of an expressive
769
773
  # state machine and helper methods, every defined state can be re-configured
770
774
  # with a custom stored value. For example,
771
- #
775
+ #
772
776
  # class Vehicle
773
777
  # state_machine :initial => :parked do
774
778
  # event :ignite do
775
779
  # transition :parked => :idling
776
780
  # end
777
- #
781
+ #
778
782
  # state :idling, :value => 'IDLING'
779
783
  # state :parked, :value => 'PARKED
780
784
  # end
781
785
  # end
782
- #
786
+ #
783
787
  # This is also useful if being used in association with a database and,
784
788
  # instead of storing the state name in a column, you want to store the
785
789
  # state's foreign key:
786
- #
790
+ #
787
791
  # class VehicleState < ActiveRecord::Base
788
792
  # end
789
- #
793
+ #
790
794
  # class Vehicle < ActiveRecord::Base
791
795
  # state_machine :attribute => :state_id, :initial => :parked do
792
796
  # event :ignite do
793
797
  # transition :parked => :idling
794
798
  # end
795
- #
799
+ #
796
800
  # states.each do |state|
797
801
  # self.state(state.name, :value => lambda { VehicleState.find_by_name(state.name.to_s).id }, :cache => true)
798
802
  # end
799
803
  # end
800
804
  # end
801
- #
805
+ #
802
806
  # In the above example, each known state is configured to store it's
803
807
  # associated database id in the +state_id+ attribute. Also, notice that a
804
808
  # lambda block is used to define the state's value. This is required in
805
809
  # situations (like testing) where the model is loaded without any existing
806
810
  # data (i.e. no VehicleState records available).
807
- #
811
+ #
808
812
  # One caveat to the above example is to keep performance in mind. To avoid
809
813
  # constant db hits for looking up the VehicleState ids, the value is cached
810
814
  # by specifying the <tt>:cache</tt> option. Alternatively, a custom
811
815
  # caching strategy can be used like so:
812
- #
816
+ #
813
817
  # class VehicleState < ActiveRecord::Base
814
818
  # cattr_accessor :cache_store
815
819
  # self.cache_store = ActiveSupport::Cache::MemoryStore.new
816
- #
820
+ #
817
821
  # def self.find_by_name(name)
818
822
  # cache_store.fetch(name) { find(:first, :conditions => {:name => name}) }
819
823
  # end
820
824
  # end
821
- #
825
+ #
822
826
  # === Dynamic values
823
- #
827
+ #
824
828
  # In addition to customizing states with other value types, lambda blocks
825
829
  # can also be specified to allow for a state's value to be determined
826
830
  # dynamically at runtime. For example,
827
- #
831
+ #
828
832
  # class Vehicle
829
833
  # state_machine :purchased_at, :initial => :available do
830
834
  # event :purchase do
831
835
  # transition all => :purchased
832
836
  # end
833
- #
837
+ #
834
838
  # event :restock do
835
839
  # transition all => :available
836
840
  # end
837
- #
841
+ #
838
842
  # state :available, :value => nil
839
843
  # state :purchased, :if => lambda {|value| !value.nil?}, :value => lambda {Time.now}
840
844
  # end
841
845
  # end
842
- #
846
+ #
843
847
  # In the above definition, the <tt>:purchased</tt> state is customized with
844
848
  # both a dynamic value *and* a value matcher.
845
- #
849
+ #
846
850
  # When an object transitions to the purchased state, the value's lambda
847
851
  # block will be called. This will get the current time and store it in the
848
852
  # object's +purchased_at+ attribute.
849
- #
853
+ #
850
854
  # *Note* that the custom matcher is very important here. Since there's no
851
855
  # way for the state machine to figure out an object's state when it's set to
852
856
  # a runtime value, it must be explicitly defined. If the <tt>:if</tt> option
853
857
  # were not configured for the state, then an ArgumentError exception would
854
858
  # be raised at runtime, indicating that the state machine could not figure
855
859
  # out what the current state of the object was.
856
- #
860
+ #
857
861
  # == Behaviors
858
- #
862
+ #
859
863
  # Behaviors define a series of methods to mixin with objects when the current
860
864
  # state matches the given one(s). This allows instance methods to behave
861
865
  # a specific way depending on what the value of the object's state is.
862
- #
866
+ #
863
867
  # For example,
864
- #
868
+ #
865
869
  # class Vehicle
866
870
  # attr_accessor :driver
867
871
  # attr_accessor :passenger
868
- #
872
+ #
869
873
  # state_machine :initial => :parked do
870
874
  # event :ignite do
871
875
  # transition :parked => :idling
872
876
  # end
873
- #
877
+ #
874
878
  # state :parked do
875
879
  # def speed
876
880
  # 0
877
881
  # end
878
- #
882
+ #
879
883
  # def rotate_driver
880
884
  # driver = self.driver
881
885
  # self.driver = passenger
@@ -883,97 +887,97 @@ module StateMachines
883
887
  # true
884
888
  # end
885
889
  # end
886
- #
890
+ #
887
891
  # state :idling, :first_gear do
888
892
  # def speed
889
893
  # 20
890
894
  # end
891
- #
895
+ #
892
896
  # def rotate_driver
893
897
  # self.state = 'parked'
894
898
  # rotate_driver
895
899
  # end
896
900
  # end
897
- #
901
+ #
898
902
  # other_states :backing_up
899
903
  # end
900
904
  # end
901
- #
905
+ #
902
906
  # In the above example, there are two dynamic behaviors defined for the
903
907
  # class:
904
908
  # * +speed+
905
909
  # * +rotate_driver+
906
- #
910
+ #
907
911
  # Each of these behaviors are instance methods on the Vehicle class. However,
908
912
  # which method actually gets invoked is based on the current state of the
909
913
  # object. Using the above class as the example:
910
- #
914
+ #
911
915
  # vehicle = Vehicle.new
912
916
  # vehicle.driver = 'John'
913
917
  # vehicle.passenger = 'Jane'
914
- #
918
+ #
915
919
  # # Behaviors in the "parked" state
916
920
  # vehicle.state # => "parked"
917
921
  # vehicle.speed # => 0
918
922
  # vehicle.rotate_driver # => true
919
923
  # vehicle.driver # => "Jane"
920
924
  # vehicle.passenger # => "John"
921
- #
925
+ #
922
926
  # vehicle.ignite # => true
923
- #
927
+ #
924
928
  # # Behaviors in the "idling" state
925
929
  # vehicle.state # => "idling"
926
930
  # vehicle.speed # => 20
927
931
  # vehicle.rotate_driver # => true
928
932
  # vehicle.driver # => "John"
929
933
  # vehicle.passenger # => "Jane"
930
- #
934
+ #
931
935
  # As can be seen, both the +speed+ and +rotate_driver+ instance method
932
936
  # implementations changed how they behave based on what the current state
933
937
  # of the vehicle was.
934
- #
938
+ #
935
939
  # === Invalid behaviors
936
- #
940
+ #
937
941
  # If a specific behavior has not been defined for a state, then a
938
942
  # NoMethodError exception will be raised, indicating that that method would
939
943
  # not normally exist for an object with that state.
940
- #
944
+ #
941
945
  # Using the example from before:
942
- #
946
+ #
943
947
  # vehicle = Vehicle.new
944
948
  # vehicle.state = 'backing_up'
945
949
  # vehicle.speed # => NoMethodError: undefined method 'speed' for #<Vehicle:0xb7d296ac> in state "backing_up"
946
- #
950
+ #
947
951
  # === Using matchers
948
- #
952
+ #
949
953
  # The +all+ / +any+ matchers can be used to easily define behaviors for a
950
954
  # group of states. Note, however, that you cannot use these matchers to
951
955
  # set configurations for states. Behaviors using these matchers can be
952
956
  # defined at any point in the state machine and will always get applied to
953
957
  # the proper states.
954
- #
958
+ #
955
959
  # For example:
956
- #
960
+ #
957
961
  # state_machine :initial => :parked do
958
962
  # ...
959
- #
963
+ #
960
964
  # state all - [:parked, :idling, :stalled] do
961
965
  # validates_presence_of :speed
962
- #
966
+ #
963
967
  # def speed
964
968
  # gear * 10
965
969
  # end
966
970
  # end
967
971
  # end
968
- #
972
+ #
969
973
  # == State-aware class methods
970
- #
974
+ #
971
975
  # In addition to defining scopes for instance methods that are state-aware,
972
976
  # the same can be done for certain types of class methods.
973
- #
977
+ #
974
978
  # Some libraries have support for class-level methods that only run certain
975
979
  # behaviors based on a conditions hash passed in. For example:
976
- #
980
+ #
977
981
  # class Vehicle < ActiveRecord::Base
978
982
  # state_machine do
979
983
  # ...
@@ -983,19 +987,19 @@ module StateMachines
983
987
  # end
984
988
  # end
985
989
  # end
986
- #
990
+ #
987
991
  # In the above ActiveRecord model, two validations have been defined which
988
992
  # will *only* run when the Vehicle object is in one of the three states:
989
993
  # +first_gear+, +second_gear+, or +third_gear. Notice, also, that if/unless
990
994
  # conditions can continue to be used.
991
- #
995
+ #
992
996
  # This functionality is not library-specific and can work for any class-level
993
997
  # method that is defined like so:
994
- #
998
+ #
995
999
  # def validates_presence_of(attribute, options = {})
996
1000
  # ...
997
1001
  # end
998
- #
1002
+ #
999
1003
  # The minimum requirement is that the last argument in the method be an
1000
1004
  # options hash which contains at least <tt>:if</tt> condition support.
1001
1005
  def state(*names, &block)
@@ -1010,6 +1014,7 @@ module StateMachines
1010
1014
  # Add any states referenced in the matcher. When matchers are used,
1011
1015
  # states are not allowed to be configured.
1012
1016
  raise ArgumentError, "Cannot configure states when using matchers (using #{options.inspect})" if options.any?
1017
+
1013
1018
  states = add_states(names.first.values)
1014
1019
  else
1015
1020
  states = add_states(names)
@@ -1033,15 +1038,15 @@ module StateMachines
1033
1038
  alias_method :other_states, :state
1034
1039
 
1035
1040
  # Gets the current value stored in the given object's attribute.
1036
- #
1041
+ #
1037
1042
  # For example,
1038
- #
1043
+ #
1039
1044
  # class Vehicle
1040
1045
  # state_machine :initial => :parked do
1041
1046
  # ...
1042
1047
  # end
1043
1048
  # end
1044
- #
1049
+ #
1045
1050
  # vehicle = Vehicle.new # => #<Vehicle:0xb7d94ab0 @state="parked">
1046
1051
  # Vehicle.state_machine.read(vehicle, :state) # => "parked" # Equivalent to vehicle.state
1047
1052
  # Vehicle.state_machine.read(vehicle, :event) # => nil # Equivalent to vehicle.state_event
@@ -1055,15 +1060,15 @@ module StateMachines
1055
1060
  end
1056
1061
 
1057
1062
  # Sets a new value in the given object's attribute.
1058
- #
1063
+ #
1059
1064
  # For example,
1060
- #
1065
+ #
1061
1066
  # class Vehicle
1062
1067
  # state_machine :initial => :parked do
1063
1068
  # ...
1064
1069
  # end
1065
1070
  # end
1066
- #
1071
+ #
1067
1072
  # vehicle = Vehicle.new # => #<Vehicle:0xb7d94ab0 @state="parked">
1068
1073
  # Vehicle.state_machine.write(vehicle, :state, 'idling') # => Equivalent to vehicle.state = 'idling'
1069
1074
  # Vehicle.state_machine.write(vehicle, :event, 'park') # => Equivalent to vehicle.state_event = 'park'
@@ -1076,17 +1081,17 @@ module StateMachines
1076
1081
 
1077
1082
  # Defines one or more events for the machine and the transitions that can
1078
1083
  # be performed when those events are run.
1079
- #
1084
+ #
1080
1085
  # This method is also aliased as +on+ for improved compatibility with
1081
1086
  # using a domain-specific language.
1082
- #
1087
+ #
1083
1088
  # Configuration options:
1084
1089
  # * <tt>:human_name</tt> - The human-readable version of this event's name.
1085
1090
  # By default, this is either defined by the integration or stringifies the
1086
1091
  # name and converts underscores to spaces.
1087
- #
1092
+ #
1088
1093
  # == Instance methods
1089
- #
1094
+ #
1090
1095
  # The following instance methods are generated when a new event is defined
1091
1096
  # (the "park" event is used as an example):
1092
1097
  # * <tt>park(..., run_action = true)</tt> - Fires the "park" event,
@@ -1110,13 +1115,13 @@ module StateMachines
1110
1115
  # object or nil if no transitions can be performed. Like <tt>can_park?</tt>
1111
1116
  # this will also *not* run validations or callbacks. It will only
1112
1117
  # determine if the state machine defines a valid transition for the event.
1113
- #
1118
+ #
1114
1119
  # With a namespace of "car", the above names map to the following methods:
1115
1120
  # * <tt>can_park_car?</tt>
1116
1121
  # * <tt>park_car_transition</tt>
1117
1122
  # * <tt>park_car</tt>
1118
1123
  # * <tt>park_car!</tt>
1119
- #
1124
+ #
1120
1125
  # The <tt>can_park?</tt> and <tt>park_transition</tt> helpers both take an
1121
1126
  # optional set of requirements for determining what transitions are available
1122
1127
  # for the current object. These requirements include:
@@ -1126,58 +1131,58 @@ module StateMachines
1126
1131
  # specified, then this will match any to state.
1127
1132
  # * <tt>:guard</tt> - Whether to guard transitions with the if/unless
1128
1133
  # conditionals defined for each one. Default is true.
1129
- #
1134
+ #
1130
1135
  # == Defining transitions
1131
- #
1136
+ #
1132
1137
  # +event+ requires a block which allows you to define the possible
1133
1138
  # transitions that can happen as a result of that event. For example,
1134
- #
1139
+ #
1135
1140
  # event :park, :stop do
1136
1141
  # transition :idling => :parked
1137
1142
  # end
1138
- #
1143
+ #
1139
1144
  # event :first_gear do
1140
1145
  # transition :parked => :first_gear, :if => :seatbelt_on?
1141
1146
  # transition :parked => same # Allow to loopback if seatbelt is off
1142
1147
  # end
1143
- #
1148
+ #
1144
1149
  # See StateMachines::Event#transition for more information on
1145
1150
  # the possible options that can be passed in.
1146
- #
1151
+ #
1147
1152
  # *Note* that this block is executed within the context of the actual event
1148
1153
  # object. As a result, you will not be able to reference any class methods
1149
1154
  # on the model without referencing the class itself. For example,
1150
- #
1155
+ #
1151
1156
  # class Vehicle
1152
1157
  # def self.safe_states
1153
1158
  # [:parked, :idling, :stalled]
1154
1159
  # end
1155
- #
1160
+ #
1156
1161
  # state_machine do
1157
1162
  # event :park do
1158
1163
  # transition Vehicle.safe_states => :parked
1159
1164
  # end
1160
1165
  # end
1161
- # end
1162
- #
1166
+ # end
1167
+ #
1163
1168
  # == Overriding the event method
1164
- #
1169
+ #
1165
1170
  # By default, this will define an instance method (with the same name as the
1166
1171
  # event) that will fire the next possible transition for that. Although the
1167
1172
  # +before_transition+, +after_transition+, and +around_transition+ hooks
1168
1173
  # allow you to define behavior that gets executed as a result of the event's
1169
1174
  # transition, you can also override the event method in order to have a
1170
1175
  # little more fine-grained control.
1171
- #
1176
+ #
1172
1177
  # For example:
1173
- #
1178
+ #
1174
1179
  # class Vehicle
1175
1180
  # state_machine do
1176
1181
  # event :park do
1177
1182
  # ...
1178
1183
  # end
1179
1184
  # end
1180
- #
1185
+ #
1181
1186
  # def park(*)
1182
1187
  # take_deep_breath # Executes before the transition (and before_transition hooks) even if no transition is possible
1183
1188
  # if result = super # Runs the transition and all before/after/around hooks
@@ -1186,33 +1191,33 @@ module StateMachines
1186
1191
  # result
1187
1192
  # end
1188
1193
  # end
1189
- #
1194
+ #
1190
1195
  # There are a few important things to note here. First, the method
1191
1196
  # signature is defined with an unlimited argument list in order to allow
1192
1197
  # callers to continue passing arguments that are expected by state_machine.
1193
1198
  # For example, it will still allow calls to +park+ with a single parameter
1194
1199
  # for skipping the configured action.
1195
- #
1200
+ #
1196
1201
  # Second, the overridden event method must call +super+ in order to run the
1197
1202
  # logic for running the next possible transition. In order to remain
1198
1203
  # consistent with other events, the result of +super+ is returned.
1199
- #
1204
+ #
1200
1205
  # Third, any behavior defined in this method will *not* get executed if
1201
1206
  # you're taking advantage of attribute-based event transitions. For example:
1202
- #
1207
+ #
1203
1208
  # vehicle = Vehicle.new
1204
1209
  # vehicle.state_event = 'park'
1205
1210
  # vehicle.save
1206
- #
1211
+ #
1207
1212
  # In this case, the +park+ event will run the before/after/around transition
1208
1213
  # hooks and transition the state, but the behavior defined in the overriden
1209
1214
  # +park+ method will *not* be executed.
1210
- #
1215
+ #
1211
1216
  # == Defining additional arguments
1212
- #
1217
+ #
1213
1218
  # Additional arguments can be passed into events and accessed by transition
1214
1219
  # hooks like so:
1215
- #
1220
+ #
1216
1221
  # class Vehicle
1217
1222
  # state_machine do
1218
1223
  # after_transition :on => :park do |vehicle, transition|
@@ -1220,80 +1225,80 @@ module StateMachines
1220
1225
  # ...
1221
1226
  # end
1222
1227
  # after_transition :on => :park, :do => :take_deep_breath
1223
- #
1228
+ #
1224
1229
  # event :park do
1225
1230
  # ...
1226
1231
  # end
1227
- #
1232
+ #
1228
1233
  # def take_deep_breath(transition)
1229
1234
  # kind = *transition.args # :parallel
1230
1235
  # ...
1231
1236
  # end
1232
1237
  # end
1233
1238
  # end
1234
- #
1239
+ #
1235
1240
  # vehicle = Vehicle.new
1236
1241
  # vehicle.park(:parallel)
1237
- #
1242
+ #
1238
1243
  # *Remember* that if the last argument is a boolean, it will be used as the
1239
1244
  # +run_action+ parameter to the event action. Using the +park+ action
1240
1245
  # example from above, you can might call it like so:
1241
- #
1246
+ #
1242
1247
  # vehicle.park # => Uses default args and runs machine action
1243
1248
  # vehicle.park(:parallel) # => Specifies the +kind+ argument and runs the machine action
1244
1249
  # vehicle.park(:parallel, false) # => Specifies the +kind+ argument and *skips* the machine action
1245
- #
1250
+ #
1246
1251
  # If you decide to override the +park+ event method *and* define additional
1247
1252
  # arguments, you can do so as shown below:
1248
- #
1253
+ #
1249
1254
  # class Vehicle
1250
1255
  # state_machine do
1251
1256
  # event :park do
1252
1257
  # ...
1253
1258
  # end
1254
1259
  # end
1255
- #
1260
+ #
1256
1261
  # def park(kind = :parallel, *args)
1257
1262
  # take_deep_breath if kind == :parallel
1258
1263
  # super
1259
1264
  # end
1260
1265
  # end
1261
- #
1266
+ #
1262
1267
  # Note that +super+ is called instead of <tt>super(*args)</tt>. This allow
1263
1268
  # the entire arguments list to be accessed by transition callbacks through
1264
1269
  # StateMachines::Transition#args.
1265
- #
1270
+ #
1266
1271
  # === Using matchers
1267
- #
1272
+ #
1268
1273
  # The +all+ / +any+ matchers can be used to easily execute blocks for a
1269
1274
  # group of events. Note, however, that you cannot use these matchers to
1270
1275
  # set configurations for events. Blocks using these matchers can be
1271
1276
  # defined at any point in the state machine and will always get applied to
1272
1277
  # the proper events.
1273
- #
1278
+ #
1274
1279
  # For example:
1275
- #
1280
+ #
1276
1281
  # state_machine :initial => :parked do
1277
1282
  # ...
1278
- #
1283
+ #
1279
1284
  # event all - [:crash] do
1280
1285
  # transition :stalled => :parked
1281
1286
  # end
1282
1287
  # end
1283
- #
1288
+ #
1284
1289
  # == Example
1285
- #
1290
+ #
1286
1291
  # class Vehicle
1287
1292
  # state_machine do
1288
1293
  # # The park, stop, and halt events will all share the given transitions
1289
1294
  # event :park, :stop, :halt do
1290
1295
  # transition [:idling, :backing_up] => :parked
1291
1296
  # end
1292
- #
1297
+ #
1293
1298
  # event :stop do
1294
1299
  # transition :first_gear => :idling
1295
1300
  # end
1296
- #
1301
+ #
1297
1302
  # event :ignite do
1298
1303
  # transition :parked => :idling
1299
1304
  # transition :idling => same # Allow ignite while still idling
@@ -1312,6 +1317,7 @@ module StateMachines
1312
1317
  # Add any events referenced in the matcher. When matchers are used,
1313
1318
  # events are not allowed to be configured.
1314
1319
  raise ArgumentError, "Cannot configure events when using matchers (using #{options.inspect})" if options.any?
1320
+
1315
1321
  events = add_events(names.first.values)
1316
1322
  else
1317
1323
  events = add_events(names)
@@ -1332,45 +1338,45 @@ module StateMachines
1332
1338
 
1333
1339
  # Creates a new transition that determines what to change the current state
1334
1340
  # to when an event fires.
1335
- #
1341
+ #
1336
1342
  # == Defining transitions
1337
- #
1343
+ #
1338
1344
  # The options for a new transition uses the Hash syntax to map beginning
1339
1345
  # states to ending states. For example,
1340
- #
1346
+ #
1341
1347
  # transition :parked => :idling, :idling => :first_gear, :on => :ignite
1342
- #
1348
+ #
1343
1349
  # In this case, when the +ignite+ event is fired, this transition will cause
1344
1350
  # the state to be +idling+ if it's current state is +parked+ or +first_gear+
1345
1351
  # if it's current state is +idling+.
1346
- #
1352
+ #
1347
1353
  # To help define these implicit transitions, a set of helpers are available
1348
1354
  # for slightly more complex matching:
1349
1355
  # * <tt>all</tt> - Matches every state in the machine
1350
1356
  # * <tt>all - [:parked, :idling, ...]</tt> - Matches every state except those specified
1351
1357
  # * <tt>any</tt> - An alias for +all+ (matches every state in the machine)
1352
1358
  # * <tt>same</tt> - Matches the same state being transitioned from
1353
- #
1359
+ #
1354
1360
  # See StateMachines::MatcherHelpers for more information.
1355
- #
1361
+ #
1356
1362
  # Examples:
1357
- #
1363
+ #
1358
1364
  # transition all => nil, :on => :ignite # Transitions to nil regardless of the current state
1359
1365
  # transition all => :idling, :on => :ignite # Transitions to :idling regardless of the current state
1360
1366
  # transition all - [:idling, :first_gear] => :idling, :on => :ignite # Transitions every state but :idling and :first_gear to :idling
1361
1367
  # transition nil => :idling, :on => :ignite # Transitions to :idling from the nil state
1362
1368
  # transition :parked => :idling, :on => :ignite # Transitions to :idling if :parked
1363
1369
  # transition [:parked, :stalled] => :idling, :on => :ignite # Transitions to :idling if :parked or :stalled
1364
- #
1370
+ #
1365
1371
  # transition :parked => same, :on => :park # Loops :parked back to :parked
1366
1372
  # transition [:parked, :stalled] => same, :on => [:park, :stall] # Loops either :parked or :stalled back to the same state on the park and stall events
1367
1373
  # transition all - :parked => same, :on => :noop # Loops every state but :parked back to the same state
1368
- #
1374
+ #
1369
1375
  # # Transitions to :idling if :parked, :first_gear if :idling, or :second_gear if :first_gear
1370
1376
  # transition :parked => :idling, :idling => :first_gear, :first_gear => :second_gear, :on => :shift_up
1371
- #
1377
+ #
1372
1378
  # == Verbose transitions
1373
- #
1379
+ #
1374
1380
  # Transitions can also be defined use an explicit set of configuration
1375
1381
  # options:
1376
1382
  # * <tt>:from</tt> - A state or array of states that can be transitioned from.
@@ -1379,24 +1385,24 @@ module StateMachines
1379
1385
  # then the transition will simply loop back (i.e. the state will not change).
1380
1386
  # * <tt>:except_from</tt> - A state or array of states that *cannot* be
1381
1387
  # transitioned from.
1382
- #
1388
+ #
1383
1389
  # These options must be used when defining transitions within the context
1384
1390
  # of a state.
1385
- #
1391
+ #
1386
1392
  # Examples:
1387
- #
1393
+ #
1388
1394
  # transition :to => nil, :on => :park
1389
1395
  # transition :to => :idling, :on => :ignite
1390
1396
  # transition :except_from => [:idling, :first_gear], :to => :idling, :on => :ignite
1391
1397
  # transition :from => nil, :to => :idling, :on => :ignite
1392
1398
  # transition :from => [:parked, :stalled], :to => :idling, :on => :ignite
1393
- #
1399
+ #
1394
1400
  # == Conditions
1395
- #
1401
+ #
1396
1402
  # In addition to the state requirements for each transition, a condition
1397
1403
  # can also be defined to help determine whether that transition is
1398
1404
  # available. These options will work on both the normal and verbose syntax.
1399
- #
1405
+ #
1400
1406
  # Configuration options:
1401
1407
  # * <tt>:if</tt> - A method, proc or string to call to determine if the
1402
1408
  # transition should occur (e.g. :if => :moving?, or :if => lambda {|vehicle| vehicle.speed > 60}).
@@ -1404,18 +1410,18 @@ module StateMachines
1404
1410
  # * <tt>:unless</tt> - A method, proc or string to call to determine if the
1405
1411
  # transition should not occur (e.g. :unless => :stopped?, or :unless => lambda {|vehicle| vehicle.speed <= 60}).
1406
1412
  # The condition should return or evaluate to true or false.
1407
- #
1413
+ #
1408
1414
  # Examples:
1409
- #
1415
+ #
1410
1416
  # transition :parked => :idling, :on => :ignite, :if => :moving?
1411
1417
  # transition :parked => :idling, :on => :ignite, :unless => :stopped?
1412
1418
  # transition :idling => :first_gear, :first_gear => :second_gear, :on => :shift_up, :if => :seatbelt_on?
1413
- #
1419
+ #
1414
1420
  # transition :from => :parked, :to => :idling, :on => ignite, :if => :moving?
1415
1421
  # transition :from => :parked, :to => :idling, :on => ignite, :unless => :stopped?
1416
- #
1422
+ #
1417
1423
  # == Order of operations
1418
- #
1424
+ #
1419
1425
  # Transitions are evaluated in the order in which they're defined. As a
1420
1426
  # result, if more than one transition applies to a given object, then the
1421
1427
  # first transition that matches will be performed.
@@ -1431,12 +1437,12 @@ module StateMachines
1431
1437
 
1432
1438
  # Creates a callback that will be invoked *before* a transition is
1433
1439
  # performed so long as the given requirements match the transition.
1434
- #
1440
+ #
1435
1441
  # == The callback
1436
- #
1442
+ #
1437
1443
  # Callbacks must be defined as either an argument, in the :do option, or
1438
1444
  # as a block. For example,
1439
- #
1445
+ #
1440
1446
  # class Vehicle
1441
1447
  # state_machine do
1442
1448
  # before_transition :set_alarm
@@ -1448,13 +1454,13 @@ module StateMachines
1448
1454
  # ...
1449
1455
  # end
1450
1456
  # end
1451
- #
1457
+ #
1452
1458
  # Notice that the first three callbacks are the same in terms of how the
1453
1459
  # methods to invoke are defined. However, using the <tt>:do</tt> can
1454
1460
  # provide for a more fluid DSL.
1455
- #
1461
+ #
1456
1462
  # In addition, multiple callbacks can be defined like so:
1457
- #
1463
+ #
1458
1464
  # class Vehicle
1459
1465
  # state_machine do
1460
1466
  # before_transition :set_alarm, :lock_doors, all => :parked
@@ -1464,54 +1470,54 @@ module StateMachines
1464
1470
  # end
1465
1471
  # end
1466
1472
  # end
1467
- #
1473
+ #
1468
1474
  # Notice that the different ways of configuring methods can be mixed.
1469
- #
1475
+ #
1470
1476
  # == State requirements
1471
- #
1477
+ #
1472
1478
  # Callbacks can require that the machine be transitioning from and to
1473
1479
  # specific states. These requirements use a Hash syntax to map beginning
1474
1480
  # states to ending states. For example,
1475
- #
1481
+ #
1476
1482
  # before_transition :parked => :idling, :idling => :first_gear, :do => :set_alarm
1477
- #
1483
+ #
1478
1484
  # In this case, the +set_alarm+ callback will only be called if the machine
1479
1485
  # is transitioning from +parked+ to +idling+ or from +idling+ to +parked+.
1480
- #
1486
+ #
1481
1487
  # To help define state requirements, a set of helpers are available for
1482
1488
  # slightly more complex matching:
1483
1489
  # * <tt>all</tt> - Matches every state/event in the machine
1484
1490
  # * <tt>all - [:parked, :idling, ...]</tt> - Matches every state/event except those specified
1485
1491
  # * <tt>any</tt> - An alias for +all+ (matches every state/event in the machine)
1486
1492
  # * <tt>same</tt> - Matches the same state being transitioned from
1487
- #
1493
+ #
1488
1494
  # See StateMachines::MatcherHelpers for more information.
1489
- #
1495
+ #
1490
1496
  # Examples:
1491
- #
1497
+ #
1492
1498
  # before_transition :parked => [:idling, :first_gear], :do => ... # Matches from parked to idling or first_gear
1493
1499
  # before_transition all - [:parked, :idling] => :idling, :do => ... # Matches from every state except parked and idling to idling
1494
1500
  # before_transition all => :parked, :do => ... # Matches all states to parked
1495
1501
  # before_transition any => same, :do => ... # Matches every loopback
1496
- #
1502
+ #
1497
1503
  # == Event requirements
1498
- #
1504
+ #
1499
1505
  # In addition to state requirements, an event requirement can be defined so
1500
1506
  # that the callback is only invoked on specific events using the +on+
1501
1507
  # option. This can also use the same matcher helpers as the state
1502
1508
  # requirements.
1503
- #
1509
+ #
1504
1510
  # Examples:
1505
- #
1511
+ #
1506
1512
  # before_transition :on => :ignite, :do => ... # Matches only on ignite
1507
1513
  # before_transition :on => all - :ignite, :do => ... # Matches on every event except ignite
1508
1514
  # before_transition :parked => :idling, :on => :ignite, :do => ... # Matches from parked to idling on ignite
1509
- #
1515
+ #
1510
1516
  # == Verbose Requirements
1511
- #
1517
+ #
1512
1518
  # Requirements can also be defined using verbose options rather than the
1513
1519
  # implicit Hash syntax and helper methods described above.
1514
- #
1520
+ #
1515
1521
  # Configuration options:
1516
1522
  # * <tt>:from</tt> - One or more states being transitioned from. If none
1517
1523
  # are specified, then all states will match.
@@ -1522,116 +1528,116 @@ module StateMachines
1522
1528
  # * <tt>:except_from</tt> - One or more states *not* being transitioned from
1523
1529
  # * <tt>:except_to</tt> - One more states *not* being transitioned to
1524
1530
  # * <tt>:except_on</tt> - One or more events that *did not* fire the transition
1525
- #
1531
+ #
1526
1532
  # Examples:
1527
- #
1533
+ #
1528
1534
  # before_transition :from => :ignite, :to => :idling, :on => :park, :do => ...
1529
1535
  # before_transition :except_from => :ignite, :except_to => :idling, :except_on => :park, :do => ...
1530
- #
1536
+ #
1531
1537
  # == Conditions
1532
- #
1538
+ #
1533
1539
  # In addition to the state/event requirements, a condition can also be
1534
1540
  # defined to help determine whether the callback should be invoked.
1535
- #
1541
+ #
1536
1542
  # Configuration options:
1537
1543
  # * <tt>:if</tt> - A method, proc or string to call to determine if the
1538
1544
  # callback should occur (e.g. :if => :allow_callbacks, or
1539
1545
  # :if => lambda {|user| user.signup_step > 2}). The method, proc or string
1540
- # should return or evaluate to a true or false value.
1546
+ # should return or evaluate to a true or false value.
1541
1547
  # * <tt>:unless</tt> - A method, proc or string to call to determine if the
1542
1548
  # callback should not occur (e.g. :unless => :skip_callbacks, or
1543
1549
  # :unless => lambda {|user| user.signup_step <= 2}). The method, proc or
1544
- # string should return or evaluate to a true or false value.
1545
- #
1550
+ # string should return or evaluate to a true or false value.
1551
+ #
1546
1552
  # Examples:
1547
- #
1553
+ #
1548
1554
  # before_transition :parked => :idling, :if => :moving?, :do => ...
1549
1555
  # before_transition :on => :ignite, :unless => :seatbelt_on?, :do => ...
1550
- #
1556
+ #
1551
1557
  # == Accessing the transition
1552
- #
1558
+ #
1553
1559
  # In addition to passing the object being transitioned, the actual
1554
1560
  # transition describing the context (e.g. event, from, to) can be accessed
1555
1561
  # as well. This additional argument is only passed if the callback allows
1556
1562
  # for it.
1557
- #
1563
+ #
1558
1564
  # For example,
1559
- #
1565
+ #
1560
1566
  # class Vehicle
1561
1567
  # # Only specifies one parameter (the object being transitioned)
1562
1568
  # before_transition all => :parked do |vehicle|
1563
1569
  # vehicle.set_alarm
1564
1570
  # end
1565
- #
1571
+ #
1566
1572
  # # Specifies 2 parameters (object being transitioned and actual transition)
1567
1573
  # before_transition all => :parked do |vehicle, transition|
1568
1574
  # vehicle.set_alarm(transition)
1569
1575
  # end
1570
1576
  # end
1571
- #
1577
+ #
1572
1578
  # *Note* that the object in the callback will only be passed in as an
1573
1579
  # argument if callbacks are configured to *not* be bound to the object
1574
1580
  # involved. This is the default and may change on a per-integration basis.
1575
- #
1581
+ #
1576
1582
  # See StateMachines::Transition for more information about the
1577
1583
  # attributes available on the transition.
1578
- #
1584
+ #
1579
1585
  # == Usage with delegates
1580
- #
1586
+ #
1581
1587
  # As noted above, state_machine uses the callback method's argument list
1582
1588
  # arity to determine whether to include the transition in the method call.
1583
1589
  # If you're using delegates, such as those defined in ActiveSupport or
1584
1590
  # Forwardable, the actual arity of the delegated method gets masked. This
1585
1591
  # means that callbacks which reference delegates will always get passed the
1586
1592
  # transition as an argument. For example:
1587
- #
1593
+ #
1588
1594
  # class Vehicle
1589
1595
  # extend Forwardable
1590
1596
  # delegate :refresh => :dashboard
1591
- #
1597
+ #
1592
1598
  # state_machine do
1593
1599
  # before_transition :refresh
1594
1600
  # ...
1595
1601
  # end
1596
- #
1602
+ #
1597
1603
  # def dashboard
1598
1604
  # @dashboard ||= Dashboard.new
1599
1605
  # end
1600
1606
  # end
1601
- #
1607
+ #
1602
1608
  # class Dashboard
1603
1609
  # def refresh(transition)
1604
1610
  # # ...
1605
1611
  # end
1606
1612
  # end
1607
- #
1613
+ #
1608
1614
  # In the above example, <tt>Dashboard#refresh</tt> *must* defined a
1609
1615
  # +transition+ argument. Otherwise, an +ArgumentError+ exception will get
1610
1616
  # raised. The only way around this is to avoid the use of delegates and
1611
1617
  # manually define the delegate method so that the correct arity is used.
1612
- #
1618
+ #
1613
1619
  # == Examples
1614
- #
1620
+ #
1615
1621
  # Below is an example of a class with one state machine and various types
1616
1622
  # of +before+ transitions defined for it:
1617
- #
1623
+ #
1618
1624
  # class Vehicle
1619
1625
  # state_machine do
1620
1626
  # # Before all transitions
1621
1627
  # before_transition :update_dashboard
1622
- #
1628
+ #
1623
1629
  # # Before specific transition:
1624
1630
  # before_transition [:first_gear, :idling] => :parked, :on => :park, :do => :take_off_seatbelt
1625
- #
1631
+ #
1626
1632
  # # With conditional callback:
1627
1633
  # before_transition all => :parked, :do => :take_off_seatbelt, :if => :seatbelt_on?
1628
- #
1634
+ #
1629
1635
  # # Using helpers:
1630
1636
  # before_transition all - :stalled => same, :on => any - :crash, :do => :update_dashboard
1631
1637
  # ...
1632
1638
  # end
1633
1639
  # end
1634
- #
1640
+ #
1635
1641
  # As can be seen, any number of transitions can be created using various
1636
1642
  # combinations of configuration options.
1637
1643
  def before_transition(*args, &block)
@@ -1642,7 +1648,7 @@ module StateMachines
1642
1648
 
1643
1649
  # Creates a callback that will be invoked *after* a transition is
1644
1650
  # performed so long as the given requirements match the transition.
1645
- #
1651
+ #
1646
1652
  # See +before_transition+ for a description of the possible configurations
1647
1653
  # for defining callbacks.
1648
1654
  def after_transition(*args, &block)
@@ -1653,32 +1659,32 @@ module StateMachines
1653
1659
 
1654
1660
  # Creates a callback that will be invoked *around* a transition so long as
1655
1661
  # the given requirements match the transition.
1656
- #
1662
+ #
1657
1663
  # == The callback
1658
- #
1664
+ #
1659
1665
  # Around callbacks wrap transitions, executing code both before and after.
1660
1666
  # These callbacks are defined in the exact same manner as before / after
1661
1667
  # callbacks with the exception that the transition must be yielded to in
1662
1668
  # order to finish running it.
1663
- #
1669
+ #
1664
1670
  # If defining +around+ callbacks using blocks, you must yield within the
1665
1671
  # transition by directly calling the block (since yielding is not allowed
1666
1672
  # within blocks).
1667
- #
1673
+ #
1668
1674
  # For example,
1669
- #
1675
+ #
1670
1676
  # class Vehicle
1671
1677
  # state_machine do
1672
1678
  # around_transition do |block|
1673
1679
  # Benchmark.measure { block.call }
1674
1680
  # end
1675
- #
1681
+ #
1676
1682
  # around_transition do |vehicle, block|
1677
1683
  # logger.info "vehicle was #{state}..."
1678
1684
  # block.call
1679
1685
  # logger.info "...and is now #{state}"
1680
1686
  # end
1681
- #
1687
+ #
1682
1688
  # around_transition do |vehicle, transition, block|
1683
1689
  # logger.info "before #{transition.event}: #{vehicle.state}"
1684
1690
  # block.call
@@ -1686,24 +1692,24 @@ module StateMachines
1686
1692
  # end
1687
1693
  # end
1688
1694
  # end
1689
- #
1695
+ #
1690
1696
  # Notice that referencing the block is similar to doing so within an
1691
1697
  # actual method definition in that it is always the last argument.
1692
- #
1698
+ #
1693
1699
  # On the other hand, if you're defining +around+ callbacks using method
1694
1700
  # references, you can yield like normal:
1695
- #
1701
+ #
1696
1702
  # class Vehicle
1697
1703
  # state_machine do
1698
1704
  # around_transition :benchmark
1699
1705
  # ...
1700
1706
  # end
1701
- #
1707
+ #
1702
1708
  # def benchmark
1703
1709
  # Benchmark.measure { yield }
1704
1710
  # end
1705
1711
  # end
1706
- #
1712
+ #
1707
1713
  # See +before_transition+ for a description of the possible configurations
1708
1714
  # for defining callbacks.
1709
1715
  def around_transition(*args, &block)
@@ -1714,29 +1720,29 @@ module StateMachines
1714
1720
 
1715
1721
  # Creates a callback that will be invoked *after* a transition failures to
1716
1722
  # be performed so long as the given requirements match the transition.
1717
- #
1723
+ #
1718
1724
  # See +before_transition+ for a description of the possible configurations
1719
1725
  # for defining callbacks. *Note* however that you cannot define the state
1720
1726
  # requirements in these callbacks. You may only define event requirements.
1721
- #
1727
+ #
1722
1728
  # = The callback
1723
- #
1729
+ #
1724
1730
  # Failure callbacks get invoked whenever an event fails to execute. This
1725
1731
  # can happen when no transition is available, a +before+ callback halts
1726
1732
  # execution, or the action associated with this machine fails to succeed.
1727
1733
  # In any of these cases, any failure callback that matches the attempted
1728
1734
  # transition will be run.
1729
- #
1735
+ #
1730
1736
  # For example,
1731
- #
1737
+ #
1732
1738
  # class Vehicle
1733
1739
  # state_machine do
1734
1740
  # after_failure do |vehicle, transition|
1735
1741
  # logger.error "vehicle #{vehicle} failed to transition on #{transition.event}"
1736
1742
  # end
1737
- #
1743
+ #
1738
1744
  # after_failure :on => :ignite, :do => :log_ignition_failure
1739
- #
1745
+ #
1740
1746
  # ...
1741
1747
  # end
1742
1748
  # end
@@ -1752,7 +1758,7 @@ module StateMachines
1752
1758
  # the given object. These paths can reveal all of the possible states and
1753
1759
  # events that can be encountered in the object's state machine based on the
1754
1760
  # object's current state.
1755
- #
1761
+ #
1756
1762
  # Configuration options:
1757
1763
  # * +from+ - The initial state to start all paths from. By default, this
1758
1764
  # is the object's current state.
@@ -1763,31 +1769,31 @@ module StateMachines
1763
1769
  # state (if specified) is reached. If this is enabled, then paths can
1764
1770
  # continue even after reaching the target state; they will stop when
1765
1771
  # reaching the target state a second time.
1766
- #
1772
+ #
1767
1773
  # *Note* that the object is never modified when the list of paths is
1768
1774
  # generated.
1769
- #
1775
+ #
1770
1776
  # == Examples
1771
- #
1777
+ #
1772
1778
  # class Vehicle
1773
1779
  # state_machine :initial => :parked do
1774
1780
  # event :ignite do
1775
1781
  # transition :parked => :idling
1776
1782
  # end
1777
- #
1783
+ #
1778
1784
  # event :shift_up do
1779
1785
  # transition :idling => :first_gear, :first_gear => :second_gear
1780
1786
  # end
1781
- #
1787
+ #
1782
1788
  # event :shift_down do
1783
1789
  # transition :second_gear => :first_gear, :first_gear => :idling
1784
1790
  # end
1785
1791
  # end
1786
1792
  # end
1787
- #
1793
+ #
1788
1794
  # vehicle = Vehicle.new # => #<Vehicle:0xb7c27024 @state="parked">
1789
1795
  # vehicle.state # => "parked"
1790
- #
1796
+ #
1791
1797
  # vehicle.state_paths
1792
1798
  # # => [
1793
1799
  # # [#<StateMachines::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>,
@@ -1795,26 +1801,26 @@ module StateMachines
1795
1801
  # # #<StateMachines::Transition attribute=:state event=:shift_up from="first_gear" from_name=:first_gear to="second_gear" to_name=:second_gear>,
1796
1802
  # # #<StateMachines::Transition attribute=:state event=:shift_down from="second_gear" from_name=:second_gear to="first_gear" to_name=:first_gear>,
1797
1803
  # # #<StateMachines::Transition attribute=:state event=:shift_down from="first_gear" from_name=:first_gear to="idling" to_name=:idling>],
1798
- # #
1804
+ # #
1799
1805
  # # [#<StateMachines::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>,
1800
1806
  # # #<StateMachines::Transition attribute=:state event=:shift_up from="idling" from_name=:idling to="first_gear" to_name=:first_gear>,
1801
1807
  # # #<StateMachines::Transition attribute=:state event=:shift_down from="first_gear" from_name=:first_gear to="idling" to_name=:idling>]
1802
1808
  # # ]
1803
- #
1809
+ #
1804
1810
  # vehicle.state_paths(:from => :parked, :to => :second_gear)
1805
1811
  # # => [
1806
1812
  # # [#<StateMachines::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>,
1807
1813
  # # #<StateMachines::Transition attribute=:state event=:shift_up from="idling" from_name=:idling to="first_gear" to_name=:first_gear>,
1808
1814
  # # #<StateMachines::Transition attribute=:state event=:shift_up from="first_gear" from_name=:first_gear to="second_gear" to_name=:second_gear>]
1809
1815
  # # ]
1810
- #
1816
+ #
1811
1817
  # In addition to getting the possible paths that can be accessed, you can
1812
1818
  # also get summary information about the states / events that can be
1813
1819
  # accessed at some point along one of the paths. For example:
1814
- #
1820
+ #
1815
1821
  # # Get the list of states that can be accessed from the current state
1816
1822
  # vehicle.state_paths.to_states # => [:idling, :first_gear, :second_gear]
1817
- #
1823
+ #
1818
1824
  # # Get the list of events that can be accessed from the current state
1819
1825
  # vehicle.state_paths.events # => [:ignite, :shift_up, :shift_down]
1820
1826
  def paths_for(object, requirements = {})
@@ -1822,7 +1828,7 @@ module StateMachines
1822
1828
  end
1823
1829
 
1824
1830
  # Marks the given object as invalid with the given message.
1825
- #
1831
+ #
1826
1832
  # By default, this is a no-op.
1827
1833
  def invalidate(_object, _attribute, _message, _values = [])
1828
1834
  end
@@ -1835,7 +1841,7 @@ module StateMachines
1835
1841
  end
1836
1842
 
1837
1843
  # Resets any errors previously added when invalidating the given object.
1838
- #
1844
+ #
1839
1845
  # By default, this is a no-op.
1840
1846
  def reset(_object)
1841
1847
  end
@@ -1855,7 +1861,7 @@ module StateMachines
1855
1861
  end
1856
1862
 
1857
1863
  # Runs a transaction, rolling back any changes if the yielded block fails.
1858
- #
1864
+ #
1859
1865
  # This is only applicable to integrations that involve databases. By
1860
1866
  # default, this will not run any transactions since the changes aren't
1861
1867
  # taking place within the context of a database.
@@ -1878,7 +1884,8 @@ module StateMachines
1878
1884
  @action_hook_defined || !self_only && owner_class.state_machines.any? { |name, machine| machine.action == action && machine != self && machine.action_hook?(true) }
1879
1885
  end
1880
1886
 
1881
- protected
1887
+ protected
1888
+
1882
1889
  # Runs additional initialization hooks. By default, this is a no-op.
1883
1890
  def after_initialize
1884
1891
  end
@@ -2026,7 +2033,7 @@ module StateMachines
2026
2033
  def #{action_hook}(*)
2027
2034
  self.class.state_machines.transitions(self, #{action.inspect}).perform { super }
2028
2035
  end
2029
-
2036
+
2030
2037
  private #{action_hook.inspect} if #{private_action_hook}
2031
2038
  end_eval
2032
2039
  end
@@ -2046,14 +2053,12 @@ module StateMachines
2046
2053
  # the method and is further along in the ancestor chain than this
2047
2054
  # machine's helper module.
2048
2055
  def owner_class_ancestor_has_method?(scope, method)
2049
- superclasses = owner_class.ancestors[1..-1].select { |ancestor| ancestor.is_a?(Class) }
2056
+ return false unless owner_class_has_method?(scope, method)
2057
+
2058
+ superclasses = owner_class.ancestors.select { |ancestor| ancestor.is_a?(Class) }[1..-1]
2050
2059
 
2051
2060
  if scope == :class
2052
- # Use singleton classes
2053
- current = (
2054
- class << owner_class;
2055
- self;
2056
- end)
2061
+ current = owner_class.singleton_class
2057
2062
  superclass = superclasses.first
2058
2063
  else
2059
2064
  current = owner_class
@@ -2068,14 +2073,16 @@ module StateMachines
2068
2073
 
2069
2074
  # Search for for the first ancestor that defined this method
2070
2075
  ancestors.detect do |ancestor|
2071
- ancestor = (
2072
- class << ancestor;
2073
- self;
2074
- end) if scope == :class && ancestor.is_a?(Class)
2076
+ ancestor = ancestor.singleton_class if scope == :class && ancestor.is_a?(Class)
2075
2077
  ancestor.method_defined?(method) || ancestor.private_method_defined?(method)
2076
2078
  end
2077
2079
  end
2078
2080
 
2081
+ def owner_class_has_method?(scope, method)
2082
+ target = scope == :class ? owner_class.singleton_class : owner_class
2083
+ target.method_defined?(method) || target.private_method_defined?(method)
2084
+ end
2085
+
2079
2086
  # Adds helper methods for accessing naming information about states and
2080
2087
  # events on the owner class
2081
2088
  def define_name_helpers
@@ -2112,7 +2119,7 @@ module StateMachines
2112
2119
  [name, plural].map { |s| s.to_s }.uniq.each do |suffix|
2113
2120
  method = "#{kind}_#{suffix}"
2114
2121
 
2115
- if scope = send("create_#{kind}_scope", method)
2122
+ if (scope = send("create_#{kind}_scope", method))
2116
2123
  # Converts state names to their corresponding values so that they
2117
2124
  # can be looked up properly
2118
2125
  define_helper(:class, method) do |machine, klass, *states|
@@ -2126,7 +2133,7 @@ module StateMachines
2126
2133
  # Generates the results for the given scope based on one or more states to
2127
2134
  # filter by
2128
2135
  def run_scope(scope, machine, klass, states)
2129
- values = states.flatten.map { |state| machine.states.fetch(state).value }
2136
+ values = states.flatten.compact.map { |state| machine.states.fetch(state).value }
2130
2137
  scope.call(klass, values)
2131
2138
  end
2132
2139
 
@@ -2194,11 +2201,11 @@ module StateMachines
2194
2201
  new_states.map do |new_state|
2195
2202
  # Check for other states that use a different class type for their name.
2196
2203
  # This typically prevents string / symbol misuse.
2197
- if new_state && conflict = states.detect { |state| state.name && state.name.class != new_state.class }
2204
+ if new_state && (conflict = states.detect { |state| state.name && state.name.class != new_state.class })
2198
2205
  raise ArgumentError, "#{new_state.inspect} state defined as #{new_state.class}, #{conflict.name.inspect} defined as #{conflict.name.class}; all states must be consistent"
2199
2206
  end
2200
2207
 
2201
- unless state = states[new_state]
2208
+ unless (state = states[new_state])
2202
2209
  states << state = State.new(self, new_state)
2203
2210
 
2204
2211
  # Copy states over to sibling machines
@@ -2215,11 +2222,11 @@ module StateMachines
2215
2222
  new_events.map do |new_event|
2216
2223
  # Check for other states that use a different class type for their name.
2217
2224
  # This typically prevents string / symbol misuse.
2218
- if conflict = events.detect { |event| event.name.class != new_event.class }
2225
+ if (conflict = events.detect { |event| event.name.class != new_event.class })
2219
2226
  raise ArgumentError, "#{new_event.inspect} event defined as #{new_event.class}, #{conflict.name.inspect} defined as #{conflict.name.class}; all events must be consistent"
2220
2227
  end
2221
2228
 
2222
- unless event = events[new_event]
2229
+ unless (event = events[new_event])
2223
2230
  events << event = Event.new(self, new_event)
2224
2231
  end
2225
2232