state_machines 0.5.0 → 0.50.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 (486) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +1 -1
  3. data/README.md +443 -22
  4. data/lib/state_machines/async_mode/async_event_extensions.rb +49 -0
  5. data/lib/state_machines/async_mode/async_events.rb +282 -0
  6. data/lib/state_machines/async_mode/async_machine.rb +60 -0
  7. data/lib/state_machines/async_mode/async_transition_collection.rb +141 -0
  8. data/lib/state_machines/async_mode/thread_safe_state.rb +47 -0
  9. data/lib/state_machines/async_mode.rb +64 -0
  10. data/lib/state_machines/branch.rb +146 -86
  11. data/lib/state_machines/callback.rb +35 -32
  12. data/lib/state_machines/core.rb +3 -3
  13. data/lib/state_machines/core_ext/class/state_machine.rb +2 -0
  14. data/lib/state_machines/core_ext.rb +2 -0
  15. data/lib/state_machines/error.rb +7 -4
  16. data/lib/state_machines/eval_helpers.rb +197 -39
  17. data/lib/state_machines/event.rb +77 -58
  18. data/lib/state_machines/event_collection.rb +49 -39
  19. data/lib/state_machines/extensions.rb +6 -4
  20. data/lib/state_machines/helper_module.rb +4 -2
  21. data/lib/state_machines/integrations/base.rb +3 -1
  22. data/lib/state_machines/integrations.rb +19 -20
  23. data/lib/state_machines/machine/action_hooks.rb +53 -0
  24. data/lib/state_machines/machine/async_extensions.rb +88 -0
  25. data/lib/state_machines/machine/callbacks.rb +59 -0
  26. data/lib/state_machines/machine/class_methods.rb +97 -0
  27. data/lib/state_machines/machine/configuration.rb +134 -0
  28. data/lib/state_machines/machine/event_methods.rb +59 -0
  29. data/lib/state_machines/machine/helper_generators.rb +125 -0
  30. data/lib/state_machines/machine/integration.rb +70 -0
  31. data/lib/state_machines/machine/parsing.rb +77 -0
  32. data/lib/state_machines/machine/rendering.rb +17 -0
  33. data/lib/state_machines/machine/scoping.rb +44 -0
  34. data/lib/state_machines/machine/state_methods.rb +101 -0
  35. data/lib/state_machines/machine/utilities.rb +85 -0
  36. data/lib/state_machines/machine/validation.rb +39 -0
  37. data/lib/state_machines/machine.rb +425 -1011
  38. data/lib/state_machines/machine_collection.rb +28 -19
  39. data/lib/state_machines/macro_methods.rb +104 -102
  40. data/lib/state_machines/matcher.rb +31 -28
  41. data/lib/state_machines/matcher_helpers.rb +14 -12
  42. data/lib/state_machines/node_collection.rb +36 -29
  43. data/lib/state_machines/options_validator.rb +72 -0
  44. data/lib/state_machines/path.rb +60 -57
  45. data/lib/state_machines/path_collection.rb +39 -36
  46. data/lib/state_machines/state.rb +84 -47
  47. data/lib/state_machines/state_collection.rb +22 -19
  48. data/lib/state_machines/state_context.rb +40 -39
  49. data/lib/state_machines/stdio_renderer.rb +74 -0
  50. data/lib/state_machines/syntax_validator.rb +57 -0
  51. data/lib/state_machines/test_helper.rb +896 -0
  52. data/lib/state_machines/transition.rb +215 -199
  53. data/lib/state_machines/transition_collection.rb +187 -170
  54. data/lib/state_machines/version.rb +3 -1
  55. data/lib/state_machines.rb +4 -1
  56. metadata +39 -446
  57. data/.gitignore +0 -21
  58. data/.rspec +0 -3
  59. data/.ruby-gemset +0 -1
  60. data/.ruby-version +0 -1
  61. data/.travis.yml +0 -16
  62. data/Changelog.md +0 -22
  63. data/Contributors.md +0 -39
  64. data/Gemfile +0 -8
  65. data/Rakefile +0 -12
  66. data/Testing.md +0 -0
  67. data/lib/state_machines/assertions.rb +0 -40
  68. data/state_machines.gemspec +0 -22
  69. data/test/files/integrations/event_on_failure_integration.rb +0 -10
  70. data/test/files/integrations/vehicle.rb +0 -7
  71. data/test/files/models/auto_shop.rb +0 -31
  72. data/test/files/models/car.rb +0 -21
  73. data/test/files/models/driver.rb +0 -13
  74. data/test/files/models/model_base.rb +0 -6
  75. data/test/files/models/motorcycle.rb +0 -16
  76. data/test/files/models/traffic_light.rb +0 -47
  77. data/test/files/models/vehicle.rb +0 -127
  78. data/test/files/node.rb +0 -5
  79. data/test/files/switch.rb +0 -15
  80. data/test/functional/auto_shop_available_test.rb +0 -20
  81. data/test/functional/auto_shop_busy_test.rb +0 -25
  82. data/test/functional/car_backing_up_test.rb +0 -45
  83. data/test/functional/car_test.rb +0 -49
  84. data/test/functional/driver_default_nonstandard_test.rb +0 -13
  85. data/test/functional/motorcycle_test.rb +0 -52
  86. data/test/functional/traffic_light_caution_test.rb +0 -17
  87. data/test/functional/traffic_light_proceed_test.rb +0 -17
  88. data/test/functional/traffic_light_stop_test.rb +0 -26
  89. data/test/functional/vehicle_first_gear_test.rb +0 -42
  90. data/test/functional/vehicle_idling_test.rb +0 -59
  91. data/test/functional/vehicle_locked_test.rb +0 -29
  92. data/test/functional/vehicle_parked_test.rb +0 -53
  93. data/test/functional/vehicle_repaired_test.rb +0 -20
  94. data/test/functional/vehicle_second_gear_test.rb +0 -42
  95. data/test/functional/vehicle_stalled_test.rb +0 -65
  96. data/test/functional/vehicle_test.rb +0 -20
  97. data/test/functional/vehicle_third_gear_test.rb +0 -42
  98. data/test/functional/vehicle_unsaved_test.rb +0 -181
  99. data/test/functional/vehicle_with_event_attributes_test.rb +0 -30
  100. data/test/functional/vehicle_with_parallel_events_test.rb +0 -36
  101. data/test/test_helper.rb +0 -15
  102. data/test/unit/assertions/assert_exclusive_keys_test.rb +0 -22
  103. data/test/unit/assertions/assert_valid_key_test.rb +0 -12
  104. data/test/unit/branch/branch_test.rb +0 -28
  105. data/test/unit/branch/branch_with_conflicting_conditionals_test.rb +0 -27
  106. data/test/unit/branch/branch_with_conflicting_from_requirements_test.rb +0 -8
  107. data/test/unit/branch/branch_with_conflicting_on_requirements_test.rb +0 -8
  108. data/test/unit/branch/branch_with_conflicting_to_requirements_test.rb +0 -8
  109. data/test/unit/branch/branch_with_different_requirements_test.rb +0 -41
  110. data/test/unit/branch/branch_with_except_from_matcher_requirement_test.rb +0 -8
  111. data/test/unit/branch/branch_with_except_from_requirement_test.rb +0 -36
  112. data/test/unit/branch/branch_with_except_on_matcher_requirement_test.rb +0 -8
  113. data/test/unit/branch/branch_with_except_on_requirement_test.rb +0 -36
  114. data/test/unit/branch/branch_with_except_to_matcher_requirement_test.rb +0 -8
  115. data/test/unit/branch/branch_with_except_to_requirement_test.rb +0 -36
  116. data/test/unit/branch/branch_with_from_matcher_requirement_test.rb +0 -20
  117. data/test/unit/branch/branch_with_from_requirement_test.rb +0 -45
  118. data/test/unit/branch/branch_with_if_conditional_test.rb +0 -27
  119. data/test/unit/branch/branch_with_implicit_and_explicit_requirements_test.rb +0 -23
  120. data/test/unit/branch/branch_with_implicit_from_requirement_matcher_test.rb +0 -20
  121. data/test/unit/branch/branch_with_implicit_requirement_test.rb +0 -20
  122. data/test/unit/branch/branch_with_implicit_to_requirement_matcher_test.rb +0 -16
  123. data/test/unit/branch/branch_with_multiple_except_from_requirements_test.rb +0 -20
  124. data/test/unit/branch/branch_with_multiple_except_on_requirements_test.rb +0 -16
  125. data/test/unit/branch/branch_with_multiple_except_to_requirements_test.rb +0 -20
  126. data/test/unit/branch/branch_with_multiple_from_requirements_test.rb +0 -16
  127. data/test/unit/branch/branch_with_multiple_if_conditionals_test.rb +0 -20
  128. data/test/unit/branch/branch_with_multiple_implicit_requirements_test.rb +0 -53
  129. data/test/unit/branch/branch_with_multiple_to_requirements_test.rb +0 -20
  130. data/test/unit/branch/branch_with_multiple_unless_conditionals_test.rb +0 -20
  131. data/test/unit/branch/branch_with_nil_requirements_test.rb +0 -28
  132. data/test/unit/branch/branch_with_no_requirements_test.rb +0 -36
  133. data/test/unit/branch/branch_with_on_matcher_requirement_test.rb +0 -16
  134. data/test/unit/branch/branch_with_on_requirement_test.rb +0 -45
  135. data/test/unit/branch/branch_with_to_matcher_requirement_test.rb +0 -20
  136. data/test/unit/branch/branch_with_to_requirement_test.rb +0 -45
  137. data/test/unit/branch/branch_with_unless_conditional_test.rb +0 -27
  138. data/test/unit/branch/branch_without_guards_test.rb +0 -27
  139. data/test/unit/callback/callback_by_default_test.rb +0 -25
  140. data/test/unit/callback/callback_test.rb +0 -53
  141. data/test/unit/callback/callback_with_application_bound_object_test.rb +0 -23
  142. data/test/unit/callback/callback_with_application_terminator_test.rb +0 -24
  143. data/test/unit/callback/callback_with_arguments_test.rb +0 -14
  144. data/test/unit/callback/callback_with_around_type_and_arguments_test.rb +0 -25
  145. data/test/unit/callback/callback_with_around_type_and_block_test.rb +0 -44
  146. data/test/unit/callback/callback_with_around_type_and_bound_method_test.rb +0 -23
  147. data/test/unit/callback/callback_with_around_type_and_multiple_methods_test.rb +0 -93
  148. data/test/unit/callback/callback_with_around_type_and_terminator_test.rb +0 -17
  149. data/test/unit/callback/callback_with_block_test.rb +0 -20
  150. data/test/unit/callback/callback_with_bound_method_and_arguments_test.rb +0 -28
  151. data/test/unit/callback/callback_with_bound_method_test.rb +0 -35
  152. data/test/unit/callback/callback_with_do_method_test.rb +0 -18
  153. data/test/unit/callback/callback_with_explicit_requirements_test.rb +0 -32
  154. data/test/unit/callback/callback_with_if_condition_test.rb +0 -17
  155. data/test/unit/callback/callback_with_implicit_requirements_test.rb +0 -32
  156. data/test/unit/callback/callback_with_method_argument_test.rb +0 -18
  157. data/test/unit/callback/callback_with_mixed_methods_test.rb +0 -31
  158. data/test/unit/callback/callback_with_multiple_bound_methods_test.rb +0 -21
  159. data/test/unit/callback/callback_with_multiple_do_methods_test.rb +0 -29
  160. data/test/unit/callback/callback_with_multiple_method_arguments_test.rb +0 -29
  161. data/test/unit/callback/callback_with_terminator_test.rb +0 -22
  162. data/test/unit/callback/callback_with_unbound_method_test.rb +0 -14
  163. data/test/unit/callback/callback_with_unless_condition_test.rb +0 -17
  164. data/test/unit/callback/callback_without_arguments_test.rb +0 -14
  165. data/test/unit/callback/callback_without_terminator_test.rb +0 -12
  166. data/test/unit/error/error_by_default_test.rb +0 -21
  167. data/test/unit/error/error_with_message_test.rb +0 -23
  168. data/test/unit/eval_helper/eval_helpers_base_test.rb +0 -8
  169. data/test/unit/eval_helper/eval_helpers_proc_block_and_explicit_arguments_test.rb +0 -14
  170. data/test/unit/eval_helper/eval_helpers_proc_block_and_implicit_arguments_test.rb +0 -14
  171. data/test/unit/eval_helper/eval_helpers_proc_test.rb +0 -13
  172. data/test/unit/eval_helper/eval_helpers_proc_with_arguments_test.rb +0 -13
  173. data/test/unit/eval_helper/eval_helpers_proc_with_block_test.rb +0 -13
  174. data/test/unit/eval_helper/eval_helpers_proc_with_block_without_arguments_test.rb +0 -18
  175. data/test/unit/eval_helper/eval_helpers_proc_with_block_without_object_test.rb +0 -14
  176. data/test/unit/eval_helper/eval_helpers_proc_without_arguments_test.rb +0 -19
  177. data/test/unit/eval_helper/eval_helpers_string_test.rb +0 -25
  178. data/test/unit/eval_helper/eval_helpers_string_with_block_test.rb +0 -12
  179. data/test/unit/eval_helper/eval_helpers_symbol_method_missing_test.rb +0 -20
  180. data/test/unit/eval_helper/eval_helpers_symbol_private_test.rb +0 -17
  181. data/test/unit/eval_helper/eval_helpers_symbol_protected_test.rb +0 -17
  182. data/test/unit/eval_helper/eval_helpers_symbol_tainted_method_test.rb +0 -18
  183. data/test/unit/eval_helper/eval_helpers_symbol_test.rb +0 -16
  184. data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_and_block_test.rb +0 -16
  185. data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_test.rb +0 -16
  186. data/test/unit/eval_helper/eval_helpers_symbol_with_block_test.rb +0 -16
  187. data/test/unit/eval_helper/eval_helpers_test.rb +0 -13
  188. data/test/unit/event/event_after_being_copied_test.rb +0 -17
  189. data/test/unit/event/event_by_default_test.rb +0 -60
  190. data/test/unit/event/event_context_test.rb +0 -16
  191. data/test/unit/event/event_on_failure_test.rb +0 -44
  192. data/test/unit/event/event_test.rb +0 -34
  193. data/test/unit/event/event_transitions_test.rb +0 -62
  194. data/test/unit/event/event_with_conflicting_helpers_after_definition_test.rb +0 -79
  195. data/test/unit/event/event_with_conflicting_helpers_before_definition_test.rb +0 -58
  196. data/test/unit/event/event_with_conflicting_machine_test.rb +0 -48
  197. data/test/unit/event/event_with_dynamic_human_name_test.rb +0 -26
  198. data/test/unit/event/event_with_human_name_test.rb +0 -13
  199. data/test/unit/event/event_with_invalid_current_state_test.rb +0 -30
  200. data/test/unit/event/event_with_machine_action_test.rb +0 -33
  201. data/test/unit/event/event_with_marshalling_test.rb +0 -47
  202. data/test/unit/event/event_with_matching_disabled_transitions_test.rb +0 -115
  203. data/test/unit/event/event_with_matching_enabled_transitions_test.rb +0 -75
  204. data/test/unit/event/event_with_multiple_transitions_test.rb +0 -61
  205. data/test/unit/event/event_with_namespace_test.rb +0 -34
  206. data/test/unit/event/event_with_transition_with_blacklisted_to_state_test.rb +0 -60
  207. data/test/unit/event/event_with_transition_with_loopback_state_test.rb +0 -36
  208. data/test/unit/event/event_with_transition_with_nil_to_state_test.rb +0 -36
  209. data/test/unit/event/event_with_transition_with_whitelisted_to_state_test.rb +0 -51
  210. data/test/unit/event/event_with_transition_without_to_state_test.rb +0 -36
  211. data/test/unit/event/event_with_transitions_test.rb +0 -32
  212. data/test/unit/event/event_without_matching_transitions_test.rb +0 -41
  213. data/test/unit/event/event_without_transitions_test.rb +0 -28
  214. data/test/unit/event/invalid_event_test.rb +0 -20
  215. data/test/unit/event_collection/event_collection_attribute_with_machine_action_test.rb +0 -62
  216. data/test/unit/event_collection/event_collection_attribute_with_namespaced_machine_test.rb +0 -36
  217. data/test/unit/event_collection/event_collection_by_default_test.rb +0 -26
  218. data/test/unit/event_collection/event_collection_test.rb +0 -39
  219. data/test/unit/event_collection/event_collection_with_custom_machine_attribute_test.rb +0 -31
  220. data/test/unit/event_collection/event_collection_with_events_with_transitions_test.rb +0 -76
  221. data/test/unit/event_collection/event_collection_with_multiple_events_test.rb +0 -27
  222. data/test/unit/event_collection/event_collection_with_validations_test.rb +0 -74
  223. data/test/unit/event_collection/event_collection_without_machine_action_test.rb +0 -18
  224. data/test/unit/event_collection/event_string_collection_test.rb +0 -31
  225. data/test/unit/helper_module_test.rb +0 -17
  226. data/test/unit/integrations/integration_finder_test.rb +0 -16
  227. data/test/unit/integrations/integration_matcher_test.rb +0 -29
  228. data/test/unit/invalid_transition/invalid_parallel_transition_test.rb +0 -18
  229. data/test/unit/invalid_transition/invalid_transition_test.rb +0 -47
  230. data/test/unit/invalid_transition/invalid_transition_with_integration_test.rb +0 -45
  231. data/test/unit/invalid_transition/invalid_transition_with_namespace_test.rb +0 -32
  232. data/test/unit/machine/machine_after_being_copied_test.rb +0 -62
  233. data/test/unit/machine/machine_after_changing_initial_state.rb +0 -28
  234. data/test/unit/machine/machine_after_changing_owner_class_test.rb +0 -31
  235. data/test/unit/machine/machine_by_default_test.rb +0 -160
  236. data/test/unit/machine/machine_finder_custom_options_test.rb +0 -17
  237. data/test/unit/machine/machine_finder_with_existing_machine_on_superclass_test.rb +0 -85
  238. data/test/unit/machine/machine_finder_with_existing_on_same_class_test.rb +0 -23
  239. data/test/unit/machine/machine_finder_without_existing_machine_test.rb +0 -25
  240. data/test/unit/machine/machine_persistence_test.rb +0 -52
  241. data/test/unit/machine/machine_state_initialization_test.rb +0 -56
  242. data/test/unit/machine/machine_test.rb +0 -30
  243. data/test/unit/machine/machine_with_action_already_overridden_test.rb +0 -23
  244. data/test/unit/machine/machine_with_action_defined_in_class_test.rb +0 -37
  245. data/test/unit/machine/machine_with_action_defined_in_included_module_test.rb +0 -46
  246. data/test/unit/machine/machine_with_action_defined_in_superclass_test.rb +0 -43
  247. data/test/unit/machine/machine_with_action_undefined_test.rb +0 -33
  248. data/test/unit/machine/machine_with_cached_state_test.rb +0 -20
  249. data/test/unit/machine/machine_with_class_helpers_test.rb +0 -179
  250. data/test/unit/machine/machine_with_conflicting_helpers_after_definition_test.rb +0 -244
  251. data/test/unit/machine/machine_with_conflicting_helpers_before_definition_test.rb +0 -175
  252. data/test/unit/machine/machine_with_custom_action_test.rb +0 -11
  253. data/test/unit/machine/machine_with_custom_attribute_test.rb +0 -103
  254. data/test/unit/machine/machine_with_custom_initialize_test.rb +0 -24
  255. data/test/unit/machine/machine_with_custom_integration_test.rb +0 -72
  256. data/test/unit/machine/machine_with_custom_invalidation_test.rb +0 -39
  257. data/test/unit/machine/machine_with_custom_name_test.rb +0 -57
  258. data/test/unit/machine/machine_with_custom_plural_test.rb +0 -52
  259. data/test/unit/machine/machine_with_dynamic_initial_state_test.rb +0 -65
  260. data/test/unit/machine/machine_with_event_matchers_test.rb +0 -41
  261. data/test/unit/machine/machine_with_events_test.rb +0 -52
  262. data/test/unit/machine/machine_with_events_with_custom_human_names_test.rb +0 -18
  263. data/test/unit/machine/machine_with_events_with_transitions_test.rb +0 -37
  264. data/test/unit/machine/machine_with_existing_event_test.rb +0 -17
  265. data/test/unit/machine/machine_with_existing_machines_on_owner_class_test.rb +0 -20
  266. data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_class_test.rb +0 -71
  267. data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_subclass_test.rb +0 -31
  268. data/test/unit/machine/machine_with_existing_state_test.rb +0 -27
  269. data/test/unit/machine/machine_with_failure_callbacks_test.rb +0 -48
  270. data/test/unit/machine/machine_with_helpers_test.rb +0 -14
  271. data/test/unit/machine/machine_with_initial_state_with_value_and_owner_default.rb +0 -25
  272. data/test/unit/machine/machine_with_initialize_and_super_test.rb +0 -17
  273. data/test/unit/machine/machine_with_initialize_arguments_and_block_test.rb +0 -31
  274. data/test/unit/machine/machine_with_initialize_without_super_test.rb +0 -17
  275. data/test/unit/machine/machine_with_instance_helpers_test.rb +0 -179
  276. data/test/unit/machine/machine_with_integration_test.rb +0 -72
  277. data/test/unit/machine/machine_with_multiple_events_test.rb +0 -32
  278. data/test/unit/machine/machine_with_namespace_test.rb +0 -48
  279. data/test/unit/machine/machine_with_nil_action_test.rb +0 -27
  280. data/test/unit/machine/machine_with_other_states.rb +0 -22
  281. data/test/unit/machine/machine_with_owner_subclass_test.rb +0 -18
  282. data/test/unit/machine/machine_with_paths_test.rb +0 -25
  283. data/test/unit/machine/machine_with_private_action_test.rb +0 -43
  284. data/test/unit/machine/machine_with_state_matchers_test.rb +0 -41
  285. data/test/unit/machine/machine_with_state_with_matchers_test.rb +0 -19
  286. data/test/unit/machine/machine_with_states_test.rb +0 -55
  287. data/test/unit/machine/machine_with_states_with_behaviors_test.rb +0 -23
  288. data/test/unit/machine/machine_with_states_with_custom_human_names_test.rb +0 -18
  289. data/test/unit/machine/machine_with_states_with_custom_values_test.rb +0 -21
  290. data/test/unit/machine/machine_with_states_with_runtime_dependencies_test.rb +0 -19
  291. data/test/unit/machine/machine_with_static_initial_state_test.rb +0 -49
  292. data/test/unit/machine/machine_with_superclass_conflicting_helpers_after_definition_test.rb +0 -36
  293. data/test/unit/machine/machine_with_transition_callbacks_test.rb +0 -144
  294. data/test/unit/machine/machine_with_transitions_test.rb +0 -87
  295. data/test/unit/machine/machine_without_initialization_test.rb +0 -31
  296. data/test/unit/machine/machine_without_initialize_test.rb +0 -14
  297. data/test/unit/machine/machine_without_integration_test.rb +0 -31
  298. data/test/unit/machine_collection/machine_collection_by_default_test.rb +0 -11
  299. data/test/unit/machine_collection/machine_collection_fire_test.rb +0 -80
  300. data/test/unit/machine_collection/machine_collection_fire_with_transactions_test.rb +0 -54
  301. data/test/unit/machine_collection/machine_collection_fire_with_validations_test.rb +0 -76
  302. data/test/unit/machine_collection/machine_collection_state_initialization_test.rb +0 -111
  303. data/test/unit/machine_collection/machine_collection_transitions_with_blank_events_test.rb +0 -25
  304. data/test/unit/machine_collection/machine_collection_transitions_with_custom_options_test.rb +0 -20
  305. data/test/unit/machine_collection/machine_collection_transitions_with_different_actions_test.rb +0 -26
  306. data/test/unit/machine_collection/machine_collection_transitions_with_exisiting_transitions_test.rb +0 -25
  307. data/test/unit/machine_collection/machine_collection_transitions_with_invalid_events_test.rb +0 -25
  308. data/test/unit/machine_collection/machine_collection_transitions_with_same_actions_test.rb +0 -31
  309. data/test/unit/machine_collection/machine_collection_transitions_with_transition_test.rb +0 -26
  310. data/test/unit/machine_collection/machine_collection_transitions_without_events_test.rb +0 -25
  311. data/test/unit/machine_collection/machine_collection_transitions_without_transition_test.rb +0 -27
  312. data/test/unit/matcher/all_matcher_test.rb +0 -29
  313. data/test/unit/matcher/blacklist_matcher_test.rb +0 -30
  314. data/test/unit/matcher/loopback_matcher_test.rb +0 -27
  315. data/test/unit/matcher/matcher_by_default_test.rb +0 -15
  316. data/test/unit/matcher/matcher_with_multiple_values_test.rb +0 -15
  317. data/test/unit/matcher/matcher_with_value_test.rb +0 -15
  318. data/test/unit/matcher/whitelist_matcher_test.rb +0 -30
  319. data/test/unit/matcher_helpers/matcher_helpers_all_test.rb +0 -14
  320. data/test/unit/matcher_helpers/matcher_helpers_any_test.rb +0 -14
  321. data/test/unit/matcher_helpers/matcher_helpers_same_test.rb +0 -13
  322. data/test/unit/node_collection/node_collection_after_being_copied_test.rb +0 -46
  323. data/test/unit/node_collection/node_collection_after_update_test.rb +0 -36
  324. data/test/unit/node_collection/node_collection_by_default_test.rb +0 -22
  325. data/test/unit/node_collection/node_collection_test.rb +0 -23
  326. data/test/unit/node_collection/node_collection_with_indices_test.rb +0 -42
  327. data/test/unit/node_collection/node_collection_with_matcher_contexts_test.rb +0 -25
  328. data/test/unit/node_collection/node_collection_with_nodes_test.rb +0 -46
  329. data/test/unit/node_collection/node_collection_with_numeric_index_test.rb +0 -24
  330. data/test/unit/node_collection/node_collection_with_postdefined_contexts_test.rb +0 -22
  331. data/test/unit/node_collection/node_collection_with_predefined_contexts_test.rb +0 -23
  332. data/test/unit/node_collection/node_collection_with_string_index_test.rb +0 -20
  333. data/test/unit/node_collection/node_collection_with_symbol_index_test.rb +0 -20
  334. data/test/unit/node_collection/node_collection_without_indices_test.rb +0 -30
  335. data/test/unit/path/path_by_default_test.rb +0 -54
  336. data/test/unit/path/path_test.rb +0 -14
  337. data/test/unit/path/path_with_available_transitions_after_reaching_target_test.rb +0 -40
  338. data/test/unit/path/path_with_available_transitions_test.rb +0 -54
  339. data/test/unit/path/path_with_deep_target_reached_test.rb +0 -50
  340. data/test/unit/path/path_with_deep_target_test.rb +0 -40
  341. data/test/unit/path/path_with_duplicates_test.rb +0 -32
  342. data/test/unit/path/path_with_encountered_transitions_test.rb +0 -34
  343. data/test/unit/path/path_with_guarded_transitions_test.rb +0 -42
  344. data/test/unit/path/path_with_reached_target_test.rb +0 -35
  345. data/test/unit/path/path_with_transitions_test.rb +0 -54
  346. data/test/unit/path/path_with_unreached_target_test.rb +0 -31
  347. data/test/unit/path/path_without_transitions_test.rb +0 -24
  348. data/test/unit/path_collection/path_collection_by_default_test.rb +0 -46
  349. data/test/unit/path_collection/path_collection_test.rb +0 -24
  350. data/test/unit/path_collection/path_collection_with_deep_paths_test.rb +0 -43
  351. data/test/unit/path_collection/path_collection_with_duplicate_nodes_test.rb +0 -31
  352. data/test/unit/path_collection/path_collection_with_from_state_test.rb +0 -27
  353. data/test/unit/path_collection/path_collection_with_paths_test.rb +0 -47
  354. data/test/unit/path_collection/path_collection_with_to_state_test.rb +0 -29
  355. data/test/unit/path_collection/path_with_guarded_paths_test.rb +0 -25
  356. data/test/unit/state/state_after_being_copied_test.rb +0 -19
  357. data/test/unit/state/state_by_default_test.rb +0 -41
  358. data/test/unit/state/state_final_test.rb +0 -28
  359. data/test/unit/state/state_initial_test.rb +0 -13
  360. data/test/unit/state/state_not_final_test.rb +0 -32
  361. data/test/unit/state/state_not_initial_test.rb +0 -13
  362. data/test/unit/state/state_test.rb +0 -44
  363. data/test/unit/state/state_with_cached_lambda_value_test.rb +0 -29
  364. data/test/unit/state/state_with_conflicting_helpers_after_definition_test.rb +0 -38
  365. data/test/unit/state/state_with_conflicting_helpers_before_definition_test.rb +0 -29
  366. data/test/unit/state/state_with_conflicting_machine_name_test.rb +0 -20
  367. data/test/unit/state/state_with_conflicting_machine_test.rb +0 -37
  368. data/test/unit/state/state_with_context_test.rb +0 -60
  369. data/test/unit/state/state_with_dynamic_human_name_test.rb +0 -25
  370. data/test/unit/state/state_with_existing_context_method_test.rb +0 -24
  371. data/test/unit/state/state_with_human_name_test.rb +0 -13
  372. data/test/unit/state/state_with_integer_value_test.rb +0 -32
  373. data/test/unit/state/state_with_invalid_method_call_test.rb +0 -21
  374. data/test/unit/state/state_with_lambda_value_test.rb +0 -37
  375. data/test/unit/state/state_with_matcher_test.rb +0 -18
  376. data/test/unit/state/state_with_multiple_contexts_test.rb +0 -57
  377. data/test/unit/state/state_with_name_test.rb +0 -43
  378. data/test/unit/state/state_with_namespace_test.rb +0 -22
  379. data/test/unit/state/state_with_nil_value_test.rb +0 -35
  380. data/test/unit/state/state_with_redefined_context_method_test.rb +0 -45
  381. data/test/unit/state/state_with_symbolic_value_test.rb +0 -32
  382. data/test/unit/state/state_with_valid_inherited_method_call_for_current_state_test.rb +0 -40
  383. data/test/unit/state/state_with_valid_method_call_for_current_state_test.rb +0 -33
  384. data/test/unit/state/state_with_valid_method_call_for_different_state_test.rb +0 -41
  385. data/test/unit/state/state_without_cached_lambda_value_test.rb +0 -25
  386. data/test/unit/state/state_without_name_test.rb +0 -39
  387. data/test/unit/state_collection/state_collection_by_default_test.rb +0 -21
  388. data/test/unit/state_collection/state_collection_string_test.rb +0 -35
  389. data/test/unit/state_collection/state_collection_test.rb +0 -74
  390. data/test/unit/state_collection/state_collection_with_custom_state_values_test.rb +0 -29
  391. data/test/unit/state_collection/state_collection_with_event_transitions_test.rb +0 -39
  392. data/test/unit/state_collection/state_collection_with_initial_state_test.rb +0 -40
  393. data/test/unit/state_collection/state_collection_with_namespace_test.rb +0 -21
  394. data/test/unit/state_collection/state_collection_with_state_behaviors_test.rb +0 -40
  395. data/test/unit/state_collection/state_collection_with_state_matchers_test.rb +0 -29
  396. data/test/unit/state_collection/state_collection_with_transition_callbacks_test.rb +0 -40
  397. data/test/unit/state_context/state_context_proxy_test.rb +0 -26
  398. data/test/unit/state_context/state_context_proxy_with_if_and_unless_conditions_test.rb +0 -42
  399. data/test/unit/state_context/state_context_proxy_with_if_condition_test.rb +0 -64
  400. data/test/unit/state_context/state_context_proxy_with_multiple_if_conditions_test.rb +0 -32
  401. data/test/unit/state_context/state_context_proxy_with_multiple_unless_conditions_test.rb +0 -32
  402. data/test/unit/state_context/state_context_proxy_with_unless_condition_test.rb +0 -64
  403. data/test/unit/state_context/state_context_proxy_without_conditions_test.rb +0 -31
  404. data/test/unit/state_context/state_context_test.rb +0 -28
  405. data/test/unit/state_context/state_context_transition_test.rb +0 -104
  406. data/test/unit/state_context/state_context_with_matching_transition_test.rb +0 -27
  407. data/test/unit/state_machine/state_machine_by_default_test.rb +0 -12
  408. data/test/unit/state_machine/state_machine_test.rb +0 -20
  409. data/test/unit/transition/transition_after_being_performed_test.rb +0 -48
  410. data/test/unit/transition/transition_after_being_persisted_test.rb +0 -46
  411. data/test/unit/transition/transition_after_being_rolled_back_test.rb +0 -35
  412. data/test/unit/transition/transition_equality_test.rb +0 -52
  413. data/test/unit/transition/transition_loopback_test.rb +0 -18
  414. data/test/unit/transition/transition_test.rb +0 -96
  415. data/test/unit/transition/transition_transient_test.rb +0 -20
  416. data/test/unit/transition/transition_with_action_test.rb +0 -27
  417. data/test/unit/transition/transition_with_after_callbacks_skipped_test.rb +0 -127
  418. data/test/unit/transition/transition_with_after_callbacks_test.rb +0 -93
  419. data/test/unit/transition/transition_with_around_callbacks_test.rb +0 -141
  420. data/test/unit/transition/transition_with_before_callbacks_skipped_test.rb +0 -30
  421. data/test/unit/transition/transition_with_before_callbacks_test.rb +0 -104
  422. data/test/unit/transition/transition_with_custom_machine_attribute_test.rb +0 -28
  423. data/test/unit/transition/transition_with_different_states_test.rb +0 -18
  424. data/test/unit/transition/transition_with_dynamic_to_value_test.rb +0 -19
  425. data/test/unit/transition/transition_with_failure_callbacks_test.rb +0 -84
  426. data/test/unit/transition/transition_with_invalid_nodes_test.rb +0 -29
  427. data/test/unit/transition/transition_with_mixed_callbacks_test.rb +0 -105
  428. data/test/unit/transition/transition_with_multiple_after_callbacks_test.rb +0 -40
  429. data/test/unit/transition/transition_with_multiple_around_callbacks_test.rb +0 -114
  430. data/test/unit/transition/transition_with_multiple_before_callbacks_test.rb +0 -40
  431. data/test/unit/transition/transition_with_multiple_failure_callbacks_test.rb +0 -40
  432. data/test/unit/transition/transition_with_namespace_test.rb +0 -47
  433. data/test/unit/transition/transition_with_perform_arguments_test.rb +0 -35
  434. data/test/unit/transition/transition_with_transactions_test.rb +0 -42
  435. data/test/unit/transition/transition_without_callbacks_test.rb +0 -33
  436. data/test/unit/transition/transition_without_reading_state_test.rb +0 -22
  437. data/test/unit/transition/transition_without_running_action_test.rb +0 -47
  438. data/test/unit/transition_collection/attribute_transition_collection_by_default_test.rb +0 -23
  439. data/test/unit/transition_collection/attribute_transition_collection_marshalling_test.rb +0 -64
  440. data/test/unit/transition_collection/attribute_transition_collection_with_action_error_test.rb +0 -44
  441. data/test/unit/transition_collection/attribute_transition_collection_with_action_failed_test.rb +0 -44
  442. data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_error_test.rb +0 -32
  443. data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_halt_test.rb +0 -33
  444. data/test/unit/transition_collection/attribute_transition_collection_with_around_after_yield_callback_error_test.rb +0 -32
  445. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_error_test.rb +0 -32
  446. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_halt_test.rb +0 -33
  447. data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_before_yield_halt_test.rb +0 -33
  448. data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_error_test.rb +0 -32
  449. data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_halt_test.rb +0 -33
  450. data/test/unit/transition_collection/attribute_transition_collection_with_callbacks_test.rb +0 -68
  451. data/test/unit/transition_collection/attribute_transition_collection_with_event_transitions_test.rb +0 -41
  452. data/test/unit/transition_collection/attribute_transition_collection_with_events_test.rb +0 -44
  453. data/test/unit/transition_collection/attribute_transition_collection_with_skipped_after_callbacks_test.rb +0 -42
  454. data/test/unit/transition_collection/transition_collection_by_default_test.rb +0 -23
  455. data/test/unit/transition_collection/transition_collection_empty_with_block_test.rb +0 -23
  456. data/test/unit/transition_collection/transition_collection_empty_without_block_test.rb +0 -12
  457. data/test/unit/transition_collection/transition_collection_invalid_test.rb +0 -21
  458. data/test/unit/transition_collection/transition_collection_partial_invalid_test.rb +0 -69
  459. data/test/unit/transition_collection/transition_collection_test.rb +0 -26
  460. data/test/unit/transition_collection/transition_collection_valid_test.rb +0 -57
  461. data/test/unit/transition_collection/transition_collection_with_action_error_test.rb +0 -66
  462. data/test/unit/transition_collection/transition_collection_with_action_failed_test.rb +0 -60
  463. data/test/unit/transition_collection/transition_collection_with_action_hook_and_block_test.rb +0 -17
  464. data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_action_test.rb +0 -17
  465. data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_after_callbacks_test.rb +0 -37
  466. data/test/unit/transition_collection/transition_collection_with_action_hook_base_test.rb +0 -34
  467. data/test/unit/transition_collection/transition_collection_with_action_hook_error_test.rb +0 -29
  468. data/test/unit/transition_collection/transition_collection_with_action_hook_invalid_test.rb +0 -17
  469. data/test/unit/transition_collection/transition_collection_with_action_hook_multiple_test.rb +0 -79
  470. data/test/unit/transition_collection/transition_collection_with_action_hook_test.rb +0 -45
  471. data/test/unit/transition_collection/transition_collection_with_action_hook_with_different_actions_test.rb +0 -48
  472. data/test/unit/transition_collection/transition_collection_with_action_hook_with_nil_action_test.rb +0 -42
  473. data/test/unit/transition_collection/transition_collection_with_after_callback_halt_test.rb +0 -47
  474. data/test/unit/transition_collection/transition_collection_with_before_callback_halt_test.rb +0 -51
  475. data/test/unit/transition_collection/transition_collection_with_block_test.rb +0 -46
  476. data/test/unit/transition_collection/transition_collection_with_callbacks_test.rb +0 -135
  477. data/test/unit/transition_collection/transition_collection_with_different_actions_test.rb +0 -189
  478. data/test/unit/transition_collection/transition_collection_with_duplicate_actions_test.rb +0 -48
  479. data/test/unit/transition_collection/transition_collection_with_empty_actions_test.rb +0 -41
  480. data/test/unit/transition_collection/transition_collection_with_mixed_actions_test.rb +0 -41
  481. data/test/unit/transition_collection/transition_collection_with_skipped_actions_and_block_test.rb +0 -34
  482. data/test/unit/transition_collection/transition_collection_with_skipped_actions_test.rb +0 -69
  483. data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_and_around_callbacks_test.rb +0 -53
  484. data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_test.rb +0 -34
  485. data/test/unit/transition_collection/transition_collection_with_transactions_test.rb +0 -65
  486. data/test/unit/transition_collection/transition_collection_without_transactions_test.rb +0 -29
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'syntax_validator'
4
+
1
5
  module StateMachines
