state_machines 0.5.0 → 0.20.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 (466) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +1 -1
  3. data/README.md +99 -16
  4. data/lib/state_machines/branch.rb +88 -82
  5. data/lib/state_machines/callback.rb +24 -21
  6. data/lib/state_machines/core.rb +3 -2
  7. data/lib/state_machines/core_ext/class/state_machine.rb +2 -0
  8. data/lib/state_machines/core_ext.rb +2 -0
  9. data/lib/state_machines/error.rb +2 -0
  10. data/lib/state_machines/eval_helpers.rb +51 -22
  11. data/lib/state_machines/event.rb +53 -35
  12. data/lib/state_machines/event_collection.rb +28 -25
  13. data/lib/state_machines/extensions.rb +3 -1
  14. data/lib/state_machines/helper_module.rb +3 -1
  15. data/lib/state_machines/integrations/base.rb +2 -0
  16. data/lib/state_machines/integrations.rb +10 -8
  17. data/lib/state_machines/machine/class_methods.rb +79 -0
  18. data/lib/state_machines/machine.rb +386 -429
  19. data/lib/state_machines/machine_collection.rb +16 -11
  20. data/lib/state_machines/macro_methods.rb +102 -100
  21. data/lib/state_machines/matcher.rb +26 -23
  22. data/lib/state_machines/matcher_helpers.rb +13 -11
  23. data/lib/state_machines/node_collection.rb +20 -14
  24. data/lib/state_machines/options_validator.rb +72 -0
  25. data/lib/state_machines/path.rb +61 -56
  26. data/lib/state_machines/path_collection.rb +40 -36
  27. data/lib/state_machines/state.rb +72 -43
  28. data/lib/state_machines/state_collection.rb +22 -19
  29. data/lib/state_machines/state_context.rb +38 -36
  30. data/lib/state_machines/stdio_renderer.rb +74 -0
  31. data/lib/state_machines/test_helper.rb +305 -0
  32. data/lib/state_machines/transition.rb +182 -178
  33. data/lib/state_machines/transition_collection.rb +175 -169
  34. data/lib/state_machines/version.rb +3 -1
  35. data/lib/state_machines.rb +4 -1
  36. metadata +12 -440
  37. data/.gitignore +0 -21
  38. data/.rspec +0 -3
  39. data/.ruby-gemset +0 -1
  40. data/.ruby-version +0 -1
  41. data/.travis.yml +0 -16
  42. data/Changelog.md +0 -22
  43. data/Contributors.md +0 -39
  44. data/Gemfile +0 -8
  45. data/Rakefile +0 -12
  46. data/Testing.md +0 -0
  47. data/lib/state_machines/assertions.rb +0 -40
  48. data/state_machines.gemspec +0 -22
  49. data/test/files/integrations/event_on_failure_integration.rb +0 -10
  50. data/test/files/integrations/vehicle.rb +0 -7
  51. data/test/files/models/auto_shop.rb +0 -31
  52. data/test/files/models/car.rb +0 -21
  53. data/test/files/models/driver.rb +0 -13
  54. data/test/files/models/model_base.rb +0 -6
  55. data/test/files/models/motorcycle.rb +0 -16
  56. data/test/files/models/traffic_light.rb +0 -47
  57. data/test/files/models/vehicle.rb +0 -127
  58. data/test/files/node.rb +0 -5
  59. data/test/files/switch.rb +0 -15
  60. data/test/functional/auto_shop_available_test.rb +0 -20
  61. data/test/functional/auto_shop_busy_test.rb +0 -25
  62. data/test/functional/car_backing_up_test.rb +0 -45
  63. data/test/functional/car_test.rb +0 -49
  64. data/test/functional/driver_default_nonstandard_test.rb +0 -13
  65. data/test/functional/motorcycle_test.rb +0 -52
  66. data/test/functional/traffic_light_caution_test.rb +0 -17
  67. data/test/functional/traffic_light_proceed_test.rb +0 -17
  68. data/test/functional/traffic_light_stop_test.rb +0 -26
  69. data/test/functional/vehicle_first_gear_test.rb +0 -42
  70. data/test/functional/vehicle_idling_test.rb +0 -59
  71. data/test/functional/vehicle_locked_test.rb +0 -29
  72. data/test/functional/vehicle_parked_test.rb +0 -53
  73. data/test/functional/vehicle_repaired_test.rb +0 -20
  74. data/test/functional/vehicle_second_gear_test.rb +0 -42
  75. data/test/functional/vehicle_stalled_test.rb +0 -65
  76. data/test/functional/vehicle_test.rb +0 -20
  77. data/test/functional/vehicle_third_gear_test.rb +0 -42
  78. data/test/functional/vehicle_unsaved_test.rb +0 -181
  79. data/test/functional/vehicle_with_event_attributes_test.rb +0 -30
  80. data/test/functional/vehicle_with_parallel_events_test.rb +0 -36
  81. data/test/test_helper.rb +0 -15
  82. data/test/unit/assertions/assert_exclusive_keys_test.rb +0 -22
  83. data/test/unit/assertions/assert_valid_key_test.rb +0 -12
  84. data/test/unit/branch/branch_test.rb +0 -28
  85. data/test/unit/branch/branch_with_conflicting_conditionals_test.rb +0 -27
  86. data/test/unit/branch/branch_with_conflicting_from_requirements_test.rb +0 -8
  87. data/test/unit/branch/branch_with_conflicting_on_requirements_test.rb +0 -8
  88. data/test/unit/branch/branch_with_conflicting_to_requirements_test.rb +0 -8
  89. data/test/unit/branch/branch_with_different_requirements_test.rb +0 -41
  90. data/test/unit/branch/branch_with_except_from_matcher_requirement_test.rb +0 -8
  91. data/test/unit/branch/branch_with_except_from_requirement_test.rb +0 -36
  92. data/test/unit/branch/branch_with_except_on_matcher_requirement_test.rb +0 -8
  93. data/test/unit/branch/branch_with_except_on_requirement_test.rb +0 -36
  94. data/test/unit/branch/branch_with_except_to_matcher_requirement_test.rb +0 -8
  95. data/test/unit/branch/branch_with_except_to_requirement_test.rb +0 -36
  96. data/test/unit/branch/branch_with_from_matcher_requirement_test.rb +0 -20
  97. data/test/unit/branch/branch_with_from_requirement_test.rb +0 -45
  98. data/test/unit/branch/branch_with_if_conditional_test.rb +0 -27
  99. data/test/unit/branch/branch_with_implicit_and_explicit_requirements_test.rb +0 -23
  100. data/test/unit/branch/branch_with_implicit_from_requirement_matcher_test.rb +0 -20
  101. data/test/unit/branch/branch_with_implicit_requirement_test.rb +0 -20
  102. data/test/unit/branch/branch_with_implicit_to_requirement_matcher_test.rb +0 -16
  103. data/test/unit/branch/branch_with_multiple_except_from_requirements_test.rb +0 -20
  104. data/test/unit/branch/branch_with_multiple_except_on_requirements_test.rb +0 -16
  105. data/test/unit/branch/branch_with_multiple_except_to_requirements_test.rb +0 -20
  106. data/test/unit/branch/branch_with_multiple_from_requirements_test.rb +0 -16
  107. data/test/unit/branch/branch_with_multiple_if_conditionals_test.rb +0 -20
  108. data/test/unit/branch/branch_with_multiple_implicit_requirements_test.rb +0 -53
  109. data/test/unit/branch/branch_with_multiple_to_requirements_test.rb +0 -20
  110. data/test/unit/branch/branch_with_multiple_unless_conditionals_test.rb +0 -20
  111. data/test/unit/branch/branch_with_nil_requirements_test.rb +0 -28
  112. data/test/unit/branch/branch_with_no_requirements_test.rb +0 -36
  113. data/test/unit/branch/branch_with_on_matcher_requirement_test.rb +0 -16
  114. data/test/unit/branch/branch_with_on_requirement_test.rb +0 -45
  115. data/test/unit/branch/branch_with_to_matcher_requirement_test.rb +0 -20
  116. data/test/unit/branch/branch_with_to_requirement_test.rb +0 -45
  117. data/test/unit/branch/branch_with_unless_conditional_test.rb +0 -27
  118. data/test/unit/branch/branch_without_guards_test.rb +0 -27
  119. data/test/unit/callback/callback_by_default_test.rb +0 -25
  120. data/test/unit/callback/callback_test.rb +0 -53
  121. data/test/unit/callback/callback_with_application_bound_object_test.rb +0 -23
  122. data/test/unit/callback/callback_with_application_terminator_test.rb +0 -24
  123. data/test/unit/callback/callback_with_arguments_test.rb +0 -14
  124. data/test/unit/callback/callback_with_around_type_and_arguments_test.rb +0 -25
  125. data/test/unit/callback/callback_with_around_type_and_block_test.rb +0 -44
  126. data/test/unit/callback/callback_with_around_type_and_bound_method_test.rb +0 -23
  127. data/test/unit/callback/callback_with_around_type_and_multiple_methods_test.rb +0 -93
  128. data/test/unit/callback/callback_with_around_type_and_terminator_test.rb +0 -17
  129. data/test/unit/callback/callback_with_block_test.rb +0 -20
  130. data/test/unit/callback/callback_with_bound_method_and_arguments_test.rb +0 -28
  131. data/test/unit/callback/callback_with_bound_method_test.rb +0 -35
  132. data/test/unit/callback/callback_with_do_method_test.rb +0 -18
  133. data/test/unit/callback/callback_with_explicit_requirements_test.rb +0 -32
  134. data/test/unit/callback/callback_with_if_condition_test.rb +0 -17
  135. data/test/unit/callback/callback_with_implicit_requirements_test.rb +0 -32
  136. data/test/unit/callback/callback_with_method_argument_test.rb +0 -18
  137. data/test/unit/callback/callback_with_mixed_methods_test.rb +0 -31
  138. data/test/unit/callback/callback_with_multiple_bound_methods_test.rb +0 -21
  139. data/test/unit/callback/callback_with_multiple_do_methods_test.rb +0 -29
  140. data/test/unit/callback/callback_with_multiple_method_arguments_test.rb +0 -29
  141. data/test/unit/callback/callback_with_terminator_test.rb +0 -22
  142. data/test/unit/callback/callback_with_unbound_method_test.rb +0 -14
  143. data/test/unit/callback/callback_with_unless_condition_test.rb +0 -17
  144. data/test/unit/callback/callback_without_arguments_test.rb +0 -14
  145. data/test/unit/callback/callback_without_terminator_test.rb +0 -12
  146. data/test/unit/error/error_by_default_test.rb +0 -21
  147. data/test/unit/error/error_with_message_test.rb +0 -23
  148. data/test/unit/eval_helper/eval_helpers_base_test.rb +0 -8
  149. data/test/unit/eval_helper/eval_helpers_proc_block_and_explicit_arguments_test.rb +0 -14
  150. data/test/unit/eval_helper/eval_helpers_proc_block_and_implicit_arguments_test.rb +0 -14
  151. data/test/unit/eval_helper/eval_helpers_proc_test.rb +0 -13
  152. data/test/unit/eval_helper/eval_helpers_proc_with_arguments_test.rb +0 -13
  153. data/test/unit/eval_helper/eval_helpers_proc_with_block_test.rb +0 -13
  154. data/test/unit/eval_helper/eval_helpers_proc_with_block_without_arguments_test.rb +0 -18
  155. data/test/unit/eval_helper/eval_helpers_proc_with_block_without_object_test.rb +0 -14
  156. data/test/unit/eval_helper/eval_helpers_proc_without_arguments_test.rb +0 -19
  157. data/test/unit/eval_helper/eval_helpers_string_test.rb +0 -25
  158. data/test/unit/eval_helper/eval_helpers_string_with_block_test.rb +0 -12
  159. data/test/unit/eval_helper/eval_helpers_symbol_method_missing_test.rb +0 -20
  160. data/test/unit/eval_helper/eval_helpers_symbol_private_test.rb +0 -17
  161. data/test/unit/eval_helper/eval_helpers_symbol_protected_test.rb +0 -17
  162. data/test/unit/eval_helper/eval_helpers_symbol_tainted_method_test.rb +0 -18
  163. data/test/unit/eval_helper/eval_helpers_symbol_test.rb +0 -16
  164. data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_and_block_test.rb +0 -16
  165. data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_test.rb +0 -16
  166. data/test/unit/eval_helper/eval_helpers_symbol_with_block_test.rb +0 -16
  167. data/test/unit/eval_helper/eval_helpers_test.rb +0 -13
  168. data/test/unit/event/event_after_being_copied_test.rb +0 -17
  169. data/test/unit/event/event_by_default_test.rb +0 -60
  170. data/test/unit/event/event_context_test.rb +0 -16
  171. data/test/unit/event/event_on_failure_test.rb +0 -44
  172. data/test/unit/event/event_test.rb +0 -34
  173. data/test/unit/event/event_transitions_test.rb +0 -62
  174. data/test/unit/event/event_with_conflicting_helpers_after_definition_test.rb +0 -79
  175. data/test/unit/event/event_with_conflicting_helpers_before_definition_test.rb +0 -58
  176. data/test/unit/event/event_with_conflicting_machine_test.rb +0 -48
  177. data/test/unit/event/event_with_dynamic_human_name_test.rb +0 -26
  178. data/test/unit/event/event_with_human_name_test.rb +0 -13
  179. data/test/unit/event/event_with_invalid_current_state_test.rb +0 -30
  180. data/test/unit/event/event_with_machine_action_test.rb +0 -33
  181. data/test/unit/event/event_with_marshalling_test.rb +0 -47
  182. data/test/unit/event/event_with_matching_disabled_transitions_test.rb +0 -115
  183. data/test/unit/event/event_with_matching_enabled_transitions_test.rb +0 -75
  184. data/test/unit/event/event_with_multiple_transitions_test.rb +0 -61
  185. data/test/unit/event/event_with_namespace_test.rb +0 -34
  186. data/test/unit/event/event_with_transition_with_blacklisted_to_state_test.rb +0 -60
  187. data/test/unit/event/event_with_transition_with_loopback_state_test.rb +0 -36
  188. data/test/unit/event/event_with_transition_with_nil_to_state_test.rb +0 -36
  189. data/test/unit/event/event_with_transition_with_whitelisted_to_state_test.rb +0 -51
  190. data/test/unit/event/event_with_transition_without_to_state_test.rb +0 -36
  191. data/test/unit/event/event_with_transitions_test.rb +0 -32
  192. data/test/unit/event/event_without_matching_transitions_test.rb +0 -41
  193. data/test/unit/event/event_without_transitions_test.rb +0 -28
  194. data/test/unit/event/invalid_event_test.rb +0 -20
  195. data/test/unit/event_collection/event_collection_attribute_with_machine_action_test.rb +0 -62
  196. data/test/unit/event_collection/event_collection_attribute_with_namespaced_machine_test.rb +0 -36
  197. data/test/unit/event_collection/event_collection_by_default_test.rb +0 -26
  198. data/test/unit/event_collection/event_collection_test.rb +0 -39
  199. data/test/unit/event_collection/event_collection_with_custom_machine_attribute_test.rb +0 -31
  200. data/test/unit/event_collection/event_collection_with_events_with_transitions_test.rb +0 -76
  201. data/test/unit/event_collection/event_collection_with_multiple_events_test.rb +0 -27
  202. data/test/unit/event_collection/event_collection_with_validations_test.rb +0 -74
  203. data/test/unit/event_collection/event_collection_without_machine_action_test.rb +0 -18
  204. data/test/unit/event_collection/event_string_collection_test.rb +0 -31
  205. data/test/unit/helper_module_test.rb +0 -17
  206. data/test/unit/integrations/integration_finder_test.rb +0 -16
  207. data/test/unit/integrations/integration_matcher_test.rb +0 -29
  208. data/test/unit/invalid_transition/invalid_parallel_transition_test.rb +0 -18
  209. data/test/unit/invalid_transition/invalid_transition_test.rb +0 -47
  210. data/test/unit/invalid_transition/invalid_transition_with_integration_test.rb +0 -45
  211. data/test/unit/invalid_transition/invalid_transition_with_namespace_test.rb +0 -32
  212. data/test/unit/machine/machine_after_being_copied_test.rb +0 -62
  213. data/test/unit/machine/machine_after_changing_initial_state.rb +0 -28
  214. data/test/unit/machine/machine_after_changing_owner_class_test.rb +0 -31
  215. data/test/unit/machine/machine_by_default_test.rb +0 -160
  216. data/test/unit/machine/machine_finder_custom_options_test.rb +0 -17
  217. data/test/unit/machine/machine_finder_with_existing_machine_on_superclass_test.rb +0 -85
  218. data/test/unit/machine/machine_finder_with_existing_on_same_class_test.rb +0 -23
  219. data/test/unit/machine/machine_finder_without_existing_machine_test.rb +0 -25
  220. data/test/unit/machine/machine_persistence_test.rb +0 -52
  221. data/test/unit/machine/machine_state_initialization_test.rb +0 -56
  222. data/test/unit/machine/machine_test.rb +0 -30
  223. data/test/unit/machine/machine_with_action_already_overridden_test.rb +0 -23
  224. data/test/unit/machine/machine_with_action_defined_in_class_test.rb +0 -37
  225. data/test/unit/machine/machine_with_action_defined_in_included_module_test.rb +0 -46
  226. data/test/unit/machine/machine_with_action_defined_in_superclass_test.rb +0 -43
  227. data/test/unit/machine/machine_with_action_undefined_test.rb +0 -33
  228. data/test/unit/machine/machine_with_cached_state_test.rb +0 -20
  229. data/test/unit/machine/machine_with_class_helpers_test.rb +0 -179
  230. data/test/unit/machine/machine_with_conflicting_helpers_after_definition_test.rb +0 -244
  231. data/test/unit/machine/machine_with_conflicting_helpers_before_definition_test.rb +0 -175
  232. data/test/unit/machine/machine_with_custom_action_test.rb +0 -11
  233. data/test/unit/machine/machine_with_custom_attribute_test.rb +0 -103
  234. data/test/unit/machine/machine_with_custom_initialize_test.rb +0 -24
  235. data/test/unit/machine/machine_with_custom_integration_test.rb +0 -72
  236. data/test/unit/machine/machine_with_custom_invalidation_test.rb +0 -39
  237. data/test/unit/machine/machine_with_custom_name_test.rb +0 -57
  238. data/test/unit/machine/machine_with_custom_plural_test.rb +0 -52
  239. data/test/unit/machine/machine_with_dynamic_initial_state_test.rb +0 -65
  240. data/test/unit/machine/machine_with_event_matchers_test.rb +0 -41
  241. data/test/unit/machine/machine_with_events_test.rb +0 -52
  242. data/test/unit/machine/machine_with_events_with_custom_human_names_test.rb +0 -18
  243. data/test/unit/machine/machine_with_events_with_transitions_test.rb +0 -37
  244. data/test/unit/machine/machine_with_existing_event_test.rb +0 -17
  245. data/test/unit/machine/machine_with_existing_machines_on_owner_class_test.rb +0 -20
  246. data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_class_test.rb +0 -71
  247. data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_subclass_test.rb +0 -31
  248. data/test/unit/machine/machine_with_existing_state_test.rb +0 -27
  249. data/test/unit/machine/machine_with_failure_callbacks_test.rb +0 -48
  250. data/test/unit/machine/machine_with_helpers_test.rb +0 -14
  251. data/test/unit/machine/machine_with_initial_state_with_value_and_owner_default.rb +0 -25
  252. data/test/unit/machine/machine_with_initialize_and_super_test.rb +0 -17
  253. data/test/unit/machine/machine_with_initialize_arguments_and_block_test.rb +0 -31
  254. data/test/unit/machine/machine_with_initialize_without_super_test.rb +0 -17
  255. data/test/unit/machine/machine_with_instance_helpers_test.rb +0 -179
  256. data/test/unit/machine/machine_with_integration_test.rb +0 -72
  257. data/test/unit/machine/machine_with_multiple_events_test.rb +0 -32
  258. data/test/unit/machine/machine_with_namespace_test.rb +0 -48
  259. data/test/unit/machine/machine_with_nil_action_test.rb +0 -27
  260. data/test/unit/machine/machine_with_other_states.rb +0 -22
  261. data/test/unit/machine/machine_with_owner_subclass_test.rb +0 -18
  262. data/test/unit/machine/machine_with_paths_test.rb +0 -25
  263. data/test/unit/machine/machine_with_private_action_test.rb +0 -43
  264. data/test/unit/machine/machine_with_state_matchers_test.rb +0 -41
  265. data/test/unit/machine/machine_with_state_with_matchers_test.rb +0 -19
  266. data/test/unit/machine/machine_with_states_test.rb +0 -55
  267. data/test/unit/machine/machine_with_states_with_behaviors_test.rb +0 -23
  268. data/test/unit/machine/machine_with_states_with_custom_human_names_test.rb +0 -18
  269. data/test/unit/machine/machine_with_states_with_custom_values_test.rb +0 -21
  270. data/test/unit/machine/machine_with_states_with_runtime_dependencies_test.rb +0 -19
  271. data/test/unit/machine/machine_with_static_initial_state_test.rb +0 -49
  272. data/test/unit/machine/machine_with_superclass_conflicting_helpers_after_definition_test.rb +0 -36
  273. data/test/unit/machine/machine_with_transition_callbacks_test.rb +0 -144
  274. data/test/unit/machine/machine_with_transitions_test.rb +0 -87
  275. data/test/unit/machine/machine_without_initialization_test.rb +0 -31
  276. data/test/unit/machine/machine_without_initialize_test.rb +0 -14
  277. data/test/unit/machine/machine_without_integration_test.rb +0 -31
  278. data/test/unit/machine_collection/machine_collection_by_default_test.rb +0 -11
  279. data/test/unit/machine_collection/machine_collection_fire_test.rb +0 -80
  280. data/test/unit/machine_collection/machine_collection_fire_with_transactions_test.rb +0 -54
  281. data/test/unit/machine_collection/machine_collection_fire_with_validations_test.rb +0 -76
  282. data/test/unit/machine_collection/machine_collection_state_initialization_test.rb +0 -111
  283. data/test/unit/machine_collection/machine_collection_transitions_with_blank_events_test.rb +0 -25
  284. data/test/unit/machine_collection/machine_collection_transitions_with_custom_options_test.rb +0 -20
  285. data/test/unit/machine_collection/machine_collection_transitions_with_different_actions_test.rb +0 -26
  286. data/test/unit/machine_collection/machine_collection_transitions_with_exisiting_transitions_test.rb +0 -25
  287. data/test/unit/machine_collection/machine_collection_transitions_with_invalid_events_test.rb +0 -25
  288. data/test/unit/machine_collection/machine_collection_transitions_with_same_actions_test.rb +0 -31
  289. data/test/unit/machine_collection/machine_collection_transitions_with_transition_test.rb +0 -26
  290. data/test/unit/machine_collection/machine_collection_transitions_without_events_test.rb +0 -25
  291. data/test/unit/machine_collection/machine_collection_transitions_without_transition_test.rb +0 -27
  292. data/test/unit/matcher/all_matcher_test.rb +0 -29
  293. data/test/unit/matcher/blacklist_matcher_test.rb +0 -30
  294. data/test/unit/matcher/loopback_matcher_test.rb +0 -27
  295. data/test/unit/matcher/matcher_by_default_test.rb +0 -15
  296. data/test/unit/matcher/matcher_with_multiple_values_test.rb +0 -15
  297. data/test/unit/matcher/matcher_with_value_test.rb +0 -15
  298. data/test/unit/matcher/whitelist_matcher_test.rb +0 -30
  299. data/test/unit/matcher_helpers/matcher_helpers_all_test.rb +0 -14
  300. data/test/unit/matcher_helpers/matcher_helpers_any_test.rb +0 -14
  301. data/test/unit/matcher_helpers/matcher_helpers_same_test.rb +0 -13
  302. data/test/unit/node_collection/node_collection_after_being_copied_test.rb +0 -46
  303. data/test/unit/node_collection/node_collection_after_update_test.rb +0 -36
  304. data/test/unit/node_collection/node_collection_by_default_test.rb +0 -22
  305. data/test/unit/node_collection/node_collection_test.rb +0 -23
  306. data/test/unit/node_collection/node_collection_with_indices_test.rb +0 -42
  307. data/test/unit/node_collection/node_collection_with_matcher_contexts_test.rb +0 -25
  308. data/test/unit/node_collection/node_collection_with_nodes_test.rb +0 -46
  309. data/test/unit/node_collection/node_collection_with_numeric_index_test.rb +0 -24
  310. data/test/unit/node_collection/node_collection_with_postdefined_contexts_test.rb +0 -22
  311. data/test/unit/node_collection/node_collection_with_predefined_contexts_test.rb +0 -23
  312. data/test/unit/node_collection/node_collection_with_string_index_test.rb +0 -20
  313. data/test/unit/node_collection/node_collection_with_symbol_index_test.rb +0 -20
  314. data/test/unit/node_collection/node_collection_without_indices_test.rb +0 -30
  315. data/test/unit/path/path_by_default_test.rb +0 -54
  316. data/test/unit/path/path_test.rb +0 -14
  317. data/test/unit/path/path_with_available_transitions_after_reaching_target_test.rb +0 -40
  318. data/test/unit/path/path_with_available_transitions_test.rb +0 -54
  319. data/test/unit/path/path_with_deep_target_reached_test.rb +0 -50
  320. data/test/unit/path/path_with_deep_target_test.rb +0 -40
  321. data/test/unit/path/path_with_duplicates_test.rb +0 -32
  322. data/test/unit/path/path_with_encountered_transitions_test.rb +0 -34
  323. data/test/unit/path/path_with_guarded_transitions_test.rb +0 -42
  324. data/test/unit/path/path_with_reached_target_test.rb +0 -35
  325. data/test/unit/path/path_with_transitions_test.rb +0 -54
  326. data/test/unit/path/path_with_unreached_target_test.rb +0 -31
  327. data/test/unit/path/path_without_transitions_test.rb +0 -24
  328. data/test/unit/path_collection/path_collection_by_default_test.rb +0 -46
  329. data/test/unit/path_collection/path_collection_test.rb +0 -24
  330. data/test/unit/path_collection/path_collection_with_deep_paths_test.rb +0 -43
  331. data/test/unit/path_collection/path_collection_with_duplicate_nodes_test.rb +0 -31
  332. data/test/unit/path_collection/path_collection_with_from_state_test.rb +0 -27
  333. data/test/unit/path_collection/path_collection_with_paths_test.rb +0 -47
  334. data/test/unit/path_collection/path_collection_with_to_state_test.rb +0 -29
  335. data/test/unit/path_collection/path_with_guarded_paths_test.rb +0 -25
  336. data/test/unit/state/state_after_being_copied_test.rb +0 -19
  337. data/test/unit/state/state_by_default_test.rb +0 -41
  338. data/test/unit/state/state_final_test.rb +0 -28
  339. data/test/unit/state/state_initial_test.rb +0 -13
  340. data/test/unit/state/state_not_final_test.rb +0 -32
  341. data/test/unit/state/state_not_initial_test.rb +0 -13
  342. data/test/unit/state/state_test.rb +0 -44
  343. data/test/unit/state/state_with_cached_lambda_value_test.rb +0 -29
  344. data/test/unit/state/state_with_conflicting_helpers_after_definition_test.rb +0 -38
  345. data/test/unit/state/state_with_conflicting_helpers_before_definition_test.rb +0 -29
  346. data/test/unit/state/state_with_conflicting_machine_name_test.rb +0 -20
  347. data/test/unit/state/state_with_conflicting_machine_test.rb +0 -37
  348. data/test/unit/state/state_with_context_test.rb +0 -60
  349. data/test/unit/state/state_with_dynamic_human_name_test.rb +0 -25
  350. data/test/unit/state/state_with_existing_context_method_test.rb +0 -24
  351. data/test/unit/state/state_with_human_name_test.rb +0 -13
  352. data/test/unit/state/state_with_integer_value_test.rb +0 -32
  353. data/test/unit/state/state_with_invalid_method_call_test.rb +0 -21
  354. data/test/unit/state/state_with_lambda_value_test.rb +0 -37
  355. data/test/unit/state/state_with_matcher_test.rb +0 -18
  356. data/test/unit/state/state_with_multiple_contexts_test.rb +0 -57
  357. data/test/unit/state/state_with_name_test.rb +0 -43
  358. data/test/unit/state/state_with_namespace_test.rb +0 -22
  359. data/test/unit/state/state_with_nil_value_test.rb +0 -35
  360. data/test/unit/state/state_with_redefined_context_method_test.rb +0 -45
  361. data/test/unit/state/state_with_symbolic_value_test.rb +0 -32
  362. data/test/unit/state/state_with_valid_inherited_method_call_for_current_state_test.rb +0 -40
  363. data/test/unit/state/state_with_valid_method_call_for_current_state_test.rb +0 -33
  364. data/test/unit/state/state_with_valid_method_call_for_different_state_test.rb +0 -41
  365. data/test/unit/state/state_without_cached_lambda_value_test.rb +0 -25
  366. data/test/unit/state/state_without_name_test.rb +0 -39
  367. data/test/unit/state_collection/state_collection_by_default_test.rb +0 -21
  368. data/test/unit/state_collection/state_collection_string_test.rb +0 -35
  369. data/test/unit/state_collection/state_collection_test.rb +0 -74
  370. data/test/unit/state_collection/state_collection_with_custom_state_values_test.rb +0 -29
  371. data/test/unit/state_collection/state_collection_with_event_transitions_test.rb +0 -39
  372. data/test/unit/state_collection/state_collection_with_initial_state_test.rb +0 -40
  373. data/test/unit/state_collection/state_collection_with_namespace_test.rb +0 -21
  374. data/test/unit/state_collection/state_collection_with_state_behaviors_test.rb +0 -40
  375. data/test/unit/state_collection/state_collection_with_state_matchers_test.rb +0 -29
  376. data/test/unit/state_collection/state_collection_with_transition_callbacks_test.rb +0 -40
  377. data/test/unit/state_context/state_context_proxy_test.rb +0 -26
  378. data/test/unit/state_context/state_context_proxy_with_if_and_unless_conditions_test.rb +0 -42
  379. data/test/unit/state_context/state_context_proxy_with_if_condition_test.rb +0 -64
  380. data/test/unit/state_context/state_context_proxy_with_multiple_if_conditions_test.rb +0 -32
  381. data/test/unit/state_context/state_context_proxy_with_multiple_unless_conditions_test.rb +0 -32
  382. data/test/unit/state_context/state_context_proxy_with_unless_condition_test.rb +0 -64
  383. data/test/unit/state_context/state_context_proxy_without_conditions_test.rb +0 -31
  384. data/test/unit/state_context/state_context_test.rb +0 -28
  385. data/test/unit/state_context/state_context_transition_test.rb +0 -104
  386. data/test/unit/state_context/state_context_with_matching_transition_test.rb +0 -27
  387. data/test/unit/state_machine/state_machine_by_default_test.rb +0 -12
  388. data/test/unit/state_machine/state_machine_test.rb +0 -20
  389. data/test/unit/transition/transition_after_being_performed_test.rb +0 -48
  390. data/test/unit/transition/transition_after_being_persisted_test.rb +0 -46
  391. data/test/unit/transition/transition_after_being_rolled_back_test.rb +0 -35
  392. data/test/unit/transition/transition_equality_test.rb +0 -52
  393. data/test/unit/transition/transition_loopback_test.rb +0 -18
  394. data/test/unit/transition/transition_test.rb +0 -96
  395. data/test/unit/transition/transition_transient_test.rb +0 -20
  396. data/test/unit/transition/transition_with_action_test.rb +0 -27
  397. data/test/unit/transition/transition_with_after_callbacks_skipped_test.rb +0 -127
  398. data/test/unit/transition/transition_with_after_callbacks_test.rb +0 -93
  399. data/test/unit/transition/transition_with_around_callbacks_test.rb +0 -141
  400. data/test/unit/transition/transition_with_before_callbacks_skipped_test.rb +0 -30
  401. data/test/unit/transition/transition_with_before_callbacks_test.rb +0 -104
  402. data/test/unit/transition/transition_with_custom_machine_attribute_test.rb +0 -28
  403. data/test/unit/transition/transition_with_different_states_test.rb +0 -18
  404. data/test/unit/transition/transition_with_dynamic_to_value_test.rb +0 -19
  405. data/test/unit/transition/transition_with_failure_callbacks_test.rb +0 -84
  406. data/test/unit/transition/transition_with_invalid_nodes_test.rb +0 -29
  407. data/test/unit/transition/transition_with_mixed_callbacks_test.rb +0 -105
  408. data/test/unit/transition/transition_with_multiple_after_callbacks_test.rb +0 -40
  409. data/test/unit/transition/transition_with_multiple_around_callbacks_test.rb +0 -114
  410. data/test/unit/transition/transition_with_multiple_before_callbacks_test.rb +0 -40
  411. data/test/unit/transition/transition_with_multiple_failure_callbacks_test.rb +0 -40
  412. data/test/unit/transition/transition_with_namespace_test.rb +0 -47
  413. data/test/unit/transition/transition_with_perform_arguments_test.rb +0 -35
  414. data/test/unit/transition/transition_with_transactions_test.rb +0 -42
  415. data/test/unit/transition/transition_without_callbacks_test.rb +0 -33
  416. data/test/unit/transition/transition_without_reading_state_test.rb +0 -22
  417. data/test/unit/transition/transition_without_running_action_test.rb +0 -47
  418. data/test/unit/transition_collection/attribute_transition_collection_by_default_test.rb +0 -23
  419. data/test/unit/transition_collection/attribute_transition_collection_marshalling_test.rb +0 -64
  420. data/test/unit/transition_collection/attribute_transition_collection_with_action_error_test.rb +0 -44
  421. data/test/unit/transition_collection/attribute_transition_collection_with_action_failed_test.rb +0 -44
  422. data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_error_test.rb +0 -32
  423. data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_halt_test.rb +0 -33
  424. data/test/unit/transition_collection/attribute_transition_collection_with_around_after_yield_callback_error_test.rb +0 -32
  425. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_error_test.rb +0 -32
  426. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_halt_test.rb +0 -33
  427. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_before_yield_halt_test.rb +0 -33
  428. data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_error_test.rb +0 -32
  429. data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_halt_test.rb +0 -33
  430. data/test/unit/transition_collection/attribute_transition_collection_with_callbacks_test.rb +0 -68
  431. data/test/unit/transition_collection/attribute_transition_collection_with_event_transitions_test.rb +0 -41
  432. data/test/unit/transition_collection/attribute_transition_collection_with_events_test.rb +0 -44
  433. data/test/unit/transition_collection/attribute_transition_collection_with_skipped_after_callbacks_test.rb +0 -42
  434. data/test/unit/transition_collection/transition_collection_by_default_test.rb +0 -23
  435. data/test/unit/transition_collection/transition_collection_empty_with_block_test.rb +0 -23
  436. data/test/unit/transition_collection/transition_collection_empty_without_block_test.rb +0 -12
  437. data/test/unit/transition_collection/transition_collection_invalid_test.rb +0 -21
  438. data/test/unit/transition_collection/transition_collection_partial_invalid_test.rb +0 -69
  439. data/test/unit/transition_collection/transition_collection_test.rb +0 -26
  440. data/test/unit/transition_collection/transition_collection_valid_test.rb +0 -57
  441. data/test/unit/transition_collection/transition_collection_with_action_error_test.rb +0 -66
  442. data/test/unit/transition_collection/transition_collection_with_action_failed_test.rb +0 -60
  443. data/test/unit/transition_collection/transition_collection_with_action_hook_and_block_test.rb +0 -17
  444. data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_action_test.rb +0 -17
  445. data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_after_callbacks_test.rb +0 -37
  446. data/test/unit/transition_collection/transition_collection_with_action_hook_base_test.rb +0 -34
  447. data/test/unit/transition_collection/transition_collection_with_action_hook_error_test.rb +0 -29
  448. data/test/unit/transition_collection/transition_collection_with_action_hook_invalid_test.rb +0 -17
  449. data/test/unit/transition_collection/transition_collection_with_action_hook_multiple_test.rb +0 -79
  450. data/test/unit/transition_collection/transition_collection_with_action_hook_test.rb +0 -45
  451. data/test/unit/transition_collection/transition_collection_with_action_hook_with_different_actions_test.rb +0 -48
  452. data/test/unit/transition_collection/transition_collection_with_action_hook_with_nil_action_test.rb +0 -42
  453. data/test/unit/transition_collection/transition_collection_with_after_callback_halt_test.rb +0 -47
  454. data/test/unit/transition_collection/transition_collection_with_before_callback_halt_test.rb +0 -51
  455. data/test/unit/transition_collection/transition_collection_with_block_test.rb +0 -46
  456. data/test/unit/transition_collection/transition_collection_with_callbacks_test.rb +0 -135
  457. data/test/unit/transition_collection/transition_collection_with_different_actions_test.rb +0 -189
  458. data/test/unit/transition_collection/transition_collection_with_duplicate_actions_test.rb +0 -48
  459. data/test/unit/transition_collection/transition_collection_with_empty_actions_test.rb +0 -41
  460. data/test/unit/transition_collection/transition_collection_with_mixed_actions_test.rb +0 -41
  461. data/test/unit/transition_collection/transition_collection_with_skipped_actions_and_block_test.rb +0 -34
  462. data/test/unit/transition_collection/transition_collection_with_skipped_actions_test.rb +0 -69
  463. data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_and_around_callbacks_test.rb +0 -53
  464. data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_test.rb +0 -34
  465. data/test/unit/transition_collection/transition_collection_with_transactions_test.rb +0 -65
  466. data/test/unit/transition_collection/transition_collection_without_transactions_test.rb +0 -29
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module StateMachines
2
4
  # Provides a general strategy pattern for determining whether a match is found
