state_machines 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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