state_machines 0.5.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +99 -16
- data/lib/state_machines/branch.rb +88 -82
- data/lib/state_machines/callback.rb +24 -21
- data/lib/state_machines/core.rb +3 -2
- 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 +2 -0
- data/lib/state_machines/eval_helpers.rb +51 -22
- data/lib/state_machines/event.rb +53 -35
- data/lib/state_machines/event_collection.rb +28 -25
- data/lib/state_machines/extensions.rb +3 -1
- data/lib/state_machines/helper_module.rb +3 -1
- data/lib/state_machines/integrations/base.rb +2 -0
- data/lib/state_machines/integrations.rb +10 -8
- data/lib/state_machines/machine/class_methods.rb +79 -0
- data/lib/state_machines/machine.rb +386 -429
- data/lib/state_machines/machine_collection.rb +16 -11
- data/lib/state_machines/macro_methods.rb +102 -100
- data/lib/state_machines/matcher.rb +26 -23
- data/lib/state_machines/matcher_helpers.rb +13 -11
- data/lib/state_machines/node_collection.rb +20 -14
- data/lib/state_machines/options_validator.rb +72 -0
- data/lib/state_machines/path.rb +61 -56
- data/lib/state_machines/path_collection.rb +40 -36
- data/lib/state_machines/state.rb +72 -43
- data/lib/state_machines/state_collection.rb +22 -19
- data/lib/state_machines/state_context.rb +38 -36
- data/lib/state_machines/stdio_renderer.rb +74 -0
- data/lib/state_machines/test_helper.rb +305 -0
- data/lib/state_machines/transition.rb +182 -178
- data/lib/state_machines/transition_collection.rb +175 -169
- data/lib/state_machines/version.rb +3 -1
- data/lib/state_machines.rb +4 -1
- metadata +12 -440
- 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,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module StateMachines
|
2
4
|
# Provides a general strategy pattern for determining whether a match is found
|
3
5
|
# for a value. The algorithm that actually determines the match depends on
|
@@ -5,114 +7,115 @@ module StateMachines
|
|
5
7
|
class Matcher
|
6
8
|
# The list of values against which queries are matched
|
7
9
|
attr_reader :values
|
8
|
-
|
10
|
+
|
9
11
|
# Creates a new matcher for querying against the given set of values
|
10
12
|
def initialize(values = [])
|
11
13
|
@values = values.is_a?(Array) ? values : [values]
|
12
14
|
end
|
13
|
-
|
15
|
+
|
14
16
|
# Generates a subset of values that exists in both the set of values being
|
15
17
|
# filtered and the values configured for the matcher
|
16
18
|
def filter(values)
|
17
19
|
self.values & values
|
18
20
|
end
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
# Matches any given value. Since there is no configuration for this type of
|
22
24
|
# matcher, it must be used as a singleton.
|
23
25
|
class AllMatcher < Matcher
|
24
26
|
include Singleton
|
25
|
-
|
27
|
+
|
26
28
|
# Generates a blacklist matcher based on the given set of values
|
27
|
-
#
|
29
|
+
#
|
28
30
|
# == Examples
|
29
|
-
#
|
31
|
+
#
|
30
32
|
# matcher = StateMachines::AllMatcher.instance - [:parked, :idling]
|
31
33
|
# matcher.matches?(:parked) # => false
|
32
34
|
# matcher.matches?(:first_gear) # => true
|
33
35
|
def -(blacklist)
|
34
36
|
BlacklistMatcher.new(blacklist)
|
35
37
|
end
|
36
|
-
|
38
|
+
alias_method :except, :-
|
39
|
+
|
37
40
|
# Always returns true
|
38
41
|
def matches?(value, context = {})
|
39
42
|
true
|
40
43
|
end
|
41
|
-
|
44
|
+
|
42
45
|
# Always returns the given set of values
|
43
46
|
def filter(values)
|
44
47
|
values
|
45
48
|
end
|
46
|
-
|
49
|
+
|
47
50
|
# A human-readable description of this matcher. Always "all".
|
48
51
|
def description
|
49
52
|
'all'
|
50
53
|
end
|
51
54
|
end
|
52
|
-
|
55
|
+
|
53
56
|
# Matches a specific set of values
|
54
57
|
class WhitelistMatcher < Matcher
|
55
58
|
# Checks whether the given value exists within the whitelist configured
|
56
59
|
# for this matcher.
|
57
|
-
#
|
60
|
+
#
|
58
61
|
# == Examples
|
59
|
-
#
|
62
|
+
#
|
60
63
|
# matcher = StateMachines::WhitelistMatcher.new([:parked, :idling])
|
61
64
|
# matcher.matches?(:parked) # => true
|
62
65
|
# matcher.matches?(:first_gear) # => false
|
63
66
|
def matches?(value, context = {})
|
64
67
|
values.include?(value)
|
65
68
|
end
|
66
|
-
|
69
|
+
|
67
70
|
# A human-readable description of this matcher
|
68
71
|
def description
|
69
72
|
values.length == 1 ? values.first.inspect : values.inspect
|
70
73
|
end
|
71
74
|
end
|
72
|
-
|
75
|
+
|
73
76
|
# Matches everything but a specific set of values
|
74
77
|
class BlacklistMatcher < Matcher
|
75
78
|
# Checks whether the given value exists outside the blacklist configured
|
76
79
|
# for this matcher.
|
77
|
-
#
|
80
|
+
#
|
78
81
|
# == Examples
|
79
|
-
#
|
82
|
+
#
|
80
83
|
# matcher = StateMachines::BlacklistMatcher.new([:parked, :idling])
|
81
84
|
# matcher.matches?(:parked) # => false
|
82
85
|
# matcher.matches?(:first_gear) # => true
|
83
86
|
def matches?(value, context = {})
|
84
87
|
!values.include?(value)
|
85
88
|
end
|
86
|
-
|
89
|
+
|
87
90
|
# Finds all values that are *not* within the blacklist configured for this
|
88
91
|
# matcher
|
89
92
|
def filter(values)
|
90
93
|
values - self.values
|
91
94
|
end
|
92
|
-
|
95
|
+
|
93
96
|
# A human-readable description of this matcher
|
94
97
|
def description
|
95
98
|
"all - #{values.length == 1 ? values.first.inspect : values.inspect}"
|
96
99
|
end
|
97
100
|
end
|
98
|
-
|
101
|
+
|
99
102
|
# Matches a loopback of two values within a context. Since there is no
|
100
103
|
# configuration for this type of matcher, it must be used as a singleton.
|
101
104
|
class LoopbackMatcher < Matcher
|
102
105
|
include Singleton
|
103
|
-
|
106
|
+
|
104
107
|
# Checks whether the given value matches what the value originally was.
|
105
108
|
# This value should be defined in the context.
|
106
|
-
#
|
109
|
+
#
|
107
110
|
# == Examples
|
108
|
-
#
|
111
|
+
#
|
109
112
|
# matcher = StateMachines::LoopbackMatcher.instance
|
110
113
|
# matcher.matches?(:parked, :from => :parked) # => true
|
111
114
|
# matcher.matches?(:parked, :from => :idling) # => false
|
112
115
|
def matches?(value, context)
|
113
116
|
context[:from] == value
|
114
117
|
end
|
115
|
-
|
118
|
+
|
116
119
|
# A human-readable description of this matcher. Always "same".
|
117
120
|
def description
|
118
121
|
'same'
|
@@ -1,25 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module StateMachines
|
2
4
|
# Provides a set of helper methods for generating matchers
|
3
5
|
module MatcherHelpers
|
4
6
|
# Represents a state that matches all known states in a machine.
|
5
|
-
#
|
7
|
+
#
|
6
8
|
# == Examples
|
7
|
-
#
|
9
|
+
#
|
8
10
|
# class Vehicle
|
9
11
|
# state_machine do
|
10
12
|
# before_transition any => :parked, :do => lambda {...}
|
11
13
|
# before_transition all - :parked => all - :idling, :do => lambda {}
|
12
|
-
#
|
14
|
+
#
|
13
15
|
# event :park
|
14
16
|
# transition all => :parked
|
15
17
|
# end
|
16
|
-
#
|
18
|
+
#
|
17
19
|
# event :crash
|
18
20
|
# transition all - :parked => :stalled
|
19
21
|
# end
|
20
22
|
# end
|
21
23
|
# end
|
22
|
-
#
|
24
|
+
#
|
23
25
|
# In the above example, +all+ will match the following states since they
|
24
26
|
# are known:
|
25
27
|
# * +parked+
|
@@ -29,12 +31,12 @@ module StateMachines
|
|
29
31
|
AllMatcher.instance
|
30
32
|
end
|
31
33
|
alias_method :any, :all
|
32
|
-
|
34
|
+
|
33
35
|
# Represents a state that matches the original +from+ state. This is useful
|
34
36
|
# for defining transitions which are loopbacks.
|
35
|
-
#
|
37
|
+
#
|
36
38
|
# == Examples
|
37
|
-
#
|
39
|
+
#
|
38
40
|
# class Vehicle
|
39
41
|
# state_machine do
|
40
42
|
# event :ignite
|
@@ -42,11 +44,11 @@ module StateMachines
|
|
42
44
|
# end
|
43
45
|
# end
|
44
46
|
# end
|
45
|
-
#
|
47
|
+
#
|
46
48
|
# In the above example, +same+ will match whichever the from state is. In
|
47
49
|
# the case of the +ignite+ event, it is essential the same as the following:
|
48
|
-
#
|
49
|
-
# transition :
|
50
|
+
#
|
51
|
+
# transition :idling => :idling, :first_gear => :first_gear
|
50
52
|
def same
|
51
53
|
LoopbackMatcher.instance
|
52
54
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'options_validator'
|
4
|
+
|
1
5
|
module StateMachines
|
2
6
|
# Represents a collection of nodes in a state machine, be it events or states.
|
3
7
|
# Nodes will not differentiate between the String and Symbol versions of the
|
@@ -16,7 +20,7 @@ module StateMachines
|
|
16
20
|
# hashed indices for in order to perform quick lookups. Default is to
|
17
21
|
# index by the :name attribute
|
18
22
|
def initialize(machine, options = {})
|
19
|
-
|
23
|
+
StateMachines::OptionsValidator.assert_valid_keys!(options, :index)
|
20
24
|
options = { index: :name }.merge(options)
|
21
25
|
|
22
26
|
@machine = machine
|
@@ -162,37 +166,39 @@ module StateMachines
|
|
162
166
|
self[key, index_name] || fail(IndexError, "#{key.inspect} is an invalid #{index_name}")
|
163
167
|
end
|
164
168
|
|
165
|
-
|
166
|
-
|
167
|
-
|
169
|
+
protected
|
170
|
+
|
171
|
+
# Gets the given index. If the index does not exist, then an ArgumentError
|
172
|
+
# is raised.
|
168
173
|
def index(name)
|
169
174
|
fail ArgumentError, 'No indices configured' unless @indices.any?
|
175
|
+
|
170
176
|
@indices[name] || fail(ArgumentError, "Invalid index: #{name.inspect}")
|
171
177
|
end
|
172
178
|
|
173
|
-
|
179
|
+
# Gets the value for the given attribute on the node
|
174
180
|
def value(node, attribute)
|
175
181
|
node.send(attribute)
|
176
182
|
end
|
177
183
|
|
178
|
-
|
179
|
-
|
184
|
+
# Adds the given key / node combination to an index, including the string
|
185
|
+
# and symbol versions of the index
|
180
186
|
def add_to_index(name, key, node)
|
181
187
|
index(name)[key] = node
|
182
188
|
index(:"#{name}_to_s")[key.to_s] = node
|
183
189
|
index(:"#{name}_to_sym")[:"#{key}"] = node if to_sym?(key)
|
184
190
|
end
|
185
191
|
|
186
|
-
|
187
|
-
|
192
|
+
# Removes the given key from an index, including the string and symbol
|
193
|
+
# versions of the index
|
188
194
|
def remove_from_index(name, key)
|
189
195
|
index(name).delete(key)
|
190
196
|
index(:"#{name}_to_s").delete(key.to_s)
|
191
197
|
index(:"#{name}_to_sym").delete(:"#{key}") if to_sym?(key)
|
192
198
|
end
|
193
199
|
|
194
|
-
|
195
|
-
|
200
|
+
# Updates the node for the given index, including the string and symbol
|
201
|
+
# versions of the index
|
196
202
|
def update_index(name, node)
|
197
203
|
index = self.index(name)
|
198
204
|
old_key = index.key(node)
|
@@ -205,13 +211,13 @@ module StateMachines
|
|
205
211
|
end
|
206
212
|
end
|
207
213
|
|
208
|
-
|
214
|
+
# Determines whether the given value can be converted to a symbol
|
209
215
|
def to_sym?(value)
|
210
216
|
"#{value}" != ''
|
211
217
|
end
|
212
218
|
|
213
|
-
|
214
|
-
|
219
|
+
# Evaluates the given context for a particular node. This will only
|
220
|
+
# evaluate the context if the node matches.
|
215
221
|
def eval_context(context, node)
|
216
222
|
node.context(&context[:block]) if context[:nodes].matches?(node.name)
|
217
223
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StateMachines
|
4
|
+
# Define the module if it doesn't exist yet
|
5
|
+
# Module for validating options without monkey-patching Hash
|
6
|
+
# Provides the same functionality as the Hash monkey patch but in a cleaner way
|
7
|
+
module OptionsValidator
|
8
|
+
class << self
|
9
|
+
# Validates that all keys in the options hash are in the list of valid keys
|
10
|
+
#
|
11
|
+
# @param options [Hash] The options hash to validate
|
12
|
+
# @param valid_keys [Array<Symbol>] List of valid key names
|
13
|
+
# @param caller_info [String] Information about the calling method for better error messages
|
14
|
+
# @raise [ArgumentError] If any invalid keys are found
|
15
|
+
def assert_valid_keys!(options, *valid_keys, caller_info: nil)
|
16
|
+
return if options.empty?
|
17
|
+
|
18
|
+
valid_keys.flatten!
|
19
|
+
invalid_keys = options.keys - valid_keys
|
20
|
+
|
21
|
+
return if invalid_keys.empty?
|
22
|
+
|
23
|
+
caller_context = caller_info ? " in #{caller_info}" : ''
|
24
|
+
raise ArgumentError, "Unknown key#{'s' if invalid_keys.length > 1}: #{invalid_keys.map(&:inspect).join(', ')}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}#{caller_context}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Validates that at most one of the exclusive keys is present in the options hash
|
28
|
+
#
|
29
|
+
# @param options [Hash] The options hash to validate
|
30
|
+
# @param exclusive_keys [Array<Symbol>] List of mutually exclusive keys
|
31
|
+
# @param caller_info [String] Information about the calling method for better error messages
|
32
|
+
# @raise [ArgumentError] If more than one exclusive key is found
|
33
|
+
def assert_exclusive_keys!(options, *exclusive_keys, caller_info: nil)
|
34
|
+
return if options.empty?
|
35
|
+
|
36
|
+
conflicting_keys = exclusive_keys & options.keys
|
37
|
+
return if conflicting_keys.length <= 1
|
38
|
+
|
39
|
+
caller_context = caller_info ? " in #{caller_info}" : ''
|
40
|
+
raise ArgumentError, "Conflicting keys: #{conflicting_keys.join(', ')}#{caller_context}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Validates options using a more convenient interface that works with both
|
44
|
+
# hash-style and kwargs-style method definitions
|
45
|
+
#
|
46
|
+
# @param valid_keys [Array<Symbol>] List of valid key names
|
47
|
+
# @param exclusive_key_groups [Array<Array<Symbol>>] Groups of mutually exclusive keys
|
48
|
+
# @param caller_info [String] Information about the calling method
|
49
|
+
# @return [Proc] A validation proc that can be called with options
|
50
|
+
def validator(valid_keys: [], exclusive_key_groups: [], caller_info: nil)
|
51
|
+
proc do |options|
|
52
|
+
assert_valid_keys!(options, *valid_keys, caller_info: caller_info) unless valid_keys.empty?
|
53
|
+
|
54
|
+
exclusive_key_groups.each do |group|
|
55
|
+
assert_exclusive_keys!(options, *group, caller_info: caller_info)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Helper method for backwards compatibility - allows gradual migration
|
61
|
+
# from Hash monkey patch to this module
|
62
|
+
#
|
63
|
+
# @param options [Hash] The options to validate
|
64
|
+
# @param valid_keys [Array<Symbol>] Valid keys
|
65
|
+
# @return [Hash] The same options hash (for chaining)
|
66
|
+
def validate_and_return(options, *valid_keys)
|
67
|
+
assert_valid_keys!(options, *valid_keys)
|
68
|
+
options
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/state_machines/path.rb
CHANGED
@@ -1,82 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'options_validator'
|
4
|
+
|
1
5
|
module StateMachines
|
2
6
|
# A path represents a sequence of transitions that can be run for a particular
|
3
7
|
# object. Paths can walk to new transitions, revealing all of the possible
|
4
8
|
# branches that can be encountered in the object's state machine.
|
5
9
|
class Path < Array
|
6
10
|
|
7
|
-
|
11
|
+
|
8
12
|
# The object whose state machine is being walked
|
9
13
|
attr_reader :object
|
10
|
-
|
14
|
+
|
11
15
|
# The state machine this path is walking
|
12
16
|
attr_reader :machine
|
13
|
-
|
17
|
+
|
14
18
|
# Creates a new transition path for the given object. Initially this is an
|
15
19
|
# empty path. In order to start walking the path, it must be populated with
|
16
20
|
# an initial transition.
|
17
|
-
#
|
21
|
+
#
|
18
22
|
# Configuration options:
|
19
23
|
# * <tt>:target</tt> - The target state to end the path on
|
20
24
|
# * <tt>:guard</tt> - Whether to guard transitions with the if/unless
|
21
25
|
# conditionals defined for each one
|
22
26
|
def initialize(object, machine, options = {})
|
23
|
-
|
24
|
-
|
27
|
+
StateMachines::OptionsValidator.assert_valid_keys!(options, :target, :guard)
|
28
|
+
|
25
29
|
@object = object
|
26
30
|
@machine = machine
|
27
31
|
@target = options[:target]
|
28
32
|
@guard = options[:guard]
|
29
33
|
end
|
30
|
-
|
34
|
+
|
31
35
|
def initialize_copy(orig) #:nodoc:
|
32
36
|
super
|
33
37
|
@transitions = nil
|
34
38
|
end
|
35
|
-
|
39
|
+
|
36
40
|
# The initial state name for this path
|
37
41
|
def from_name
|
38
|
-
first
|
42
|
+
first&.from_name
|
39
43
|
end
|
40
|
-
|
44
|
+
|
41
45
|
# Lists all of the from states that can be reached through this path.
|
42
|
-
#
|
46
|
+
#
|
43
47
|
# For example,
|
44
|
-
#
|
48
|
+
#
|
45
49
|
# path.to_states # => [:parked, :idling, :first_gear, ...]
|
46
50
|
def from_states
|
47
|
-
map {|transition| transition.from_name}.uniq
|
51
|
+
map { |transition| transition.from_name }.uniq
|
48
52
|
end
|
49
|
-
|
53
|
+
|
50
54
|
# The end state name for this path. If a target state was specified for
|
51
55
|
# the path, then that will be returned if the path is complete.
|
52
56
|
def to_name
|
53
|
-
last
|
57
|
+
last&.to_name
|
54
58
|
end
|
55
|
-
|
59
|
+
|
56
60
|
# Lists all of the to states that can be reached through this path.
|
57
|
-
#
|
61
|
+
#
|
58
62
|
# For example,
|
59
|
-
#
|
63
|
+
#
|
60
64
|
# path.to_states # => [:parked, :idling, :first_gear, ...]
|
61
65
|
def to_states
|
62
|
-
map {|transition| transition.to_name}.uniq
|
66
|
+
map { |transition| transition.to_name }.uniq
|
63
67
|
end
|
64
|
-
|
68
|
+
|
65
69
|
# Lists all of the events that can be fired through this path.
|
66
|
-
#
|
70
|
+
#
|
67
71
|
# For example,
|
68
|
-
#
|
72
|
+
#
|
69
73
|
# path.events # => [:park, :ignite, :shift_up, ...]
|
70
74
|
def events
|
71
|
-
map {|transition| transition.event}.uniq
|
75
|
+
map { |transition| transition.event }.uniq
|
72
76
|
end
|
73
|
-
|
77
|
+
|
74
78
|
# Walks down the next transitions at the end of this path. This will only
|
75
79
|
# walk down paths that are considered valid.
|
76
80
|
def walk
|
77
|
-
transitions.each {|transition| yield dup.push(transition)}
|
81
|
+
transitions.each { |transition| yield dup.push(transition) }
|
78
82
|
end
|
79
|
-
|
83
|
+
|
80
84
|
# Determines whether or not this path has completed. A path is considered
|
81
85
|
# complete when one of the following conditions is met:
|
82
86
|
# * The last transition in the path ends on the target state
|
@@ -85,36 +89,37 @@ module StateMachines
|
|
85
89
|
def complete?
|
86
90
|
!empty? && (@target ? to_name == @target : transitions.empty?)
|
87
91
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
transitions.include?(transition)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Determines whether it's possible to walk to the given transition from
|
107
|
-
# the current path. A transition can be walked to if:
|
108
|
-
# * It has not been recently walked and
|
109
|
-
# * If a target is specified, it has not been walked to twice yet
|
110
|
-
def can_walk_to?(transition)
|
111
|
-
!recently_walked?(transition) && (!@target || times_walked_to(@target) < 2)
|
112
|
-
end
|
113
|
-
|
114
|
-
# Get the next set of transitions that can be walked to starting from the
|
115
|
-
# end of this path
|
116
|
-
def transitions
|
117
|
-
@transitions ||= empty? ? [] : machine.events.transitions_for(object, :from => to_name, :guard => @guard).select {|transition| can_walk_to?(transition)}
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Calculates the number of times the given state has been walked to
|
96
|
+
def times_walked_to(state)
|
97
|
+
select { |transition| transition.to_name == state }.length
|
98
|
+
end
|
99
|
+
|
100
|
+
# Determines whether the given transition has been recently walked down in
|
101
|
+
# this path. If a target is configured for this path, then this will only
|
102
|
+
# look at transitions walked down since the target was last reached.
|
103
|
+
def recently_walked?(transition)
|
104
|
+
transitions = self
|
105
|
+
if @target && @target != to_name && (target_transition = detect { |t| t.to_name == @target })
|
106
|
+
transitions = transitions[index(target_transition) + 1..-1]
|
118
107
|
end
|
108
|
+
transitions.include?(transition)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Determines whether it's possible to walk to the given transition from
|
112
|
+
# the current path. A transition can be walked to if:
|
113
|
+
# * It has not been recently walked and
|
114
|
+
# * If a target is specified, it has not been walked to twice yet
|
115
|
+
def can_walk_to?(transition)
|
116
|
+
!recently_walked?(transition) && (!@target || times_walked_to(@target) < 2)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Get the next set of transitions that can be walked to starting from the
|
120
|
+
# end of this path
|
121
|
+
def transitions
|
122
|
+
@transitions ||= empty? ? [] : machine.events.transitions_for(object, from: to_name, guard: @guard).select { |transition| can_walk_to?(transition) }
|
123
|
+
end
|
119
124
|
end
|
120
125
|
end
|
@@ -1,23 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'options_validator'
|
4
|
+
|
1
5
|
module StateMachines
|
2
6
|
# Represents a collection of paths that are generated based on a set of
|
3
7
|
# requirements regarding what states to start and end on
|
4
8
|
class PathCollection < Array
|
5
9
|
|
6
|
-
|
7
10
|
# The object whose state machine is being walked
|
8
11
|
attr_reader :object
|
9
|
-
|
12
|
+
|
10
13
|
# The state machine these path are walking
|
11
14
|
attr_reader :machine
|
12
|
-
|
15
|
+
|
13
16
|
# The initial state to start each path from
|
14
17
|
attr_reader :from_name
|
15
|
-
|
18
|
+
|
16
19
|
# The target state for each path
|
17
20
|
attr_reader :to_name
|
18
|
-
|
21
|
+
|
19
22
|
# Creates a new collection of paths with the given requirements.
|
20
|
-
#
|
23
|
+
#
|
21
24
|
# Configuration options:
|
22
25
|
# * <tt>:from</tt> - The initial state to start from
|
23
26
|
# * <tt>:to</tt> - The target end state
|
@@ -25,64 +28,65 @@ module StateMachines
|
|
25
28
|
# * <tt>:guard</tt> - Whether to guard transitions with the if/unless
|
26
29
|
# conditionals defined for each one
|
27
30
|
def initialize(object, machine, options = {})
|
28
|
-
options = {:
|
29
|
-
|
30
|
-
|
31
|
+
options = {deep: false, from: machine.states.match!(object).name}.merge(options)
|
32
|
+
StateMachines::OptionsValidator.assert_valid_keys!(options, :from, :to, :deep, :guard)
|
33
|
+
|
31
34
|
@object = object
|
32
35
|
@machine = machine
|
33
36
|
@from_name = machine.states.fetch(options[:from]).name
|
34
37
|
@to_name = options[:to] && machine.states.fetch(options[:to]).name
|
35
38
|
@guard = options[:guard]
|
36
39
|
@deep = options[:deep]
|
37
|
-
|
38
|
-
initial_paths.each {|path| walk(path)}
|
40
|
+
|
41
|
+
initial_paths.each { |path| walk(path) }
|
39
42
|
end
|
40
|
-
|
43
|
+
|
41
44
|
# Lists all of the states that can be transitioned from through the paths in
|
42
45
|
# this collection.
|
43
|
-
#
|
46
|
+
#
|
44
47
|
# For example,
|
45
|
-
#
|
48
|
+
#
|
46
49
|
# paths.from_states # => [:parked, :idling, :first_gear, ...]
|
47
50
|
def from_states
|
48
51
|
flat_map(&:from_states).uniq
|
49
52
|
end
|
50
|
-
|
53
|
+
|
51
54
|
# Lists all of the states that can be transitioned to through the paths in
|
52
55
|
# this collection.
|
53
|
-
#
|
56
|
+
#
|
54
57
|
# For example,
|
55
|
-
#
|
58
|
+
#
|
56
59
|
# paths.to_states # => [:idling, :first_gear, :second_gear, ...]
|
57
60
|
def to_states
|
58
61
|
flat_map(&:to_states).uniq
|
59
62
|
end
|
60
|
-
|
63
|
+
|
61
64
|
# Lists all of the events that can be fired through the paths in this
|
62
65
|
# collection.
|
63
|
-
#
|
66
|
+
#
|
64
67
|
# For example,
|
65
|
-
#
|
68
|
+
#
|
66
69
|
# paths.events # => [:park, :ignite, :shift_up, ...]
|
67
70
|
def events
|
68
71
|
flat_map(&:events).uniq
|
69
72
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
# Walks down the given path. Each new path that matches the configured
|
82
|
-
# requirements will be added to this collection.
|
83
|
-
def walk(path)
|
84
|
-
self << path if path.complete?
|
85
|
-
path.walk {|next_path| walk(next_path)} unless to_name && path.complete? && !@deep
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Gets the initial set of paths to walk
|
77
|
+
def initial_paths
|
78
|
+
machine.events.transitions_for(object, from: from_name, guard: @guard).map do |transition|
|
79
|
+
path = Path.new(object, machine, target: to_name, guard: @guard)
|
80
|
+
path << transition
|
81
|
+
path
|
86
82
|
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Walks down the given path. Each new path that matches the configured
|
86
|
+
# requirements will be added to this collection.
|
87
|
+
def walk(path)
|
88
|
+
self << path if path.complete?
|
89
|
+
path.walk { |next_path| walk(next_path) } unless to_name && path.complete? && !@deep
|
90
|
+
end
|
87
91
|
end
|
88
92
|
end
|