3
5
  # for a value. The algorithm that actually determines the match depends on
@@ -5,114 +7,115 @@ module StateMachines
5
7
  class Matcher
6
8
  # The list of values against which queries are matched
7
9
  attr_reader :values
8
-
10
+
9
11
  # Creates a new matcher for querying against the given set of values
10
12
  def initialize(values = [])
11
13
  @values = values.is_a?(Array) ? values : [values]
12
14
  end
13
-
15
+
14
16
  # Generates a subset of values that exists in both the set of values being
15
17
  # filtered and the values configured for the matcher
16
18
  def filter(values)
17
19
  self.values & values
18
20
  end
19
21
  end
20
-
22
+
21
23
  # Matches any given value. Since there is no configuration for this type of
22
24
  # matcher, it must be used as a singleton.
23
25
  class AllMatcher < Matcher
24
26
  include Singleton
25
-
27
+
26
28
  # Generates a blacklist matcher based on the given set of values
27
- #
29
+ #
28
30
  # == Examples
29
- #
31
+ #
30
32
  # matcher = StateMachines::AllMatcher.instance - [:parked, :idling]
31
33
  # matcher.matches?(:parked) # => false
32
34
  # matcher.matches?(:first_gear) # => true
33
35
  def -(blacklist)
34
36
  BlacklistMatcher.new(blacklist)
