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