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.
- checksums.yaml +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +443 -22
- data/lib/state_machines/async_mode/async_event_extensions.rb +49 -0
- data/lib/state_machines/async_mode/async_events.rb +282 -0
- data/lib/state_machines/async_mode/async_machine.rb +60 -0
- data/lib/state_machines/async_mode/async_transition_collection.rb +141 -0
- data/lib/state_machines/async_mode/thread_safe_state.rb +47 -0
- data/lib/state_machines/async_mode.rb +64 -0
- data/lib/state_machines/branch.rb +146 -86
- data/lib/state_machines/callback.rb +35 -32
- data/lib/state_machines/core.rb +3 -3
- data/lib/state_machines/core_ext/class/state_machine.rb +2 -0
- data/lib/state_machines/core_ext.rb +2 -0
- data/lib/state_machines/error.rb +7 -4
- data/lib/state_machines/eval_helpers.rb +197 -39
- data/lib/state_machines/event.rb +77 -58
- data/lib/state_machines/event_collection.rb +49 -39
- data/lib/state_machines/extensions.rb +6 -4
- data/lib/state_machines/helper_module.rb +4 -2
- data/lib/state_machines/integrations/base.rb +3 -1
- data/lib/state_machines/integrations.rb +19 -20
- data/lib/state_machines/machine/action_hooks.rb +53 -0
- data/lib/state_machines/machine/async_extensions.rb +88 -0
- data/lib/state_machines/machine/callbacks.rb +59 -0
- data/lib/state_machines/machine/class_methods.rb +97 -0
- data/lib/state_machines/machine/configuration.rb +134 -0
- data/lib/state_machines/machine/event_methods.rb +59 -0
- data/lib/state_machines/machine/helper_generators.rb +125 -0
- data/lib/state_machines/machine/integration.rb +70 -0
- data/lib/state_machines/machine/parsing.rb +77 -0
- data/lib/state_machines/machine/rendering.rb +17 -0
- data/lib/state_machines/machine/scoping.rb +44 -0
- data/lib/state_machines/machine/state_methods.rb +101 -0
- data/lib/state_machines/machine/utilities.rb +85 -0
- data/lib/state_machines/machine/validation.rb +39 -0
- data/lib/state_machines/machine.rb +425 -1011
- data/lib/state_machines/machine_collection.rb +28 -19
- data/lib/state_machines/macro_methods.rb +104 -102
- data/lib/state_machines/matcher.rb +31 -28
- data/lib/state_machines/matcher_helpers.rb +14 -12
- data/lib/state_machines/node_collection.rb +36 -29
- data/lib/state_machines/options_validator.rb +72 -0
- data/lib/state_machines/path.rb +60 -57
- data/lib/state_machines/path_collection.rb +39 -36
- data/lib/state_machines/state.rb +84 -47
- data/lib/state_machines/state_collection.rb +22 -19
- data/lib/state_machines/state_context.rb +40 -39
- data/lib/state_machines/stdio_renderer.rb +74 -0
- data/lib/state_machines/syntax_validator.rb +57 -0
- data/lib/state_machines/test_helper.rb +896 -0
- data/lib/state_machines/transition.rb +215 -199
- data/lib/state_machines/transition_collection.rb +187 -170
- data/lib/state_machines/version.rb +3 -1
- data/lib/state_machines.rb +4 -1
- metadata +39 -446
- data/.gitignore +0 -21
- data/.rspec +0 -3
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -16
- data/Changelog.md +0 -22
- data/Contributors.md +0 -39
- data/Gemfile +0 -8
- data/Rakefile +0 -12
- data/Testing.md +0 -0
- data/lib/state_machines/assertions.rb +0 -40
- data/state_machines.gemspec +0 -22
- data/test/files/integrations/event_on_failure_integration.rb +0 -10
- data/test/files/integrations/vehicle.rb +0 -7
- data/test/files/models/auto_shop.rb +0 -31
- data/test/files/models/car.rb +0 -21
- data/test/files/models/driver.rb +0 -13
- data/test/files/models/model_base.rb +0 -6
- data/test/files/models/motorcycle.rb +0 -16
- data/test/files/models/traffic_light.rb +0 -47
- data/test/files/models/vehicle.rb +0 -127
- data/test/files/node.rb +0 -5
- data/test/files/switch.rb +0 -15
- data/test/functional/auto_shop_available_test.rb +0 -20
- data/test/functional/auto_shop_busy_test.rb +0 -25
- data/test/functional/car_backing_up_test.rb +0 -45
- data/test/functional/car_test.rb +0 -49
- data/test/functional/driver_default_nonstandard_test.rb +0 -13
- data/test/functional/motorcycle_test.rb +0 -52
- data/test/functional/traffic_light_caution_test.rb +0 -17
- data/test/functional/traffic_light_proceed_test.rb +0 -17
- data/test/functional/traffic_light_stop_test.rb +0 -26
- data/test/functional/vehicle_first_gear_test.rb +0 -42
- data/test/functional/vehicle_idling_test.rb +0 -59
- data/test/functional/vehicle_locked_test.rb +0 -29
- data/test/functional/vehicle_parked_test.rb +0 -53
- data/test/functional/vehicle_repaired_test.rb +0 -20
- data/test/functional/vehicle_second_gear_test.rb +0 -42
- data/test/functional/vehicle_stalled_test.rb +0 -65
- data/test/functional/vehicle_test.rb +0 -20
- data/test/functional/vehicle_third_gear_test.rb +0 -42
- data/test/functional/vehicle_unsaved_test.rb +0 -181
- data/test/functional/vehicle_with_event_attributes_test.rb +0 -30
- data/test/functional/vehicle_with_parallel_events_test.rb +0 -36
- data/test/test_helper.rb +0 -15
- data/test/unit/assertions/assert_exclusive_keys_test.rb +0 -22
- data/test/unit/assertions/assert_valid_key_test.rb +0 -12
- data/test/unit/branch/branch_test.rb +0 -28
- data/test/unit/branch/branch_with_conflicting_conditionals_test.rb +0 -27
- data/test/unit/branch/branch_with_conflicting_from_requirements_test.rb +0 -8
- data/test/unit/branch/branch_with_conflicting_on_requirements_test.rb +0 -8
- data/test/unit/branch/branch_with_conflicting_to_requirements_test.rb +0 -8
- data/test/unit/branch/branch_with_different_requirements_test.rb +0 -41
- data/test/unit/branch/branch_with_except_from_matcher_requirement_test.rb +0 -8
- data/test/unit/branch/branch_with_except_from_requirement_test.rb +0 -36
- data/test/unit/branch/branch_with_except_on_matcher_requirement_test.rb +0 -8
- data/test/unit/branch/branch_with_except_on_requirement_test.rb +0 -36
- data/test/unit/branch/branch_with_except_to_matcher_requirement_test.rb +0 -8
- data/test/unit/branch/branch_with_except_to_requirement_test.rb +0 -36
- data/test/unit/branch/branch_with_from_matcher_requirement_test.rb +0 -20
- data/test/unit/branch/branch_with_from_requirement_test.rb +0 -45
- data/test/unit/branch/branch_with_if_conditional_test.rb +0 -27
- data/test/unit/branch/branch_with_implicit_and_explicit_requirements_test.rb +0 -23
- data/test/unit/branch/branch_with_implicit_from_requirement_matcher_test.rb +0 -20
- data/test/unit/branch/branch_with_implicit_requirement_test.rb +0 -20
- data/test/unit/branch/branch_with_implicit_to_requirement_matcher_test.rb +0 -16
- data/test/unit/branch/branch_with_multiple_except_from_requirements_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_except_on_requirements_test.rb +0 -16
- data/test/unit/branch/branch_with_multiple_except_to_requirements_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_from_requirements_test.rb +0 -16
- data/test/unit/branch/branch_with_multiple_if_conditionals_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_implicit_requirements_test.rb +0 -53
- data/test/unit/branch/branch_with_multiple_to_requirements_test.rb +0 -20
- data/test/unit/branch/branch_with_multiple_unless_conditionals_test.rb +0 -20
- data/test/unit/branch/branch_with_nil_requirements_test.rb +0 -28
- data/test/unit/branch/branch_with_no_requirements_test.rb +0 -36
- data/test/unit/branch/branch_with_on_matcher_requirement_test.rb +0 -16
- data/test/unit/branch/branch_with_on_requirement_test.rb +0 -45
- data/test/unit/branch/branch_with_to_matcher_requirement_test.rb +0 -20
- data/test/unit/branch/branch_with_to_requirement_test.rb +0 -45
- data/test/unit/branch/branch_with_unless_conditional_test.rb +0 -27
- data/test/unit/branch/branch_without_guards_test.rb +0 -27
- data/test/unit/callback/callback_by_default_test.rb +0 -25
- data/test/unit/callback/callback_test.rb +0 -53
- data/test/unit/callback/callback_with_application_bound_object_test.rb +0 -23
- data/test/unit/callback/callback_with_application_terminator_test.rb +0 -24
- data/test/unit/callback/callback_with_arguments_test.rb +0 -14
- data/test/unit/callback/callback_with_around_type_and_arguments_test.rb +0 -25
- data/test/unit/callback/callback_with_around_type_and_block_test.rb +0 -44
- data/test/unit/callback/callback_with_around_type_and_bound_method_test.rb +0 -23
- data/test/unit/callback/callback_with_around_type_and_multiple_methods_test.rb +0 -93
- data/test/unit/callback/callback_with_around_type_and_terminator_test.rb +0 -17
- data/test/unit/callback/callback_with_block_test.rb +0 -20
- data/test/unit/callback/callback_with_bound_method_and_arguments_test.rb +0 -28
- data/test/unit/callback/callback_with_bound_method_test.rb +0 -35
- data/test/unit/callback/callback_with_do_method_test.rb +0 -18
- data/test/unit/callback/callback_with_explicit_requirements_test.rb +0 -32
- data/test/unit/callback/callback_with_if_condition_test.rb +0 -17
- data/test/unit/callback/callback_with_implicit_requirements_test.rb +0 -32
- data/test/unit/callback/callback_with_method_argument_test.rb +0 -18
- data/test/unit/callback/callback_with_mixed_methods_test.rb +0 -31
- data/test/unit/callback/callback_with_multiple_bound_methods_test.rb +0 -21
- data/test/unit/callback/callback_with_multiple_do_methods_test.rb +0 -29
- data/test/unit/callback/callback_with_multiple_method_arguments_test.rb +0 -29
- data/test/unit/callback/callback_with_terminator_test.rb +0 -22
- data/test/unit/callback/callback_with_unbound_method_test.rb +0 -14
- data/test/unit/callback/callback_with_unless_condition_test.rb +0 -17
- data/test/unit/callback/callback_without_arguments_test.rb +0 -14
- data/test/unit/callback/callback_without_terminator_test.rb +0 -12
- data/test/unit/error/error_by_default_test.rb +0 -21
- data/test/unit/error/error_with_message_test.rb +0 -23
- data/test/unit/eval_helper/eval_helpers_base_test.rb +0 -8
- data/test/unit/eval_helper/eval_helpers_proc_block_and_explicit_arguments_test.rb +0 -14
- data/test/unit/eval_helper/eval_helpers_proc_block_and_implicit_arguments_test.rb +0 -14
- data/test/unit/eval_helper/eval_helpers_proc_test.rb +0 -13
- data/test/unit/eval_helper/eval_helpers_proc_with_arguments_test.rb +0 -13
- data/test/unit/eval_helper/eval_helpers_proc_with_block_test.rb +0 -13
- data/test/unit/eval_helper/eval_helpers_proc_with_block_without_arguments_test.rb +0 -18
- data/test/unit/eval_helper/eval_helpers_proc_with_block_without_object_test.rb +0 -14
- data/test/unit/eval_helper/eval_helpers_proc_without_arguments_test.rb +0 -19
- data/test/unit/eval_helper/eval_helpers_string_test.rb +0 -25
- data/test/unit/eval_helper/eval_helpers_string_with_block_test.rb +0 -12
- data/test/unit/eval_helper/eval_helpers_symbol_method_missing_test.rb +0 -20
- data/test/unit/eval_helper/eval_helpers_symbol_private_test.rb +0 -17
- data/test/unit/eval_helper/eval_helpers_symbol_protected_test.rb +0 -17
- data/test/unit/eval_helper/eval_helpers_symbol_tainted_method_test.rb +0 -18
- data/test/unit/eval_helper/eval_helpers_symbol_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_and_block_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_symbol_with_arguments_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_symbol_with_block_test.rb +0 -16
- data/test/unit/eval_helper/eval_helpers_test.rb +0 -13
- data/test/unit/event/event_after_being_copied_test.rb +0 -17
- data/test/unit/event/event_by_default_test.rb +0 -60
- data/test/unit/event/event_context_test.rb +0 -16
- data/test/unit/event/event_on_failure_test.rb +0 -44
- data/test/unit/event/event_test.rb +0 -34
- data/test/unit/event/event_transitions_test.rb +0 -62
- data/test/unit/event/event_with_conflicting_helpers_after_definition_test.rb +0 -79
- data/test/unit/event/event_with_conflicting_helpers_before_definition_test.rb +0 -58
- data/test/unit/event/event_with_conflicting_machine_test.rb +0 -48
- data/test/unit/event/event_with_dynamic_human_name_test.rb +0 -26
- data/test/unit/event/event_with_human_name_test.rb +0 -13
- data/test/unit/event/event_with_invalid_current_state_test.rb +0 -30
- data/test/unit/event/event_with_machine_action_test.rb +0 -33
- data/test/unit/event/event_with_marshalling_test.rb +0 -47
- data/test/unit/event/event_with_matching_disabled_transitions_test.rb +0 -115
- data/test/unit/event/event_with_matching_enabled_transitions_test.rb +0 -75
- data/test/unit/event/event_with_multiple_transitions_test.rb +0 -61
- data/test/unit/event/event_with_namespace_test.rb +0 -34
- data/test/unit/event/event_with_transition_with_blacklisted_to_state_test.rb +0 -60
- data/test/unit/event/event_with_transition_with_loopback_state_test.rb +0 -36
- data/test/unit/event/event_with_transition_with_nil_to_state_test.rb +0 -36
- data/test/unit/event/event_with_transition_with_whitelisted_to_state_test.rb +0 -51
- data/test/unit/event/event_with_transition_without_to_state_test.rb +0 -36
- data/test/unit/event/event_with_transitions_test.rb +0 -32
- data/test/unit/event/event_without_matching_transitions_test.rb +0 -41
- data/test/unit/event/event_without_transitions_test.rb +0 -28
- data/test/unit/event/invalid_event_test.rb +0 -20
- data/test/unit/event_collection/event_collection_attribute_with_machine_action_test.rb +0 -62
- data/test/unit/event_collection/event_collection_attribute_with_namespaced_machine_test.rb +0 -36
- data/test/unit/event_collection/event_collection_by_default_test.rb +0 -26
- data/test/unit/event_collection/event_collection_test.rb +0 -39
- data/test/unit/event_collection/event_collection_with_custom_machine_attribute_test.rb +0 -31
- data/test/unit/event_collection/event_collection_with_events_with_transitions_test.rb +0 -76
- data/test/unit/event_collection/event_collection_with_multiple_events_test.rb +0 -27
- data/test/unit/event_collection/event_collection_with_validations_test.rb +0 -74
- data/test/unit/event_collection/event_collection_without_machine_action_test.rb +0 -18
- data/test/unit/event_collection/event_string_collection_test.rb +0 -31
- data/test/unit/helper_module_test.rb +0 -17
- data/test/unit/integrations/integration_finder_test.rb +0 -16
- data/test/unit/integrations/integration_matcher_test.rb +0 -29
- data/test/unit/invalid_transition/invalid_parallel_transition_test.rb +0 -18
- data/test/unit/invalid_transition/invalid_transition_test.rb +0 -47
- data/test/unit/invalid_transition/invalid_transition_with_integration_test.rb +0 -45
- data/test/unit/invalid_transition/invalid_transition_with_namespace_test.rb +0 -32
- data/test/unit/machine/machine_after_being_copied_test.rb +0 -62
- data/test/unit/machine/machine_after_changing_initial_state.rb +0 -28
- data/test/unit/machine/machine_after_changing_owner_class_test.rb +0 -31
- data/test/unit/machine/machine_by_default_test.rb +0 -160
- data/test/unit/machine/machine_finder_custom_options_test.rb +0 -17
- data/test/unit/machine/machine_finder_with_existing_machine_on_superclass_test.rb +0 -85
- data/test/unit/machine/machine_finder_with_existing_on_same_class_test.rb +0 -23
- data/test/unit/machine/machine_finder_without_existing_machine_test.rb +0 -25
- data/test/unit/machine/machine_persistence_test.rb +0 -52
- data/test/unit/machine/machine_state_initialization_test.rb +0 -56
- data/test/unit/machine/machine_test.rb +0 -30
- data/test/unit/machine/machine_with_action_already_overridden_test.rb +0 -23
- data/test/unit/machine/machine_with_action_defined_in_class_test.rb +0 -37
- data/test/unit/machine/machine_with_action_defined_in_included_module_test.rb +0 -46
- data/test/unit/machine/machine_with_action_defined_in_superclass_test.rb +0 -43
- data/test/unit/machine/machine_with_action_undefined_test.rb +0 -33
- data/test/unit/machine/machine_with_cached_state_test.rb +0 -20
- data/test/unit/machine/machine_with_class_helpers_test.rb +0 -179
- data/test/unit/machine/machine_with_conflicting_helpers_after_definition_test.rb +0 -244
- data/test/unit/machine/machine_with_conflicting_helpers_before_definition_test.rb +0 -175
- data/test/unit/machine/machine_with_custom_action_test.rb +0 -11
- data/test/unit/machine/machine_with_custom_attribute_test.rb +0 -103
- data/test/unit/machine/machine_with_custom_initialize_test.rb +0 -24
- data/test/unit/machine/machine_with_custom_integration_test.rb +0 -72
- data/test/unit/machine/machine_with_custom_invalidation_test.rb +0 -39
- data/test/unit/machine/machine_with_custom_name_test.rb +0 -57
- data/test/unit/machine/machine_with_custom_plural_test.rb +0 -52
- data/test/unit/machine/machine_with_dynamic_initial_state_test.rb +0 -65
- data/test/unit/machine/machine_with_event_matchers_test.rb +0 -41
- data/test/unit/machine/machine_with_events_test.rb +0 -52
- data/test/unit/machine/machine_with_events_with_custom_human_names_test.rb +0 -18
- data/test/unit/machine/machine_with_events_with_transitions_test.rb +0 -37
- data/test/unit/machine/machine_with_existing_event_test.rb +0 -17
- data/test/unit/machine/machine_with_existing_machines_on_owner_class_test.rb +0 -20
- data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_class_test.rb +0 -71
- data/test/unit/machine/machine_with_existing_machines_with_same_attributes_on_owner_subclass_test.rb +0 -31
- data/test/unit/machine/machine_with_existing_state_test.rb +0 -27
- data/test/unit/machine/machine_with_failure_callbacks_test.rb +0 -48
- data/test/unit/machine/machine_with_helpers_test.rb +0 -14
- data/test/unit/machine/machine_with_initial_state_with_value_and_owner_default.rb +0 -25
- data/test/unit/machine/machine_with_initialize_and_super_test.rb +0 -17
- data/test/unit/machine/machine_with_initialize_arguments_and_block_test.rb +0 -31
- data/test/unit/machine/machine_with_initialize_without_super_test.rb +0 -17
- data/test/unit/machine/machine_with_instance_helpers_test.rb +0 -179
- data/test/unit/machine/machine_with_integration_test.rb +0 -72
- data/test/unit/machine/machine_with_multiple_events_test.rb +0 -32
- data/test/unit/machine/machine_with_namespace_test.rb +0 -48
- data/test/unit/machine/machine_with_nil_action_test.rb +0 -27
- data/test/unit/machine/machine_with_other_states.rb +0 -22
- data/test/unit/machine/machine_with_owner_subclass_test.rb +0 -18
- data/test/unit/machine/machine_with_paths_test.rb +0 -25
- data/test/unit/machine/machine_with_private_action_test.rb +0 -43
- data/test/unit/machine/machine_with_state_matchers_test.rb +0 -41
- data/test/unit/machine/machine_with_state_with_matchers_test.rb +0 -19
- data/test/unit/machine/machine_with_states_test.rb +0 -55
- data/test/unit/machine/machine_with_states_with_behaviors_test.rb +0 -23
- data/test/unit/machine/machine_with_states_with_custom_human_names_test.rb +0 -18
- data/test/unit/machine/machine_with_states_with_custom_values_test.rb +0 -21
- data/test/unit/machine/machine_with_states_with_runtime_dependencies_test.rb +0 -19
- data/test/unit/machine/machine_with_static_initial_state_test.rb +0 -49
- data/test/unit/machine/machine_with_superclass_conflicting_helpers_after_definition_test.rb +0 -36
- data/test/unit/machine/machine_with_transition_callbacks_test.rb +0 -144
- data/test/unit/machine/machine_with_transitions_test.rb +0 -87
- data/test/unit/machine/machine_without_initialization_test.rb +0 -31
- data/test/unit/machine/machine_without_initialize_test.rb +0 -14
- data/test/unit/machine/machine_without_integration_test.rb +0 -31
- data/test/unit/machine_collection/machine_collection_by_default_test.rb +0 -11
- data/test/unit/machine_collection/machine_collection_fire_test.rb +0 -80
- data/test/unit/machine_collection/machine_collection_fire_with_transactions_test.rb +0 -54
- data/test/unit/machine_collection/machine_collection_fire_with_validations_test.rb +0 -76
- data/test/unit/machine_collection/machine_collection_state_initialization_test.rb +0 -111
- data/test/unit/machine_collection/machine_collection_transitions_with_blank_events_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_with_custom_options_test.rb +0 -20
- data/test/unit/machine_collection/machine_collection_transitions_with_different_actions_test.rb +0 -26
- data/test/unit/machine_collection/machine_collection_transitions_with_exisiting_transitions_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_with_invalid_events_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_with_same_actions_test.rb +0 -31
- data/test/unit/machine_collection/machine_collection_transitions_with_transition_test.rb +0 -26
- data/test/unit/machine_collection/machine_collection_transitions_without_events_test.rb +0 -25
- data/test/unit/machine_collection/machine_collection_transitions_without_transition_test.rb +0 -27
- data/test/unit/matcher/all_matcher_test.rb +0 -29
- data/test/unit/matcher/blacklist_matcher_test.rb +0 -30
- data/test/unit/matcher/loopback_matcher_test.rb +0 -27
- data/test/unit/matcher/matcher_by_default_test.rb +0 -15
- data/test/unit/matcher/matcher_with_multiple_values_test.rb +0 -15
- data/test/unit/matcher/matcher_with_value_test.rb +0 -15
- data/test/unit/matcher/whitelist_matcher_test.rb +0 -30
- data/test/unit/matcher_helpers/matcher_helpers_all_test.rb +0 -14
- data/test/unit/matcher_helpers/matcher_helpers_any_test.rb +0 -14
- data/test/unit/matcher_helpers/matcher_helpers_same_test.rb +0 -13
- data/test/unit/node_collection/node_collection_after_being_copied_test.rb +0 -46
- data/test/unit/node_collection/node_collection_after_update_test.rb +0 -36
- data/test/unit/node_collection/node_collection_by_default_test.rb +0 -22
- data/test/unit/node_collection/node_collection_test.rb +0 -23
- data/test/unit/node_collection/node_collection_with_indices_test.rb +0 -42
- data/test/unit/node_collection/node_collection_with_matcher_contexts_test.rb +0 -25
- data/test/unit/node_collection/node_collection_with_nodes_test.rb +0 -46
- data/test/unit/node_collection/node_collection_with_numeric_index_test.rb +0 -24
- data/test/unit/node_collection/node_collection_with_postdefined_contexts_test.rb +0 -22
- data/test/unit/node_collection/node_collection_with_predefined_contexts_test.rb +0 -23
- data/test/unit/node_collection/node_collection_with_string_index_test.rb +0 -20
- data/test/unit/node_collection/node_collection_with_symbol_index_test.rb +0 -20
- data/test/unit/node_collection/node_collection_without_indices_test.rb +0 -30
- data/test/unit/path/path_by_default_test.rb +0 -54
- data/test/unit/path/path_test.rb +0 -14
- data/test/unit/path/path_with_available_transitions_after_reaching_target_test.rb +0 -40
- data/test/unit/path/path_with_available_transitions_test.rb +0 -54
- data/test/unit/path/path_with_deep_target_reached_test.rb +0 -50
- data/test/unit/path/path_with_deep_target_test.rb +0 -40
- data/test/unit/path/path_with_duplicates_test.rb +0 -32
- data/test/unit/path/path_with_encountered_transitions_test.rb +0 -34
- data/test/unit/path/path_with_guarded_transitions_test.rb +0 -42
- data/test/unit/path/path_with_reached_target_test.rb +0 -35
- data/test/unit/path/path_with_transitions_test.rb +0 -54
- data/test/unit/path/path_with_unreached_target_test.rb +0 -31
- data/test/unit/path/path_without_transitions_test.rb +0 -24
- data/test/unit/path_collection/path_collection_by_default_test.rb +0 -46
- data/test/unit/path_collection/path_collection_test.rb +0 -24
- data/test/unit/path_collection/path_collection_with_deep_paths_test.rb +0 -43
- data/test/unit/path_collection/path_collection_with_duplicate_nodes_test.rb +0 -31
- data/test/unit/path_collection/path_collection_with_from_state_test.rb +0 -27
- data/test/unit/path_collection/path_collection_with_paths_test.rb +0 -47
- data/test/unit/path_collection/path_collection_with_to_state_test.rb +0 -29
- data/test/unit/path_collection/path_with_guarded_paths_test.rb +0 -25
- data/test/unit/state/state_after_being_copied_test.rb +0 -19
- data/test/unit/state/state_by_default_test.rb +0 -41
- data/test/unit/state/state_final_test.rb +0 -28
- data/test/unit/state/state_initial_test.rb +0 -13
- data/test/unit/state/state_not_final_test.rb +0 -32
- data/test/unit/state/state_not_initial_test.rb +0 -13
- data/test/unit/state/state_test.rb +0 -44
- data/test/unit/state/state_with_cached_lambda_value_test.rb +0 -29
- data/test/unit/state/state_with_conflicting_helpers_after_definition_test.rb +0 -38
- data/test/unit/state/state_with_conflicting_helpers_before_definition_test.rb +0 -29
- data/test/unit/state/state_with_conflicting_machine_name_test.rb +0 -20
- data/test/unit/state/state_with_conflicting_machine_test.rb +0 -37
- data/test/unit/state/state_with_context_test.rb +0 -60
- data/test/unit/state/state_with_dynamic_human_name_test.rb +0 -25
- data/test/unit/state/state_with_existing_context_method_test.rb +0 -24
- data/test/unit/state/state_with_human_name_test.rb +0 -13
- data/test/unit/state/state_with_integer_value_test.rb +0 -32
- data/test/unit/state/state_with_invalid_method_call_test.rb +0 -21
- data/test/unit/state/state_with_lambda_value_test.rb +0 -37
- data/test/unit/state/state_with_matcher_test.rb +0 -18
- data/test/unit/state/state_with_multiple_contexts_test.rb +0 -57
- data/test/unit/state/state_with_name_test.rb +0 -43
- data/test/unit/state/state_with_namespace_test.rb +0 -22
- data/test/unit/state/state_with_nil_value_test.rb +0 -35
- data/test/unit/state/state_with_redefined_context_method_test.rb +0 -45
- data/test/unit/state/state_with_symbolic_value_test.rb +0 -32
- data/test/unit/state/state_with_valid_inherited_method_call_for_current_state_test.rb +0 -40
- data/test/unit/state/state_with_valid_method_call_for_current_state_test.rb +0 -33
- data/test/unit/state/state_with_valid_method_call_for_different_state_test.rb +0 -41
- data/test/unit/state/state_without_cached_lambda_value_test.rb +0 -25
- data/test/unit/state/state_without_name_test.rb +0 -39
- data/test/unit/state_collection/state_collection_by_default_test.rb +0 -21
- data/test/unit/state_collection/state_collection_string_test.rb +0 -35
- data/test/unit/state_collection/state_collection_test.rb +0 -74
- data/test/unit/state_collection/state_collection_with_custom_state_values_test.rb +0 -29
- data/test/unit/state_collection/state_collection_with_event_transitions_test.rb +0 -39
- data/test/unit/state_collection/state_collection_with_initial_state_test.rb +0 -40
- data/test/unit/state_collection/state_collection_with_namespace_test.rb +0 -21
- data/test/unit/state_collection/state_collection_with_state_behaviors_test.rb +0 -40
- data/test/unit/state_collection/state_collection_with_state_matchers_test.rb +0 -29
- data/test/unit/state_collection/state_collection_with_transition_callbacks_test.rb +0 -40
- data/test/unit/state_context/state_context_proxy_test.rb +0 -26
- data/test/unit/state_context/state_context_proxy_with_if_and_unless_conditions_test.rb +0 -42
- data/test/unit/state_context/state_context_proxy_with_if_condition_test.rb +0 -64
- data/test/unit/state_context/state_context_proxy_with_multiple_if_conditions_test.rb +0 -32
- data/test/unit/state_context/state_context_proxy_with_multiple_unless_conditions_test.rb +0 -32
- data/test/unit/state_context/state_context_proxy_with_unless_condition_test.rb +0 -64
- data/test/unit/state_context/state_context_proxy_without_conditions_test.rb +0 -31
- data/test/unit/state_context/state_context_test.rb +0 -28
- data/test/unit/state_context/state_context_transition_test.rb +0 -104
- data/test/unit/state_context/state_context_with_matching_transition_test.rb +0 -27
- data/test/unit/state_machine/state_machine_by_default_test.rb +0 -12
- data/test/unit/state_machine/state_machine_test.rb +0 -20
- data/test/unit/transition/transition_after_being_performed_test.rb +0 -48
- data/test/unit/transition/transition_after_being_persisted_test.rb +0 -46
- data/test/unit/transition/transition_after_being_rolled_back_test.rb +0 -35
- data/test/unit/transition/transition_equality_test.rb +0 -52
- data/test/unit/transition/transition_loopback_test.rb +0 -18
- data/test/unit/transition/transition_test.rb +0 -96
- data/test/unit/transition/transition_transient_test.rb +0 -20
- data/test/unit/transition/transition_with_action_test.rb +0 -27
- data/test/unit/transition/transition_with_after_callbacks_skipped_test.rb +0 -127
- data/test/unit/transition/transition_with_after_callbacks_test.rb +0 -93
- data/test/unit/transition/transition_with_around_callbacks_test.rb +0 -141
- data/test/unit/transition/transition_with_before_callbacks_skipped_test.rb +0 -30
- data/test/unit/transition/transition_with_before_callbacks_test.rb +0 -104
- data/test/unit/transition/transition_with_custom_machine_attribute_test.rb +0 -28
- data/test/unit/transition/transition_with_different_states_test.rb +0 -18
- data/test/unit/transition/transition_with_dynamic_to_value_test.rb +0 -19
- data/test/unit/transition/transition_with_failure_callbacks_test.rb +0 -84
- data/test/unit/transition/transition_with_invalid_nodes_test.rb +0 -29
- data/test/unit/transition/transition_with_mixed_callbacks_test.rb +0 -105
- data/test/unit/transition/transition_with_multiple_after_callbacks_test.rb +0 -40
- data/test/unit/transition/transition_with_multiple_around_callbacks_test.rb +0 -114
- data/test/unit/transition/transition_with_multiple_before_callbacks_test.rb +0 -40
- data/test/unit/transition/transition_with_multiple_failure_callbacks_test.rb +0 -40
- data/test/unit/transition/transition_with_namespace_test.rb +0 -47
- data/test/unit/transition/transition_with_perform_arguments_test.rb +0 -35
- data/test/unit/transition/transition_with_transactions_test.rb +0 -42
- data/test/unit/transition/transition_without_callbacks_test.rb +0 -33
- data/test/unit/transition/transition_without_reading_state_test.rb +0 -22
- data/test/unit/transition/transition_without_running_action_test.rb +0 -47
- data/test/unit/transition_collection/attribute_transition_collection_by_default_test.rb +0 -23
- data/test/unit/transition_collection/attribute_transition_collection_marshalling_test.rb +0 -64
- data/test/unit/transition_collection/attribute_transition_collection_with_action_error_test.rb +0 -44
- data/test/unit/transition_collection/attribute_transition_collection_with_action_failed_test.rb +0 -44
- data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_after_callback_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_around_after_yield_callback_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_after_yield_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_around_callback_before_yield_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_error_test.rb +0 -32
- data/test/unit/transition_collection/attribute_transition_collection_with_before_callback_halt_test.rb +0 -33
- data/test/unit/transition_collection/attribute_transition_collection_with_callbacks_test.rb +0 -68
- data/test/unit/transition_collection/attribute_transition_collection_with_event_transitions_test.rb +0 -41
- data/test/unit/transition_collection/attribute_transition_collection_with_events_test.rb +0 -44
- data/test/unit/transition_collection/attribute_transition_collection_with_skipped_after_callbacks_test.rb +0 -42
- data/test/unit/transition_collection/transition_collection_by_default_test.rb +0 -23
- data/test/unit/transition_collection/transition_collection_empty_with_block_test.rb +0 -23
- data/test/unit/transition_collection/transition_collection_empty_without_block_test.rb +0 -12
- data/test/unit/transition_collection/transition_collection_invalid_test.rb +0 -21
- data/test/unit/transition_collection/transition_collection_partial_invalid_test.rb +0 -69
- data/test/unit/transition_collection/transition_collection_test.rb +0 -26
- data/test/unit/transition_collection/transition_collection_valid_test.rb +0 -57
- data/test/unit/transition_collection/transition_collection_with_action_error_test.rb +0 -66
- data/test/unit/transition_collection/transition_collection_with_action_failed_test.rb +0 -60
- data/test/unit/transition_collection/transition_collection_with_action_hook_and_block_test.rb +0 -17
- data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_action_test.rb +0 -17
- data/test/unit/transition_collection/transition_collection_with_action_hook_and_skipped_after_callbacks_test.rb +0 -37
- data/test/unit/transition_collection/transition_collection_with_action_hook_base_test.rb +0 -34
- data/test/unit/transition_collection/transition_collection_with_action_hook_error_test.rb +0 -29
- data/test/unit/transition_collection/transition_collection_with_action_hook_invalid_test.rb +0 -17
- data/test/unit/transition_collection/transition_collection_with_action_hook_multiple_test.rb +0 -79
- data/test/unit/transition_collection/transition_collection_with_action_hook_test.rb +0 -45
- data/test/unit/transition_collection/transition_collection_with_action_hook_with_different_actions_test.rb +0 -48
- data/test/unit/transition_collection/transition_collection_with_action_hook_with_nil_action_test.rb +0 -42
- data/test/unit/transition_collection/transition_collection_with_after_callback_halt_test.rb +0 -47
- data/test/unit/transition_collection/transition_collection_with_before_callback_halt_test.rb +0 -51
- data/test/unit/transition_collection/transition_collection_with_block_test.rb +0 -46
- data/test/unit/transition_collection/transition_collection_with_callbacks_test.rb +0 -135
- data/test/unit/transition_collection/transition_collection_with_different_actions_test.rb +0 -189
- data/test/unit/transition_collection/transition_collection_with_duplicate_actions_test.rb +0 -48
- data/test/unit/transition_collection/transition_collection_with_empty_actions_test.rb +0 -41
- data/test/unit/transition_collection/transition_collection_with_mixed_actions_test.rb +0 -41
- data/test/unit/transition_collection/transition_collection_with_skipped_actions_and_block_test.rb +0 -34
- data/test/unit/transition_collection/transition_collection_with_skipped_actions_test.rb +0 -69
- data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_and_around_callbacks_test.rb +0 -53
- data/test/unit/transition_collection/transition_collection_with_skipped_after_callbacks_test.rb +0 -34
- data/test/unit/transition_collection/transition_collection_with_transactions_test.rb +0 -65
- 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
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
#
|
76
|
-
args
|
90
|
+
# insert the block to the end of the args
|
91
|
+
args << block
|
77
92
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
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
|
data/lib/state_machines/event.rb
CHANGED
@@ -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 =
|
34
|
-
|
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 =
|
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)
|
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(&
|
68
|
-
instance_eval(&
|
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
|
-
|
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(:
|
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
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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, *
|
168
|
+
def fire(object, *event_args)
|
152
169
|
machine.reset(object)
|
153
170
|
|
154
|
-
if transition = transition_for(object)
|
155
|
-
transition.perform(*
|
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)
|
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
|
-
|
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.
|
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
|