35
37
  end
36
-
38
+ alias_method :except, :-
39
+
37
40
  # Always returns true
38
41
  def matches?(value, context = {})
39
42
  true
40
43
  end
41
-
44
+
42
45
  # Always returns the given set of values
43
46
  def filter(values)
44
47
  values
45
48
  end
46
-
49
+
47
50
  # A human-readable description of this matcher. Always "all".
48
51
  def description
49
52
  'all'
50
53
  end
51
54
  end
52
-
55
+
53
56
  # Matches a specific set of values
54
57
  class WhitelistMatcher < Matcher
55
58
  # Checks whether the given value exists within the whitelist configured
56
59
  # for this matcher.
57
- #
60
+ #
58
61
  # == Examples
59
- #
62
+ #
60
63
  # matcher = StateMachines::WhitelistMatcher.new([:parked, :idling])
61
64
  # matcher.matches?(:parked) # => true
62
65
  # matcher.matches?(:first_gear) # => false
63
66
  def matches?(value, context = {})
64
67
  values.include?(value)
65
68
  end
66
-
69
+
67
70
  # A human-readable description of this matcher
68
71
  def description
69
72
  values.length == 1 ? values.first.inspect : values.inspect
70
73
  end
71
74
  end
72
-
75
+
73
76
  # Matches everything but a specific set of values