2
6
  # Provides a set of helper methods for evaluating methods within the context
3
7
  # of an object.
@@ -7,80 +11,234 @@ module StateMachines
7
11
  # * Symbol
8
12
  # * Method / Proc
9
13
  # * String
10
- #
14
+ #
11
15
  # == Examples
12
- #
16
+ #
13
17
  # Below are examples of the various ways that a method can be evaluated
14
18
  # on an object:
15
- #
19
+ #
16
20
  # class Person
17
21
  # def initialize(name)
18
22
  # @name = name
19
23
  # end
20
- #
24
+ #
21
25
  # def name
22
26
  # @name
23
27
  # end
24
28
  # end
25
- #
29
+ #
26
30
  # class PersonCallback
27
31
  # def self.run(person)
28
32
  # person.name
29
33
  # end
30
34
  # end
31
- #
35
+ #
32
36
  # person = Person.new('John Smith')
33
- #
37
+ #
34
38
  # evaluate_method(person, :name) # => "John Smith"
35
39
  # evaluate_method(person, PersonCallback.method(:run)) # => "John Smith"
36
40
  # evaluate_method(person, Proc.new {|person| person.name}) # => "John Smith"
37
41
  # evaluate_method(person, lambda {|person| person.name}) # => "John Smith"
38
42
  # evaluate_method(person, '@name') # => "John Smith"
