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,23 +1,25 @@
|
|
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
|
-
|
6
|
-
|
7
9
|
# The object whose state machine is being walked
|
8
10
|
attr_reader :object
|
9
|
-
|
11
|
+
|
10
12
|
# The state machine these path are walking
|
11
13
|
attr_reader :machine
|
12
|
-
|
14
|
+
|
13
15
|
# The initial state to start each path from
|
14
16
|
attr_reader :from_name
|
15
|
-
|
17
|
+
|
16
18
|
# The target state for each path
|
17
19
|
attr_reader :to_name
|
18
|
-
|
20
|
+
|
19
21
|
# Creates a new collection of paths with the given requirements.
|
20
|
-
#
|
22
|
+
#
|
21
23
|
# Configuration options:
|
22
24
|
# * <tt>:from</tt> - The initial state to start from
|
23
25
|
# * <tt>:to</tt> - The target end state
|
@@ -25,64 +27,65 @@ module StateMachines
|
|
25
27
|
# * <tt>:guard</tt> - Whether to guard transitions with the if/unless
|
26
28
|
# conditionals defined for each one
|
27
29
|
def initialize(object, machine, options = {})
|
28
|
-
options = {:
|
29
|
-
|
30
|
-
|
30
|
+
options = { deep: false, from: machine.states.match!(object).name }.merge(options)
|
31
|
+
StateMachines::OptionsValidator.assert_valid_keys!(options, :from, :to, :deep, :guard)
|
32
|
+
|
31
33
|
@object = object
|
32
34
|
@machine = machine
|
33
35
|
@from_name = machine.states.fetch(options[:from]).name
|
34
36
|
@to_name = options[:to] && machine.states.fetch(options[:to]).name
|
35
37
|
@guard = options[:guard]
|
36
38
|
@deep = options[:deep]
|
37
|
-
|
38
|
-
initial_paths.each {|path| walk(path)}
|
39
|
+
|
40
|
+
initial_paths.each { |path| walk(path) }
|
39
41
|
end
|
40
|
-
|
42
|
+
|
41
43
|
# Lists all of the states that can be transitioned from through the paths in
|
42
44
|
# this collection.
|
43
|
-
#
|
45
|
+
#
|
44
46
|
# For example,
|
45
|
-
#
|
47
|
+
#
|
46
48
|
# paths.from_states # => [:parked, :idling, :first_gear, ...]
|
47
49
|
def from_states
|
48
50
|
flat_map(&:from_states).uniq
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
# Lists all of the states that can be transitioned to through the paths in
|
52
54
|
# this collection.
|
53
|
-
#
|
55
|
+
#
|
54
56
|
# For example,
|
55
|
-
#
|
57
|
+
#
|
56
58
|
# paths.to_states # => [:idling, :first_gear, :second_gear, ...]
|
57
59
|
def to_states
|
58
60
|
flat_map(&:to_states).uniq
|
59
61
|
end
|
60
|
-
|
62
|
+
|
61
63
|
# Lists all of the events that can be fired through the paths in this
|
62
64
|
# collection.
|
63
|
-
#
|
65
|
+
#
|
64
66
|
# For example,
|
65
|
-
#
|
67
|
+
#
|
66
68
|
# paths.events # => [:park, :ignite, :shift_up, ...]
|
67
69
|
def events
|
68
70
|
flat_map(&:events).uniq
|
69
71
|
end
|
70
|
-
|
72
|
+
|
71
73
|
private
|
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
|
74
|
+
|
75
|
+
# Gets the initial set of paths to walk
|
76
|
+
def initial_paths
|
77
|
+
machine.events.transitions_for(object, from: from_name, guard: @guard).map do |transition|
|
78
|
+
path = Path.new(object, machine, target: to_name, guard: @guard)
|
79
|
+
path << transition
|
80
|
+
path
|
86
81
|
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Walks down the given path. Each new path that matches the configured
|
85
|
+
# requirements will be added to this collection.
|
86
|
+
def walk(path)
|
87
|
+
self << path if path.complete?
|
88
|
+
path.walk { |next_path| walk(next_path) } unless to_name && path.complete? && !@deep
|
89
|
+
end
|
87
90
|
end
|
88
91
|
end
|
data/lib/state_machines/state.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'options_validator'
|
4
|
+
|
1
5
|
module StateMachines
|
2
6
|
# A state defines a value that an attribute can be in after being transitioned
|
3
7
|
# 0 or more times. States can represent a value of any type in Ruby, though
|
@@ -8,7 +12,6 @@ module StateMachines
|
|
8
12
|
# StateMachines::Machine#state for more information about how state-driven
|
9
13
|
# behavior can be utilized.
|
10
14
|
class State
|
11
|
-
|
12
15
|
# The state machine for which this state is defined
|
13
16
|
attr_reader :machine
|
14
17
|
|
@@ -31,7 +34,7 @@ module StateMachines
|
|
31
34
|
|
32
35
|
# Whether or not this state is the initial state to use for new objects
|
33
36
|
attr_accessor :initial
|
34
|
-
|
37
|
+
alias initial? initial
|
35
38
|
|
36
39
|
# A custom lambda block for determining whether a given value matches this
|
37
40
|
# state
|
@@ -50,38 +53,59 @@ module StateMachines
|
|
50
53
|
# (e.g. :value => lambda {Time.now}, :if => lambda {|state| !state.nil?}).
|
51
54
|
# By default, the configured value is matched.
|
52
55
|
# * <tt>:human_name</tt> - The human-readable version of this state's name
|
53
|
-
def initialize(machine, name, options =
|
54
|
-
|
56
|
+
def initialize(machine, name, options = nil, initial: false, value: :__not_provided__, cache: nil, if: nil, human_name: nil, **extra_options) # :nodoc:
|
57
|
+
# Handle both old hash style and new kwargs style for backward compatibility
|
58
|
+
case options
|
59
|
+
in Hash
|
60
|
+
# Old style: initialize(machine, name, {initial: true, value: 'foo'})
|
61
|
+
StateMachines::OptionsValidator.assert_valid_keys!(options, :initial, :value, :cache, :if, :human_name)
|
62
|
+
initial = options.fetch(:initial, false)
|
63
|
+
value = options.include?(:value) ? options[:value] : :__not_provided__
|
64
|
+
cache = options[:cache]
|
65
|
+
if_condition = options[:if]
|
66
|
+
human_name = options[:human_name]
|
67
|
+
in nil
|
68
|
+
# New style: initialize(machine, name, initial: true, value: 'foo')
|
69
|
+
StateMachines::OptionsValidator.assert_valid_keys!(extra_options, :initial, :value, :cache, :if, :human_name) unless extra_options.empty?
|
70
|
+
if_condition = binding.local_variable_get(:if) # 'if' is a keyword, need special handling
|
71
|
+
else
|
72
|
+
# Handle unexpected options
|
73
|
+
raise ArgumentError, "Unexpected positional argument in State initialize: #{options.inspect}"
|
74
|
+
end
|
55
75
|
|
56
76
|
@machine = machine
|
57
77
|
@name = name
|
58
78
|
@qualified_name = name && machine.namespace ? :"#{machine.namespace}_#{name}" : name
|
59
|
-
@human_name =
|
60
|
-
@value =
|
61
|
-
@cache =
|
62
|
-
@matcher =
|
63
|
-
@initial =
|
79
|
+
@human_name = human_name || (@name ? @name.to_s.tr('_', ' ') : 'nil')
|
80
|
+
@value = value == :__not_provided__ ? name&.to_s : value
|
81
|
+
@cache = cache
|
82
|
+
@matcher = if_condition
|
83
|
+
@initial = initial == true
|
64
84
|
@context = StateContext.new(self)
|
65
85
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
86
|
+
return unless name
|
87
|
+
|
88
|
+
conflicting_machines = machine.owner_class.state_machines.select do |_other_name, other_machine|
|
89
|
+
other_machine != machine && other_machine.states[qualified_name, :qualified_name]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Output a warning if another machine has a conflicting qualified name
|
93
|
+
# for a different attribute
|
94
|
+
if (conflict = conflicting_machines.detect do |_other_name, other_machine|
|
95
|
+
other_machine.attribute != machine.attribute
|
96
|
+
end)
|
97
|
+
_name, other_machine = conflict
|
98
|
+
warn "State #{qualified_name.inspect} for #{machine.name.inspect} is already defined in #{other_machine.name.inspect}"
|
99
|
+
elsif conflicting_machines.empty?
|
100
|
+
# Only bother adding predicates when another machine for the same
|
101
|
+
# attribute hasn't already done so
|
102
|
+
add_predicate
|
79
103
|
end
|
80
104
|
end
|
81
105
|
|
82
106
|
# Creates a copy of this state, excluding the context to prevent conflicts
|
83
107
|
# across different machines.
|
84
|
-
def initialize_copy(orig)
|
108
|
+
def initialize_copy(orig) # :nodoc:
|
85
109
|
super
|
86
110
|
@context = StateContext.new(self)
|
87
111
|
end
|
@@ -96,10 +120,10 @@ module StateMachines
|
|
96
120
|
# Any objects in a final state will remain so forever given the current
|
97
121
|
# machine's definition.
|
98
122
|
def final?
|
99
|
-
|
123
|
+
machine.events.none? do |event|
|
100
124
|
event.branches.any? do |branch|
|
101
125
|
branch.state_requirements.any? do |requirement|
|
102
|
-
requirement[:from].matches?(name) && !requirement[:to].matches?(name, :
|
126
|
+
requirement[:from].matches?(name) && !requirement[:to].matches?(name, from: name)
|
103
127
|
end
|
104
128
|
end
|
105
129
|
end
|
@@ -126,7 +150,7 @@ module StateMachines
|
|
126
150
|
# description or just the internal name
|
127
151
|
def description(options = {})
|
128
152
|
label = options[:human_name] ? human_name : name
|
129
|
-
description = label ? label.to_s : label.inspect
|
153
|
+
description = +(label ? label.to_s : label.inspect)
|
130
154
|
description << " (#{@value.is_a?(Proc) ? '*' : @value.inspect})" unless name.to_s == @value.to_s
|
131
155
|
description
|
132
156
|
end
|
@@ -178,27 +202,27 @@ module StateMachines
|
|
178
202
|
#
|
179
203
|
# This can be called multiple times. Each time a new context is created,
|
180
204
|
# a new module will be included in the owner class.
|
181
|
-
def context(&
|
205
|
+
def context(&)
|
182
206
|
# Include the context
|
183
207
|
context = @context
|
184
208
|
machine.owner_class.class_eval { include context }
|
185
209
|
|
186
210
|
# Evaluate the method definitions and track which ones were added
|
187
211
|
old_methods = context_methods
|
188
|
-
context.class_eval(&
|
189
|
-
new_methods = context_methods.to_a.
|
212
|
+
context.class_eval(&)
|
213
|
+
new_methods = context_methods.to_a.reject { |(name, method)| old_methods[name] == method }
|
190
214
|
|
191
215
|
# Alias new methods so that the only execute when the object is in this state
|
192
216
|
new_methods.each do |(method_name, _method)|
|
193
217
|
context_name = context_name_for(method_name)
|
194
|
-
context.class_eval <<-
|
218
|
+
context.class_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
195
219
|
alias_method :"#{context_name}", :#{method_name}
|
196
220
|
def #{method_name}(*args, &block)
|
197
221
|
state = self.class.state_machine(#{machine.name.inspect}).states.fetch(#{name.inspect})
|
198
222
|
options = {:method_missing => lambda {super(*args, &block)}, :method_name => #{method_name.inspect}}
|
199
223
|
state.call(self, :"#{context_name}", *(args + [options]), &block)
|
200
224
|
end
|
201
|
-
|
225
|
+
END_EVAL
|
202
226
|
end
|
203
227
|
|
204
228
|
true
|
@@ -216,31 +240,30 @@ module StateMachines
|
|
216
240
|
#
|
217
241
|
# If the method has never been defined for this state, then a NoMethodError
|
218
242
|
# will be raised.
|
219
|
-
def call(object, method, *args, &
|
243
|
+
def call(object, method, *args, &)
|
220
244
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
221
|
-
options = {:
|
245
|
+
options = { method_name: method }.merge(options)
|
222
246
|
state = machine.states.match!(object)
|
223
247
|
|
224
248
|
if state == self && object.respond_to?(method)
|
225
|
-
object.send(method, *args, &
|
226
|
-
elsif method_missing = options[:method_missing]
|
249
|
+
object.send(method, *args, &)
|
250
|
+
elsif (method_missing = options[:method_missing])
|
227
251
|
# Dispatch to the superclass since the object either isn't in this state
|
228
252
|
# or this state doesn't handle the method
|
229
253
|
begin
|
230
254
|
method_missing.call
|
231
|
-
rescue NoMethodError =>
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
end
|
255
|
+
rescue NoMethodError => e
|
256
|
+
raise unless e.name.to_s == options[:method_name].to_s && e.args == args
|
257
|
+
|
258
|
+
# No valid context for this method
|
259
|
+
raise InvalidContext.new(object,
|
260
|
+
"State #{state.name.inspect} for #{machine.name.inspect} is not a valid context for calling ##{options[:method_name]}")
|
238
261
|
end
|
239
262
|
end
|
240
263
|
end
|
241
264
|
|
242
|
-
def draw(graph, options = {})
|
243
|
-
|
265
|
+
def draw(graph, options = {}, io = $stdout)
|
266
|
+
machine.renderer.draw_state(self, graph, options, io)
|
244
267
|
end
|
245
268
|
|
246
269
|
# Generates a nicely formatted description of this state's contents.
|
@@ -255,6 +278,7 @@ module StateMachines
|
|
255
278
|
end
|
256
279
|
|
257
280
|
private
|
281
|
+
|
258
282
|
# Should the value be cached after it's evaluated for the first time?
|
259
283
|
def cache_value?
|
260
284
|
@cache
|
@@ -263,9 +287,16 @@ module StateMachines
|
|
263
287
|
# Adds a predicate method to the owner class so long as a name has
|
264
288
|
# actually been configured for the state
|
265
289
|
def add_predicate
|
266
|
-
|
267
|
-
|
268
|
-
|
290
|
+
predicate_method = "#{qualified_name}?"
|
291
|
+
|
292
|
+
if machine.send(:owner_class_ancestor_has_method?, :instance, predicate_method)
|
293
|
+
warn_about_method_conflict(predicate_method, machine.owner_class.ancestors.first)
|
294
|
+
elsif machine.send(:owner_class_has_method?, :instance, predicate_method)
|
295
|
+
warn_about_method_conflict(predicate_method, machine.owner_class)
|
296
|
+
else
|
297
|
+
machine.define_helper(:instance, predicate_method) do |machine, object|
|
298
|
+
machine.states.matches?(object, name)
|
299
|
+
end
|
269
300
|
end
|
270
301
|
end
|
271
302
|
|
@@ -273,5 +304,11 @@ module StateMachines
|
|
273
304
|
def context_name_for(method)
|
274
305
|
:"__#{machine.name}_#{name}_#{method}_#{@context.object_id}__"
|
275
306
|
end
|
307
|
+
|
308
|
+
def warn_about_method_conflict(method, defined_in)
|
309
|
+
return if StateMachines::Machine.ignore_method_conflicts
|
310
|
+
|
311
|
+
warn "Instance method #{method.inspect} is already defined in #{defined_in.inspect}, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true."
|
312
|
+
end
|
276
313
|
end
|
277
314
|
end
|
@@ -1,26 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module StateMachines
|
2
4
|
# Represents a collection of states in a state machine
|
3
5
|
class StateCollection < NodeCollection
|
4
|
-
def initialize(machine)
|
5
|
-
super(machine, :
|
6
|
+
def initialize(machine) # :nodoc:
|
7
|
+
super(machine, index: %i[name qualified_name value])
|
6
8
|
end
|
7
9
|
|
8
10
|
# Determines whether the given object is in a specific state. If the
|
9
11
|
# object's current value doesn't match the state, then this will return
|
10
12
|
# false, otherwise true. If the given state is unknown, then an IndexError
|
11
13
|
# will be raised.
|
12
|
-
#
|
14
|
+
#
|
13
15
|
# == Examples
|
14
|
-
#
|
16
|
+
#
|
15
17
|
# class Vehicle
|
16
18
|
# state_machine :initial => :parked do
|
17
19
|
# other_states :idling
|
18
20
|
# end
|
19
21
|
# end
|
20
|
-
#
|
22
|
+
#
|
21
23
|
# states = Vehicle.state_machine.states
|
22
24
|
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
23
|
-
#
|
25
|
+
#
|
24
26
|
# states.matches?(vehicle, :parked) # => true
|
25
27
|
# states.matches?(vehicle, :idling) # => false
|
26
28
|
# states.matches?(vehicle, :invalid) # => IndexError: :invalid is an invalid key for :name index
|
@@ -31,23 +33,23 @@ module StateMachines
|
|
31
33
|
# Determines the current state of the given object as configured by this
|
32
34
|
# state machine. This will attempt to find a known state that matches
|
33
35
|
# the value of the attribute on the object.
|
34
|
-
#
|
36
|
+
#
|
35
37
|
# == Examples
|
36
|
-
#
|
38
|
+
#
|
37
39
|
# class Vehicle
|
38
40
|
# state_machine :initial => :parked do
|
39
41
|
# other_states :idling
|
40
42
|
# end
|
41
43
|
# end
|
42
|
-
#
|
44
|
+
#
|
43
45
|
# states = Vehicle.state_machine.states
|
44
|
-
#
|
46
|
+
#
|
45
47
|
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
46
48
|
# states.match(vehicle) # => #<StateMachines::State name=:parked value="parked" initial=true>
|
47
|
-
#
|
49
|
+
#
|
48
50
|
# vehicle.state = 'idling'
|
49
51
|
# states.match(vehicle) # => #<StateMachines::State name=:idling value="idling" initial=true>
|
50
|
-
#
|
52
|
+
#
|
51
53
|
# vehicle.state = 'invalid'
|
52
54
|
# states.match(vehicle) # => nil
|
53
55
|
def match(object)
|
@@ -58,20 +60,20 @@ module StateMachines
|
|
58
60
|
# Determines the current state of the given object as configured by this
|
59
61
|
# state machine. If no state is found, then an ArgumentError will be
|
60
62
|
# raised.
|
61
|
-
#
|
63
|
+
#
|
62
64
|
# == Examples
|
63
|
-
#
|
65
|
+
#
|
64
66
|
# class Vehicle
|
65
67
|
# state_machine :initial => :parked do
|
66
68
|
# other_states :idling
|
67
69
|
# end
|
68
70
|
# end
|
69
|
-
#
|
71
|
+
#
|
70
72
|
# states = Vehicle.state_machine.states
|
71
|
-
#
|
73
|
+
#
|
72
74
|
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
73
75
|
# states.match!(vehicle) # => #<StateMachines::State name=:parked value="parked" initial=true>
|
74
|
-
#
|
76
|
+
#
|
75
77
|
# vehicle.state = 'invalid'
|
76
78
|
# states.match!(vehicle) # => ArgumentError: "invalid" is not a known state value
|
77
79
|
def match!(object)
|
@@ -80,13 +82,13 @@ module StateMachines
|
|
80
82
|
|
81
83
|
# Gets the order in which states should be displayed based on where they
|
82
84
|
# were first referenced. This will order states in the following priority:
|
83
|
-
#
|
85
|
+
#
|
84
86
|
# 1. Initial state
|
85
87
|
# 2. Event transitions (:from, :except_from, :to, :except_to options)
|
86
88
|
# 3. States with behaviors
|
87
89
|
# 4. States referenced via +state+ or +other_states+
|
88
90
|
# 5. States referenced in callbacks
|
89
|
-
#
|
91
|
+
#
|
90
92
|
# This order will determine how the GraphViz visualizations are rendered.
|
91
93
|
def by_priority
|
92
94
|
order = select { |state| state.initial }.map { |state| state.name }
|
@@ -102,6 +104,7 @@ module StateMachines
|
|
102
104
|
end
|
103
105
|
|
104
106
|
private
|
107
|
+
|
105
108
|
# Gets the value for the given attribute on the node
|
106
109
|
def value(node, attribute)
|
107
110
|
attribute == :value ? node.value(false) : super
|
@@ -1,133 +1,134 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'options_validator'
|
2
4
|
|
3
|
-
|
5
|
+
module StateMachines
|
4
6
|
# Represents a module which will get evaluated within the context of a state.
|
5
|
-
#
|
7
|
+
#
|
6
8
|
# Class-level methods are proxied to the owner class, injecting a custom
|
7
9
|
# <tt>:if</tt> condition along with method. This assumes that the method has
|
8
10
|
# support for a set of configuration options, including <tt>:if</tt>. This
|
9
11
|
# condition will check that the object's state matches this context's state.
|
10
|
-
#
|
12
|
+
#
|
11
13
|
# Instance-level methods are used to define state-driven behavior on the
|
12
14
|
# state's owner class.
|
13
|
-
#
|
15
|
+
#
|
14
16
|
# == Examples
|
15
|
-
#
|
17
|
+
#
|
16
18
|
# class Vehicle
|
17
19
|
# class << self
|
18
20
|
# attr_accessor :validations
|
19
|
-
#
|
21
|
+
#
|
20
22
|
# def validate(options, &block)
|
21
23
|
# validations << options
|
22
24
|
# end
|
23
25
|
# end
|
24
|
-
#
|
26
|
+
#
|
25
27
|
# self.validations = []
|
26
28
|
# attr_accessor :state, :simulate
|
27
|
-
#
|
29
|
+
#
|
28
30
|
# def moving?
|
29
31
|
# self.class.validations.all? {|validation| validation[:if].call(self)}
|
30
32
|
# end
|
31
33
|
# end
|
32
|
-
#
|
34
|
+
#
|
33
35
|
# In the above class, a simple set of validation behaviors have been defined.
|
34
36
|
# Each validation consists of a configuration like so:
|
35
|
-
#
|
37
|
+
#
|
36
38
|
# Vehicle.validate :unless => :simulate
|
37
39
|
# Vehicle.validate :if => lambda {|vehicle| ...}
|
38
|
-
#
|
40
|
+
#
|
39
41
|
# In order to scope validations to a particular state context, the class-level
|
40
42
|
# +validate+ method can be invoked like so:
|
41
|
-
#
|
43
|
+
#
|
42
44
|
# machine = StateMachines::Machine.new(Vehicle)
|
43
45
|
# context = StateMachines::StateContext.new(machine.state(:first_gear))
|
44
46
|
# context.validate(:unless => :simulate)
|
45
|
-
#
|
47
|
+
#
|
46
48
|
# vehicle = Vehicle.new # => #<Vehicle:0xb7ce491c @simulate=nil, @state=nil>
|
47
49
|
# vehicle.moving? # => false
|
48
|
-
#
|
50
|
+
#
|
49
51
|
# vehicle.state = 'first_gear'
|
50
52
|
# vehicle.moving? # => true
|
51
|
-
#
|
53
|
+
#
|
52
54
|
# vehicle.simulate = true
|
53
55
|
# vehicle.moving? # => false
|
54
56
|
class StateContext < Module
|
55
|
-
|
56
57
|
include EvalHelpers
|
57
|
-
|
58
|
+
|
58
59
|
# The state machine for which this context's state is defined
|
59
60
|
attr_reader :machine
|
60
|
-
|
61
|
+
|
61
62
|
# The state that must be present in an object for this context to be active
|
62
63
|
attr_reader :state
|
63
|
-
|
64
|
+
|
64
65
|
# Creates a new context for the given state
|
65
66
|
def initialize(state)
|
66
67
|
@state = state
|
67
68
|
@machine = state.machine
|
68
|
-
|
69
|
+
|
69
70
|
state_name = state.name
|
70
71
|
machine_name = machine.name
|
71
|
-
@condition =
|
72
|
+
@condition = ->(object) { object.class.state_machine(machine_name).states.matches?(object, state_name) }
|
72
73
|
end
|
73
|
-
|
74
|
+
|
74
75
|
# Creates a new transition that determines what to change the current state
|
75
76
|
# to when an event fires from this state.
|
76
|
-
#
|
77
|
+
#
|
77
78
|
# Since this transition is being defined within a state context, you do
|
78
79
|
# *not* need to specify the <tt>:from</tt> option for the transition. For
|
79
80
|
# example:
|
80
|
-
#
|
81
|
+
#
|
81
82
|
# state_machine do
|
82
83
|
# state :parked do
|
83
84
|
# transition :to => :idling, :on => [:ignite, :shift_up] # Transitions to :idling
|
84
85
|
# transition :from => [:idling, :parked], :on => :park, :unless => :seatbelt_on? # Transitions to :parked if seatbelt is off
|
85
86
|
# end
|
86
87
|
# end
|
87
|
-
#
|
88
|
+
#
|
88
89
|
# See StateMachines::Machine#transition for a description of the possible
|
89
90
|
# configurations for defining transitions.
|
90
91
|
def transition(options)
|
91
|
-
|
92
|
+
StateMachines::OptionsValidator.assert_valid_keys!(options, :from, :to, :on, :if, :unless)
|
92
93
|
raise ArgumentError, 'Must specify :on event' unless options[:on]
|
93
94
|
raise ArgumentError, 'Must specify either :to or :from state' unless !options[:to] ^ !options[:from]
|
94
|
-
|
95
|
-
machine.transition(options.merge(options[:to] ? {:
|
95
|
+
|
96
|
+
machine.transition(options.merge(options[:to] ? { from: state.name } : { to: state.name }))
|
96
97
|
end
|
97
|
-
|
98
|
+
|
98
99
|
# Hooks in condition-merging to methods that don't exist in this module
|
99
|
-
def method_missing(*args, &
|
100
|
+
def method_missing(*args, &)
|
100
101
|
# Get the configuration
|
101
102
|
if args.last.is_a?(Hash)
|
102
103
|
options = args.last
|
103
104
|
else
|
104
105
|
args << options = {}
|
105
106
|
end
|
106
|
-
|
107
|
+
|
107
108
|
# Get any existing condition that may need to be merged
|
108
109
|
if_condition = options.delete(:if)
|
109
110
|
unless_condition = options.delete(:unless)
|
110
|
-
|
111
|
+
|
111
112
|
# Provide scope access to configuration in case the block is evaluated
|
112
113
|
# within the object instance
|
113
114
|
proxy = self
|
114
115
|
proxy_condition = @condition
|
115
|
-
|
116
|
+
|
116
117
|
# Replace the configuration condition with the one configured for this
|
117
118
|
# proxy, merging together any existing conditions
|
118
119
|
options[:if] = lambda do |*condition_args|
|
119
120
|
# Block may be executed within the context of the actual object, so
|
120
121
|
# it'll either be the first argument or the executing context
|
121
122
|
object = condition_args.first || self
|
122
|
-
|
123
|
+
|
123
124
|
proxy.evaluate_method(object, proxy_condition) &&
|
124
|
-
|
125
|
-
|
125
|
+
Array(if_condition).all? { |condition| proxy.evaluate_method(object, condition) } &&
|
126
|
+
!Array(unless_condition).any? { |condition| proxy.evaluate_method(object, condition) }
|
126
127
|
end
|
127
|
-
|
128
|
+
|
128
129
|
# Evaluate the method on the owner class with the condition proxied
|
129
130
|
# through
|
130
|
-
machine.owner_class.send(*args, &
|
131
|
+
machine.owner_class.send(*args, &)
|
131
132
|
end
|
132
133
|
end
|
133
134
|
end
|