74
77
  class BlacklistMatcher < Matcher
75
78
  # Checks whether the given value exists outside the blacklist configured
76
79
  # for this matcher.
77
- #
80
+ #
78
81
  # == Examples
79
- #
82
+ #
80
83
  # matcher = StateMachines::BlacklistMatcher.new([:parked, :idling])
81
84
  # matcher.matches?(:parked) # => false
82
85
  # matcher.matches?(:first_gear) # => true
83
86
  def matches?(value, context = {})
84
87
  !values.include?(value)
85
88
  end
86
-
89
+
87
90
  # Finds all values that are *not* within the blacklist configured for this
88
91
  # matcher
89
92
  def filter(values)
90
93
  values - self.values
91
94
  end
92
-
95
+
93
96
  # A human-readable description of this matcher
94
97
  def description
95
98
  "all - #{values.length == 1 ? values.first.inspect : values.inspect}"
96
99
  end
97
100
  end
98
-
101
+
99
102
  # Matches a loopback of two values within a context. Since there is no
100
103
  # configuration for this type of matcher, it must be used as a singleton.
101
104
  class LoopbackMatcher < Matcher
102
105
  include Singleton
103
-
106
+
104
107
  # Checks whether the given value matches what the value originally was.
105
108
  # This value should be defined in the context.