39
- #
43
+ #
40
44
  # == Additional arguments
41
- #
45
+ #
42
46
  # Additional arguments can be passed to the methods being evaluated. If
43
47
  # the method defines additional arguments other than the object context,
44
48
  # then all arguments are required.
45
- #
49
+ #
50
+ # For guard conditions in state machines, event arguments can be passed
51
+ # automatically based on the guard's arity:
52
+ # - Guards with arity 1 receive only the object (backward compatible)
53
+ # - Guards with arity -1 or > 1 receive object + event arguments
54
+ #
46
55
  # For example,
47
- #
56
+ #
48
57
  # person = Person.new('John Smith')
49
- #
58
+ #
50
59
  # evaluate_method(person, lambda {|person| person.name}, 21) # => "John Smith"
51
60
  # evaluate_method(person, lambda {|person, age| "#{person.name} is #{age}"}, 21) # => "John Smith is 21"
52
61
  # evaluate_method(person, lambda {|person, age| "#{person.name} is #{age}"}, 21, 'male') # => ArgumentError: wrong number of arguments (3 for 2)
53
- def evaluate_method(object, method, *args, &block)
62
+ #
63
+ # With event arguments for guards:
64
+ #
65
+ # # Single parameter guard (backward compatible)
66
+ # guard = lambda {|obj| obj.valid? }
67
+ # evaluate_method_with_event_args(object, guard, [arg1, arg2]) # => calls guard.call(object)
68
+ #
69
+ # # Multi-parameter guard (receives event args)
70
+ # guard = lambda {|obj, *args| obj.valid? && args[0] == :force }
71
+ # evaluate_method_with_event_args(object, guard, [:force]) # => calls guard.call(object, :force)
72
+ def evaluate_method(object, method, *args, **, &block)
54
73
  case method
