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