106
- #
109
+ #
107
110
  # == Examples
108
- #
111
+ #
109
112
  # matcher = StateMachines::LoopbackMatcher.instance
110
113
  # matcher.matches?(:parked, :from => :parked) # => true
111
114
  # matcher.matches?(:parked, :from => :idling) # => false
112
115
  def matches?(value, context)
113
116
  context[:from] == value
114
117
  end
115
-
118
+
116
119
  # A human-readable description of this matcher. Always "same".
117
120
  def description
118
121
  'same'
@@ -1,25 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module StateMachines
2
4
  # Provides a set of helper methods for generating matchers
3
5
  module MatcherHelpers
4
6
  # Represents a state that matches all known states in a machine.
5
- #
7
+ #
6
8
  # == Examples
7
- #
9
+ #
8
10
  # class Vehicle
9
11
  # state_machine do
10
12
  # before_transition any => :parked, :do => lambda {...}
11
13
  # before_transition all - :parked => all - :idling, :do => lambda {}
12
- #
14
+ #
13
15
  # event :park
14
16
  # transition all => :parked
15
17
  # end
16
- #
18
+ #
17
19
  # event :crash
18
20
  # transition all - :parked => :stalled
19
21
  # end
20
22
  # end
21
23
  # end
22
- #
24
+ #
23
25
  # In the above example, +all+ will match the following states since they