55
- when Symbol
56
- klass = (class << object; self; end)
57
- args = [] if (klass.method_defined?(method) || klass.private_method_defined?(method)) && object.method(method).arity == 0
58
- object.send(method, *args, &block)
59
- when Proc, Method
60
- args.unshift(object)
61
- arity = method.arity
62
-
63
- # Procs don't support blocks in < Ruby 1.9, so it's tacked on as an
64
- # argument for consistency across versions of Ruby
65
- if block_given? && Proc === method && arity != 0
66
- if [1, 2].include?(arity)
67
- # Force the block to be either the only argument or the 2nd one
68
- # after the object (may mean additional arguments get discarded)
69
- args = args[0, arity - 1] + [block]
70
- else
71
- # Tack the block to the end of the args
72
- args << block
73
- end
74
+ in Symbol => sym
75
+ klass = (class << object; self; end)
76
+ args = [] if (klass.method_defined?(sym) || klass.private_method_defined?(sym)) && object.method(sym).arity.zero?
77
+ object.send(sym, *args, **, &block)
78
+ in Proc => proc
79
+ args.unshift(object)
80
+ arity = proc.arity
81
+ # Handle blocks for Procs
82
+ case [block_given?, arity]
83
+ in [true, arity] if arity != 0
84
+ case arity
85
+ in 1 | 2
86
+ # Force the block to be either the only argument or the second one
87
+ # after the object (may mean additional arguments get discarded)
88
+ args = args[0, arity - 1] + [block]
74
89
  else