24
26
  # are known:
25
27
  # * +parked+
@@ -29,12 +31,12 @@ module StateMachines
29
31
  AllMatcher.instance
30
32
  end
31
33
  alias_method :any, :all
32
-
34
+
33
35
  # Represents a state that matches the original +from+ state. This is useful
34
36
  # for defining transitions which are loopbacks.
35
- #
37
+ #
36
38
  # == Examples
37
- #
39
+ #
38
40
  # class Vehicle
39
41
  # state_machine do
40
42
  # event :ignite
@@ -42,11 +44,11 @@ module StateMachines
42
44
  # end
43
45
  # end
44
46
  # end
45
- #
47
+ #
46
48
  # In the above example, +same+ will match whichever the from state is. In
47
49
  # the case of the +ignite+ event, it is essential the same as the following:
48
- #
49
- # transition :parked => :parked, :first_gear => :first_gear
50
+ #
51
+ # transition :idling => :idling, :first_gear => :first_gear
50
52
  def same
51
53
  LoopbackMatcher.instance
52
54
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options_validator'
4
+
1
5
  module StateMachines
2
6
  # Represents a collection of nodes in a state machine, be it events or states.
3
7
  # Nodes will not differentiate between the String and Symbol versions of the
@@ -16,7 +20,7 @@ module StateMachines
16
20
  # hashed indices for in order to perform quick lookups. Default is to
17
21
  # index by the :name attribute
18
22
  def initialize(machine, options = {})
19
- options.assert_valid_keys(:index)
23
+ StateMachines::OptionsValidator.assert_valid_keys!(options, :index)
20
24
  options = { index: :name }.merge(options)
21
25
 
22
26
  @machine = machine
@@ -162,37 +166,39 @@ module StateMachines
162
166
  self[key, index_name] || fail(IndexError, "#{key.inspect} is an invalid #{index_name}")
163
167
  end
164
168
 
165
- protected
166
- # Gets the given index. If the index does not exist, then an ArgumentError
167
- # is raised.
169
+ protected
170
+
171
+ # Gets the given index. If the index does not exist, then an ArgumentError
172
+ # is raised.
168
173
  def index(name)
169
174
  fail ArgumentError, 'No indices configured' unless @indices.any?
175
+
170
176
  @indices[name] || fail(ArgumentError, "Invalid index: #{name.inspect}")
171
177
  end
172
178
 
173
- # Gets the value for the given attribute on the node
179
+ # Gets the value for the given attribute on the node
174
180
  def value(node, attribute)
175
181
  node.send(attribute)
176
182
  end
177
183
 
178
- # Adds the given key / node combination to an index, including the string
179
- # and symbol versions of the index
184
+ # Adds the given key / node combination to an index, including the string
185
+ # and symbol versions of the index
180
186
  def add_to_index(name, key, node)
181
187
  index(name)[key] = node
182
188
  index(:"#{name}_to_s")[key.to_s] = node
183
189
  index(:"#{name}_to_sym")[:"#{key}"] = node if to_sym?(key)
184
190
  end
185
191
 
186
- # Removes the given key from an index, including the string and symbol
187
- # versions of the index
192
+ # Removes the given key from an index, including the string and symbol
193
+ # versions of the index
188
194
  def remove_from_index(name, key)
189
195
  index(name).delete(key)
190
196
  index(:"#{name}_to_s").delete(key.to_s)
191
197
  index(:"#{name}_to_sym").delete(:"#{key}") if to_sym?(key)
192
198
  end
193
199
 
194
- # Updates the node for the given index, including the string and symbol
195
- # versions of the index
200
+ # Updates the node for the given index, including the string and symbol
201
+ # versions of the index
196
202
  def update_index(name, node)
197
203
  index = self.index(name)
198
204
  old_key = index.key(node)
@@ -205,13 +211,13 @@ module StateMachines
205
211
  end
206
212
  end
207
213
 
208
- # Determines whether the given value can be converted to a symbol
214
+ # Determines whether the given value can be converted to a symbol
209
215
  def to_sym?(value)
210
216
  "#{value}" != ''
211
217
  end
212
218
 
213
- # Evaluates the given context for a particular node. This will only
214
- # evaluate the context if the node matches.
219
+ # Evaluates the given context for a particular node. This will only
220
+ # evaluate the context if the node matches.
215
221
  def eval_context(context, node)
216
222
  node.context(&context[:block]) if context[:nodes].matches?(node.name)
217
223
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StateMachines
4
+ # Define the module if it doesn't exist yet
5
+ # Module for validating options without monkey-patching Hash
6
+ # Provides the same functionality as the Hash monkey patch but in a cleaner way
7
+ module OptionsValidator
8
+ class << self
9
+ # Validates that all keys in the options hash are in the list of valid keys
10
+ #
11
+ # @param options [Hash] The options hash to validate
12
+ # @param valid_keys [Array<Symbol>] List of valid key names
13
+ # @param caller_info [String] Information about the calling method for better error messages
14
+ # @raise [ArgumentError] If any invalid keys are found
15
+ def assert_valid_keys!(options, *valid_keys, caller_info: nil)
16
+ return if options.empty?
17
+
18
+ valid_keys.flatten!
19
+ invalid_keys = options.keys - valid_keys
20
+
21
+ return if invalid_keys.empty?
22
+
23
+ caller_context = caller_info ? " in #{caller_info}" : ''
24
+ raise ArgumentError, "Unknown key#{'s' if invalid_keys.length > 1}: #{invalid_keys.map(&:inspect).join(', ')}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}#{caller_context}"
25
+ end
26
+
27
+ # Validates that at most one of the exclusive keys is present in the options hash
28
+ #
29
+ # @param options [Hash] The options hash to validate
30
+ # @param exclusive_keys [Array<Symbol>] List of mutually exclusive keys
31
+ # @param caller_info [String] Information about the calling method for better error messages
32
+ # @raise [ArgumentError] If more than one exclusive key is found
33
+ def assert_exclusive_keys!(options, *exclusive_keys, caller_info: nil)
34
+ return if options.empty?
35
+
36
+ conflicting_keys = exclusive_keys & options.keys
37
+ return if conflicting_keys.length <= 1
38
+
39
+ caller_context = caller_info ? " in #{caller_info}" : ''
40
+ raise ArgumentError, "Conflicting keys: #{conflicting_keys.join(', ')}#{caller_context}"
41
+ end
42
+
43
+ # Validates options using a more convenient interface that works with both
44
+ # hash-style and kwargs-style method definitions
45
+ #
46
+ # @param valid_keys [Array<Symbol>] List of valid key names
47
+ # @param exclusive_key_groups [Array<Array<Symbol>>] Groups of mutually exclusive keys
48
+ # @param caller_info [String] Information about the calling method
49
+ # @return [Proc] A validation proc that can be called with options
50
+ def validator(valid_keys: [], exclusive_key_groups: [], caller_info: nil)
51
+ proc do |options|
52
+ assert_valid_keys!(options, *valid_keys, caller_info: caller_info) unless valid_keys.empty?
53
+
54
+ exclusive_key_groups.each do |group|
55
+ assert_exclusive_keys!(options, *group, caller_info: caller_info)
56
+ end
57
+ end
58
+ end
59
+
60
+ # Helper method for backwards compatibility - allows gradual migration
61
+ # from Hash monkey patch to this module
62
+ #
63
+ # @param options [Hash] The options to validate
64
+ # @param valid_keys [Array<Symbol>] Valid keys
65
+ # @return [Hash] The same options hash (for chaining)
66
+ def validate_and_return(options, *valid_keys)
67
+ assert_valid_keys!(options, *valid_keys)
68
+ options
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,82 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options_validator'
4
+
1
5
  module StateMachines
2
6
  # A path represents a sequence of transitions that can be run for a particular
3
7
  # object. Paths can walk to new transitions, revealing all of the possible
4
8
  # branches that can be encountered in the object's state machine.
5
9
  class Path < Array
6
10
 
7
-
11
+
8
12
  # The object whose state machine is being walked
9
13
  attr_reader :object
10
-
14
+
11
15
  # The state machine this path is walking
12
16
  attr_reader :machine
13
-
17
+
14
18
  # Creates a new transition path for the given object. Initially this is an
15
19
  # empty path. In order to start walking the path, it must be populated with
16
20
  # an initial transition.
17
- #
21
+ #
18
22
  # Configuration options:
19
23
  # * <tt>:target</tt> - The target state to end the path on
20
24
  # * <tt>:guard</tt> - Whether to guard transitions with the if/unless
21
25
  # conditionals defined for each one
22
26
  def initialize(object, machine, options = {})
23
- options.assert_valid_keys(:target, :guard)
24
-
27
+ StateMachines::OptionsValidator.assert_valid_keys!(options, :target, :guard)
28
+
25
29
  @object = object
26
30
  @machine = machine
27
31
  @target = options[:target]
28
32
  @guard = options[:guard]
29
33
  end
30
-
34
+
31
35
  def initialize_copy(orig) #:nodoc:
32
36
  super
33
37
  @transitions = nil
34
38
  end
35
-
39
+
36
40
  # The initial state name for this path
37
41
  def from_name
38
- first && first.from_name
42
+ first&.from_name
39
43
  end
40
-
44
+
41
45
  # Lists all of the from states that can be reached through this path.
42
- #
46
+ #
43
47
  # For example,
44
- #
48
+ #
45
49
  # path.to_states # => [:parked, :idling, :first_gear, ...]
46
50
  def from_states
47
- map {|transition| transition.from_name}.uniq
51
+ map { |transition| transition.from_name }.uniq
48
52
  end
49
-
53
+
50
54
  # The end state name for this path. If a target state was specified for
51
55
  # the path, then that will be returned if the path is complete.
52
56
  def to_name
53
- last && last.to_name
57
+ last&.to_name
54
58
  end
55
-
59
+
56
60
  # Lists all of the to states that can be reached through this path.
57
- #
61
+ #
58
62
  # For example,
59
- #
63
+ #
60
64
  # path.to_states # => [:parked, :idling, :first_gear, ...]
61
65
  def to_states
62
- map {|transition| transition.to_name}.uniq
66
+ map { |transition| transition.to_name }.uniq
63
67
  end
64
-
68
+
65
69
  # Lists all of the events that can be fired through this path.