75
- # These method types are only called with 0, 1, or n arguments
76
- args = args[0, arity] if [0, 1].include?(arity)
90
+ # insert the block to the end of the args
91
+ args << block
77
92
  end
78
-
79
- method.is_a?(Proc) ? method.call(*args) : method.call(*args, &block)
80
- when String
81
- eval(method, object.instance_eval {binding}, &block)
93
+ in [_, 0 | 1]
94
+ # These method types are only called with 0, 1, or n arguments
95
+ args = args[0, arity]
82
96
  else
83
- raise ArgumentError, 'Methods must be a symbol denoting the method to call, a block to be invoked, or a string to be evaluated'
97
+ # No changes needed for other cases
98
+ end
99
+
100
+ # Call the Proc with the arguments
101
+ proc.call(*args, **)
102
+
103
+ in Method => meth
104
+ args.unshift(object)
105
+ arity = meth.arity
106
+
107
+ # Methods handle blocks via &block, not as arguments
108
+ # Only limit arguments if necessary based on arity
109
+ args = args[0, arity] if [0, 1].include?(arity)
110
+
111
+ # Call the Method with the arguments and pass the block
112
+ meth.call(*args, **, &block)
113
+ in String => str
114
+ # Input validation for string evaluation
115
+ validate_eval_string(str)
116
+
117
+ case [block_given?, StateMachines::Transition.pause_supported?]
118
+ in [true, true]
119
+ eval(str, object.instance_eval { binding }, &block)
120
+ in [true, false]
121
+ # Support for JRuby and Truffle Ruby, which don't support binding blocks
122
+ # Need to check with @headius, if jruby 10 does now.
123
+ eigen = class << object; self; end
124
+ eigen.class_eval <<-RUBY, __FILE__, __LINE__ + 1
125
+ def __temp_eval_method__(*args, &b)
126
+ #{str}
127
+ end
128
+ RUBY
129
+ result = object.__temp_eval_method__(*args, &block)
130
+ eigen.send(:remove_method, :__temp_eval_method__)
131
+ result
132
+ in [false, _]
133
+ eval(str, object.instance_eval { binding })
134
+ end
135
+ else
136
+ raise ArgumentError, 'Methods must be a symbol denoting the method to call, a block to be invoked, or a string to be evaluated'
137
+ end
138
+ end
139
+
140
+ # Evaluates a guard method with support for event arguments passed to transitions.
141
+ # This method uses arity detection to determine whether to pass event arguments
142
+ # to the guard, ensuring backward compatibility.
143
+ #
144
+ # == Parameters
145
+ # * object - The object context to evaluate within
146
+ # * method - The guard method/proc to evaluate
147
+ # * event_args - Array of arguments passed to the event (optional)
148
+ #
149
+ # == Arity-based behavior
150
+ # * Arity 1: Only passes the object (backward compatible)
151
+ # * Arity -1 or > 1: Passes object + event arguments
152
+ #
153
+ # == Examples
154
+ #
155
+ # # Backward compatible single-parameter guard
156
+ # guard = lambda {|obj| obj.valid? }
157
+ # evaluate_method_with_event_args(object, guard, [:force]) # => calls guard.call(object)
158
+ #
159
+ # # New multi-parameter guard receiving event args
160
+ # guard = lambda {|obj, *args| obj.valid? && args[0] != :skip }
161
+ # evaluate_method_with_event_args(object, guard, [:skip]) # => calls guard.call(object, :skip)
162
+ def evaluate_method_with_event_args(object, method, event_args = [])
163
+ case method
164
+ in Symbol
165
+ # Symbol methods currently don't support event arguments
166
+ # This maintains backward compatibility
167
+ evaluate_method(object, method)
168
+ in Proc => proc
169
+ arity = proc.arity
170
+
171
+ # Arity-based decision for backward compatibility using pattern matching
172
+ case arity
173
+ in 0
174
+ proc.call
175
+ in 1
176
+ proc.call(object)
177
+ in -1
178
+ # Splat parameters: object + all event args
179
+ proc.call(object, *event_args)
180
+ in arity if arity > 1
181
+ # Explicit parameters: object + limited event args
182
+ args_needed = arity - 1 # Subtract 1 for the object parameter
183
+ proc.call(object, *event_args[0, args_needed])
184
+ else
185
+ # Negative arity other than -1 (unlikely but handle gracefully)
186
+ proc.call(object, *event_args)
187
+ end
188
+ in Method => meth
189
+ arity = meth.arity
190
+
191
+ case arity
192
+ in 0
193
+ meth.call
194
+ in 1
195
+ meth.call(object)
196
+ in -1
197
+ meth.call(object, *event_args)
198
+ in arity if arity > 1
199
+ args_needed = arity - 1
200
+ meth.call(object, *event_args[0, args_needed])
201
+ else
202
+ meth.call(object, *event_args)
203
+ end
204
+ in String
205
+ # String evaluation doesn't support event arguments for security
206
+ evaluate_method(object, method)
207
+ else
208
+ # Fall back to standard evaluation
209
+ evaluate_method(object, method)
210
+ end
211
+ end
212
+
213
+ private
214
+
215
+ # Validates string input before eval to prevent code injection
216
+ # This is a basic safety check - not foolproof security
217
+ def validate_eval_string(method_string)
218
+ # Check for obviously dangerous patterns
219
+ dangerous_patterns = [
220
+ /`.*`/, # Backticks (shell execution)
221
+ /system\s*\(/, # System calls
222
+ /exec\s*\(/, # Exec calls
223
+ /eval\s*\(/, # Nested eval
224
+ /require\s+['"]/, # Require statements
225
+ /load\s+['"]/, # Load statements
226
+ /File\./, # File operations
227
+ /IO\./, # IO operations
228
+ /Dir\./, # Directory operations
229
+ /Kernel\./ # Kernel operations
230
+ ]
231
+
232
+ dangerous_patterns.each do |pattern|
233
+ raise SecurityError, "Potentially dangerous code detected in eval string: #{method_string.inspect}" if method_string.match?(pattern)
234
+ end
235
+
236
+ # Basic syntax validation - but allow yield since it's valid in block context
237
+ begin
238
+ test_code = method_string.include?('yield') ? "def dummy_method; #{method_string}; end" : method_string
239
+ SyntaxValidator.validate!(test_code, '(eval)')
240
+ rescue SyntaxError => e
241
+ raise ArgumentError, "Invalid Ruby syntax in eval string: #{e.message}"
84
242
  end
85
243
  end
86
244
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options_validator'
4
+
1
5
  module StateMachines
2
6
  # An event defines an action that transitions an attribute from one state to
3
7
  # another. The state that an attribute is transitioned to depends on the
4
8
  # branches configured for the event.
5
9
  class Event
6
-
7
10
  include MatcherHelpers
8
11
 
9
12
  # The state machine for which this event is defined
@@ -12,7 +15,7 @@ module StateMachines
12
15
  # The name of the event
13
16
  attr_reader :name
14
17
 
15
- # The fully-qualified name of the event, scoped by the machine's namespace
18
+ # The fully-qualified name of the event, scoped by the machine's namespace
16
19
  attr_reader :qualified_name
17
20
 
18
21
  # The human-readable name for the event
@@ -27,20 +30,32 @@ module StateMachines
27
30
  attr_reader :known_states
28
31
 
29
32
  # Creates a new event within the context of the given machine
30
- #
33
+ #
31
34
  # Configuration options:
32
35
  # * <tt>:human_name</tt> - The human-readable version of this event's name
33
- def initialize(machine, name, options = {}) #:nodoc:
34
- options.assert_valid_keys(:human_name)
36
+ def initialize(machine, name, options = nil, human_name: nil, **extra_options) # :nodoc:
37
+ # Handle both old hash style and new kwargs style for backward compatibility
38
+ case options
39
+ in Hash
40
+ # Old style: initialize(machine, name, {human_name: 'Custom Name'})
41
+ StateMachines::OptionsValidator.assert_valid_keys!(options, :human_name)
42
+ human_name = options[:human_name]
43
+ in nil
44
+ # New style: initialize(machine, name, human_name: 'Custom Name')
45
+ StateMachines::OptionsValidator.assert_valid_keys!(extra_options, :human_name) unless extra_options.empty?
46
+ else
47
+ # Handle unexpected options
48
+ raise ArgumentError, "Unexpected positional argument in Event initialize: #{options.inspect}"
49
+ end
35
50
 
36
51
  @machine = machine
37
52
  @name = name
38
53
  @qualified_name = machine.namespace ? :"#{name}_#{machine.namespace}" : name
39
- @human_name = options[:human_name] || @name.to_s.tr('_', ' ')
54
+ @human_name = human_name || @name.to_s.tr('_', ' ')
40
55
  reset
41
56
 
42
57
  # Output a warning if another event has a conflicting qualified name
43
- if conflict = machine.owner_class.state_machines.detect { |_other_name, other_machine| other_machine != @machine && other_machine.events[qualified_name, :qualified_name] }
58
+ if (conflict = machine.owner_class.state_machines.detect { |_other_name, other_machine| other_machine != @machine && other_machine.events[qualified_name, :qualified_name] })
44
59
  _name, other_machine = conflict
45
60
  warn "Event #{qualified_name.inspect} for #{machine.name.inspect} is already defined in #{other_machine.name.inspect}"
46
61
  else
@@ -50,7 +65,7 @@ module StateMachines
50
65
 
51
66
  # Creates a copy of this event in addition to the list of associated
52
67
  # branches to prevent conflicts across events within a class hierarchy.
53
- def initialize_copy(orig) #:nodoc:
68
+ def initialize_copy(orig) # :nodoc:
54
69
  super
55
70
  @branches = @branches.dup
56
71
  @known_states = @known_states.dup
@@ -64,24 +79,24 @@ module StateMachines
64
79
 
65
80
  # Evaluates the given block within the context of this event. This simply
66
81
  # provides a DSL-like syntax for defining transitions.
67
- def context(&block)
68
- instance_eval(&block)
82
+ def context(&)
83
+ instance_eval(&)
69
84
  end
70
85
 
71
86
  # Creates a new transition that determines what to change the current state
72
87
  # to when this event fires.
73
- #
88
+ #
74
89
  # Since this transition is being defined within an event context, you do
75
90
  # *not* need to specify the <tt>:on</tt> option for the transition. For
76
91
  # example:
77
- #
92
+ #
78
93
  # state_machine do
79
94
  # event :ignite do
80
95
  # transition :parked => :idling, :idling => same, :if => :seatbelt_on? # Transitions to :idling if seatbelt is on
81
96
  # transition all => :parked, :unless => :seatbelt_on? # Transitions to :parked if seatbelt is off
82
97
  # end
83
98
  # end
84
- #
99
+ #
85
100
  # See StateMachines::Machine#transition for a description of the possible
86
101
  # configurations for defining transitions.
87
102
  def transition(options)
@@ -89,18 +104,18 @@ module StateMachines
89
104
 
90
105
  # Only a certain subset of explicit options are allowed for transition
91
106
  # requirements
92
- options.assert_valid_keys(:from, :to, :except_from, :except_to, :if, :unless) if (options.keys - [:from, :to, :on, :except_from, :except_to, :except_on, :if, :unless]).empty?
107
+ StateMachines::OptionsValidator.assert_valid_keys!(options, :from, :to, :except_from, :except_to, :if, :unless, :if_state, :unless_state, :if_all_states, :unless_all_states, :if_any_state, :unless_any_state) if (options.keys - %i[from to on except_from except_to except_on if unless if_state unless_state if_all_states unless_all_states if_any_state unless_any_state]).empty?
93
108
 
94
- branches << branch = Branch.new(options.merge(:on => name))
109
+ branches << branch = Branch.new(options.merge(on: name))
95
110
  @known_states |= branch.known_states
96
111
  branch
97
112
  end
98
113
 
99
114
  # Determines whether any transitions can be performed for this event based
100
115
  # on the current state of the given object.
101
- #
116
+ #
102
117
  # If the event can't be fired, then this will return false, otherwise true.
103
- #
118
+ #
104
119
  # *Note* that this will not take the object context into account. Although
105
120
  # a transition may be possible based on the state machine definition,
106
121
  # object-specific behaviors (like validations) may prevent it from firing.
@@ -110,7 +125,7 @@ module StateMachines
110
125
 
111
126
  # Finds and builds the next transition that can be performed on the given
112
127
  # object. If no transitions can be made, then this will return nil.
113
- #
128
+ #
114
129
  # Valid requirement options:
115
130
  # * <tt>:from</tt> - One or more states being transitioned from. If none
116
131
  # are specified, then this will be the object's current state.
@@ -118,24 +133,26 @@ module StateMachines
118
133
  # specified, then this will match any to state.
119
134
  # * <tt>:guard</tt> - Whether to guard transitions with the if/unless
120
135
  # conditionals defined for each one. Default is true.
121
- def transition_for(object, requirements = {})
122
- requirements.assert_valid_keys(:from, :to, :guard)
123
- requirements[:from] = machine.states.match!(object).name unless custom_from_state = requirements.include?(:from)
136
+ #
137
+ # Event arguments are passed to guard conditions if they accept multiple parameters.
138
+ def transition_for(object, requirements = {}, *event_args)
139
+ StateMachines::OptionsValidator.assert_valid_keys!(requirements, :from, :to, :guard)
140
+ requirements[:from] = machine.states.match!(object).name unless (custom_from_state = requirements.include?(:from))
124
141
 
125
142
  branches.each do |branch|
126
- if match = branch.match(object, requirements)
127
- # Branch allows for the transition to occur
128
- from = requirements[:from]
129
- to = if match[:to].is_a?(LoopbackMatcher)
130
- from
131
- else
132
- values = requirements.include?(:to) ? [requirements[:to]].flatten : [from] | machine.states.map { |state| state.name }
133
-
134
- match[:to].filter(values).first
135
- end
136
-
137
- return Transition.new(object, machine, name, from, to, !custom_from_state)
138
- end
143
+ next unless (match = branch.match(object, requirements, event_args))
144
+
145
+ # Branch allows for the transition to occur
146
+ from = requirements[:from]
147
+ to = if match[:to].is_a?(LoopbackMatcher)
148
+ from
149
+ else
150
+ values = requirements.include?(:to) ? [requirements[:to]].flatten : [from] | machine.states.map { |state| state.name }
151
+
152
+ match[:to].filter(values).first
153
+ end
154
+
155
+ return Transition.new(object, machine, name, from, to, !custom_from_state)
139
156
  end
140
157
 
141
158
  # No transition matched
@@ -145,27 +162,29 @@ module StateMachines
145
162
  # Attempts to perform the next available transition on the given object.
146
163
  # If no transitions can be made, then this will return false, otherwise
147
164
  # true.
148
- #
165
+ #
149
166
  # Any additional arguments are passed to the StateMachines::Transition#perform
150
167
  # instance method.
151
- def fire(object, *args)
168
+ def fire(object, *event_args)
152
169
  machine.reset(object)
153
170
 
154
- if transition = transition_for(object)
155
- transition.perform(*args)
171
+ if (transition = transition_for(object, {}, *event_args))
172
+ transition.perform(*event_args)
156
173
  else
157
- on_failure(object)
174
+ on_failure(object, *event_args)
158
175
  false
159
176
  end
160
177
  end
161
178
 
162
179
  # Marks the object as invalid and runs any failure callbacks associated with
163
180
  # this event. This should get called anytime this event fails to transition.
164
- def on_failure(object)
181
+ def on_failure(object, *args)
165
182
  state = machine.states.match!(object)
166
183
  machine.invalidate(object, :state, :invalid_transition, [[:event, human_name(object.class)], [:state, state.human_name(object.class)]])
167
184
 
168
- Transition.new(object, machine, name, state.name, state.name).run_callbacks(:before => false)
185
+ transition = Transition.new(object, machine, name, state.name, state.name)
186
+ transition.args = args if args.any?
187
+ transition.run_callbacks(before: false)
169
188
  end
170
189
 
171
190
  # Resets back to the initial state of the event, with no branches / known
@@ -177,51 +196,51 @@ module StateMachines
177
196
  @known_states = []
178
197
  end
179
198
 
180
-
181
- def draw(graph, options = {})
182
- fail NotImplementedError
199
+ def draw(graph, options = {}, io = $stdout)
200
+ machine.renderer.draw_event(self, graph, options, io)
183
201
  end
184
202
 
185
203
  # Generates a nicely formatted description of this event's contents.
186
- #
204
+ #
187
205
  # For example,
188
- #
206
+ #
189
207
  # event = StateMachines::Event.new(machine, :park)
190
208
  # event.transition all - :idling => :parked, :idling => same
191
209
  # event # => #<StateMachines::Event name=:park transitions=[all - :idling => :parked, :idling => same]>
192
210
  def inspect
193
- transitions = branches.map do |branch|
211
+ transitions = branches.flat_map do |branch|
194
212
  branch.state_requirements.map do |state_requirement|
195
213
  "#{state_requirement[:from].description} => #{state_requirement[:to].description}"
196
- end * ', '
197
- end
214
+ end
215
+ end.join(', ')
198
216
 
199
- "#<#{self.class} name=#{name.inspect} transitions=[#{transitions * ', '}]>"
217
+ "#<#{self.class} name=#{name.inspect} transitions=[#{transitions}]>"
200
218
  end
201
219
 
202
220
  protected
221
+
203
222
  # Add the various instance methods that can transition the object using
204
223
  # the current event
205
224
  def add_actions
206
225
  # Checks whether the event can be fired on the current object
207
- machine.define_helper(:instance, "can_#{qualified_name}?") do |machine, object, *args|
208
- machine.event(name).can_fire?(object, *args)
226
+ machine.define_helper(:instance, "can_#{qualified_name}?") do |machine, object, *args, **kwargs|
227
+ machine.event(name).can_fire?(object, *args, **kwargs)
209
228
  end
210
229
 
211
230
  # Gets the next transition that would be performed if the event were
212
231
  # fired now
213
- machine.define_helper(:instance, "#{qualified_name}_transition") do |machine, object, *args|
214
- machine.event(name).transition_for(object, *args)
232
+ machine.define_helper(:instance, "#{qualified_name}_transition") do |machine, object, *args, **kwargs|
233
+ machine.event(name).transition_for(object, *args, **kwargs)
215
234
  end
216
235
 
217
236
  # Fires the event
218
- machine.define_helper(:instance, qualified_name) do |machine, object, *args|
219
- machine.event(name).fire(object, *args)
237
+ machine.define_helper(:instance, qualified_name) do |machine, object, *args, **kwargs|
238
+ machine.event(name).fire(object, *args, **kwargs)
220
239
  end
221
240
 
222
241
  # Fires the event, raising an exception if it fails
223
- machine.define_helper(:instance, "#{qualified_name}!") do |machine, object, *args|
224
- object.send(qualified_name, *args) || raise(StateMachines::InvalidTransition.new(object, machine, name))
242
+ machine.define_helper(:instance, "#{qualified_name}!") do |machine, object, *args, **kwargs|
243
+ object.send(qualified_name, *args, **kwargs) || raise(StateMachines::InvalidTransition.new(object, machine, name))
225
244
  end
226
245
  end
227
246
  end