66
- #
70
+ #
67
71
  # For example,
68
- #
72
+ #
69
73
  # path.events # => [:park, :ignite, :shift_up, ...]
70
74
  def events
71
- map {|transition| transition.event}.uniq
75
+ map { |transition| transition.event }.uniq
72
76
  end
73
-
77
+
74
78
  # Walks down the next transitions at the end of this path. This will only
75
79
  # walk down paths that are considered valid.
76
80
  def walk
77
- transitions.each {|transition| yield dup.push(transition)}
81
+ transitions.each { |transition| yield dup.push(transition) }
78
82
  end
79
-
83
+
80
84
  # Determines whether or not this path has completed. A path is considered
81
85
  # complete when one of the following conditions is met:
82
86
  # * The last transition in the path ends on the target state
@@ -85,36 +89,37 @@ module StateMachines
85
89
  def complete?
86
90
  !empty? && (@target ? to_name == @target : transitions.empty?)
87
91
  end
88
-
89
- private
90
- # Calculates the number of times the given state has been walked to
91
- def times_walked_to(state)
92
- select {|transition| transition.to_name == state}.length
93
- end
94
-
95
- # Determines whether the given transition has been recently walked down in
96
- # this path. If a target is configured for this path, then this will only
97
- # look at transitions walked down since the target was last reached.
98
- def recently_walked?(transition)
99
- transitions = self
100
- if @target && @target != to_name && target_transition = detect {|t| t.to_name == @target}
101
- transitions = transitions[index(target_transition) + 1..-1]
102
- end
103
- transitions.include?(transition)
104
- end
105
-
106
- # Determines whether it's possible to walk to the given transition from
107
- # the current path. A transition can be walked to if:
108
- # * It has not been recently walked and
109
- # * If a target is specified, it has not been walked to twice yet
110
- def can_walk_to?(transition)
111
- !recently_walked?(transition) && (!@target || times_walked_to(@target) < 2)
112
- end
113
-
114
- # Get the next set of transitions that can be walked to starting from the
115
- # end of this path
116
- def transitions
117
- @transitions ||= empty? ? [] : machine.events.transitions_for(object, :from => to_name, :guard => @guard).select {|transition| can_walk_to?(transition)}
92
+
93
+ private
94
+
95
+ # Calculates the number of times the given state has been walked to
96
+ def times_walked_to(state)
97
+ select { |transition| transition.to_name == state }.length
98
+ end
99
+
100
+ # Determines whether the given transition has been recently walked down in
101
+ # this path. If a target is configured for this path, then this will only
102
+ # look at transitions walked down since the target was last reached.
103
+ def recently_walked?(transition)
104
+ transitions = self
105
+ if @target && @target != to_name && (target_transition = detect { |t| t.to_name == @target })
106
+ transitions = transitions[index(target_transition) + 1..-1]
118
107
  end
108
+ transitions.include?(transition)
109
+ end
110
+
111
+ # Determines whether it's possible to walk to the given transition from
112
+ # the current path. A transition can be walked to if:
113
+ # * It has not been recently walked and
114
+ # * If a target is specified, it has not been walked to twice yet
115
+ def can_walk_to?(transition)
116
+ !recently_walked?(transition) && (!@target || times_walked_to(@target) < 2)
117
+ end
118
+
119
+ # Get the next set of transitions that can be walked to starting from the
120
+ # end of this path
121
+ def transitions
122
+ @transitions ||= empty? ? [] : machine.events.transitions_for(object, from: to_name, guard: @guard).select { |transition| can_walk_to?(transition) }
123
+ end
119
124
  end
120
125
  end
@@ -1,23 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options_validator'
4
+
1
5
  module StateMachines
2
6
  # Represents a collection of paths that are generated based on a set of
3
7
  # requirements regarding what states to start and end on
4
8
  class PathCollection < Array
5
9
 
6
-
7
10
  # The object whose state machine is being walked
8
11
  attr_reader :object
9
-
12
+
10
13
  # The state machine these path are walking
11
14
  attr_reader :machine
12
-
15
+
13
16
  # The initial state to start each path from
14
17
  attr_reader :from_name
15
-
18
+
16
19
  # The target state for each path
17
20
  attr_reader :to_name
18
-
21
+
19
22
  # Creates a new collection of paths with the given requirements.
20
- #
23
+ #
21
24
  # Configuration options:
22
25
  # * <tt>:from</tt> - The initial state to start from
23
26
  # * <tt>:to</tt> - The target end state
@@ -25,64 +28,65 @@ module StateMachines
25
28
  # * <tt>:guard</tt> - Whether to guard transitions with the if/unless
26
29
  # conditionals defined for each one
27
30
  def initialize(object, machine, options = {})
28
- options = {:deep => false, :from => machine.states.match!(object).name}.merge(options)
29
- options.assert_valid_keys( :from, :to, :deep, :guard)
30
-
31
+ options = {deep: false, from: machine.states.match!(object).name}.merge(options)
32
+ StateMachines::OptionsValidator.assert_valid_keys!(options, :from, :to, :deep, :guard)
33
+
31
34
  @object = object
32
35
  @machine = machine
33
36
  @from_name = machine.states.fetch(options[:from]).name
34
37
  @to_name = options[:to] && machine.states.fetch(options[:to]).name
35
38
  @guard = options[:guard]
36
39
  @deep = options[:deep]
37
-
38
- initial_paths.each {|path| walk(path)}
40
+
41
+ initial_paths.each { |path| walk(path) }
39
42
  end
40
-
43
+
41
44
  # Lists all of the states that can be transitioned from through the paths in
42
45
  # this collection.
43
- #
46
+ #
44
47
  # For example,
45
- #
48
+ #
46
49
  # paths.from_states # => [:parked, :idling, :first_gear, ...]
47
50
  def from_states
48
51
  flat_map(&:from_states).uniq
49
52
  end
50
-
53
+
51
54
  # Lists all of the states that can be transitioned to through the paths in
52
55
  # this collection.
53
- #
56
+ #
54
57
  # For example,
55
- #
58
+ #
56
59
  # paths.to_states # => [:idling, :first_gear, :second_gear, ...]
57
60
  def to_states
58
61
  flat_map(&:to_states).uniq
59
62
  end
60
-
63
+
61
64
  # Lists all of the events that can be fired through the paths in this
62
65
  # collection.
63
- #
66
+ #
64
67
  # For example,
65
- #
68
+ #
66
69
  # paths.events # => [:park, :ignite, :shift_up, ...]
67
70
  def events
68
71
  flat_map(&:events).uniq
69
72
  end
70
-
71
- private
72
- # Gets the initial set of paths to walk
73
- def initial_paths
74
- machine.events.transitions_for(object, :from => from_name, :guard => @guard).map do |transition|
75
- path = Path.new(object, machine, :target => to_name, :guard => @guard)
76
- path << transition
77
- path
78
- end
79
- end
80
-
81
- # Walks down the given path. Each new path that matches the configured
82
- # requirements will be added to this collection.
83
- def walk(path)
84
- self << path if path.complete?
85
- path.walk {|next_path| walk(next_path)} unless to_name && path.complete? && !@deep
73
+
74
+ private
75
+
76
+ # Gets the initial set of paths to walk
77
+ def initial_paths
78
+ machine.events.transitions_for(object, from: from_name, guard: @guard).map do |transition|
79
+ path = Path.new(object, machine, target: to_name, guard: @guard)
80
+ path << transition
81
+ path
86
82
  end
83
+ end
84
+
85
+ # Walks down the given path. Each new path that matches the configured
86
+ # requirements will be added to this collection.
87
+ def walk(path)
88
+ self << path if path.complete?
89
+ path.walk { |next_path| walk(next_path) } unless to_name && path.complete? && !@deep
90
+ end
87
91
  end
88
92
  end