domainic-command 0.1.0.alpha.1.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +11 -0
  3. data/CHANGELOG.md +19 -0
  4. data/LICENSE +1 -1
  5. data/README.md +93 -2
  6. data/lib/domainic/command/class_methods.rb +181 -0
  7. data/lib/domainic/command/context/attribute.rb +157 -0
  8. data/lib/domainic/command/context/attribute_set.rb +96 -0
  9. data/lib/domainic/command/context/behavior.rb +132 -0
  10. data/lib/domainic/command/context/input_context.rb +55 -0
  11. data/lib/domainic/command/context/output_context.rb +55 -0
  12. data/lib/domainic/command/context/runtime_context.rb +126 -0
  13. data/lib/domainic/command/errors/error.rb +23 -0
  14. data/lib/domainic/command/errors/execution_error.rb +40 -0
  15. data/lib/domainic/command/instance_methods.rb +92 -0
  16. data/lib/domainic/command/result/error_set.rb +272 -0
  17. data/lib/domainic/command/result/status.rb +49 -0
  18. data/lib/domainic/command/result.rb +194 -0
  19. data/lib/domainic/command.rb +77 -0
  20. data/lib/domainic-command.rb +3 -0
  21. data/sig/domainic/command/class_methods.rbs +100 -0
  22. data/sig/domainic/command/context/attribute.rbs +104 -0
  23. data/sig/domainic/command/context/attribute_set.rbs +69 -0
  24. data/sig/domainic/command/context/behavior.rbs +82 -0
  25. data/sig/domainic/command/context/input_context.rbs +40 -0
  26. data/sig/domainic/command/context/output_context.rbs +40 -0
  27. data/sig/domainic/command/context/runtime_context.rbs +90 -0
  28. data/sig/domainic/command/errors/error.rbs +21 -0
  29. data/sig/domainic/command/errors/execution_error.rbs +32 -0
  30. data/sig/domainic/command/instance_methods.rbs +56 -0
  31. data/sig/domainic/command/result/error_set.rbs +186 -0
  32. data/sig/domainic/command/result/status.rbs +47 -0
  33. data/sig/domainic/command/result.rbs +149 -0
  34. data/sig/domainic/command.rbs +67 -0
  35. data/sig/domainic-command.rbs +1 -0
  36. data/sig/manifest.yaml +1 -0
  37. metadata +50 -13
@@ -0,0 +1,104 @@
1
+ module Domainic
2
+ module Command
3
+ module Context
4
+ # Represents an attribute within a command context. This class manages the lifecycle of an attribute, including
5
+ # its validation, default values, and metadata such as descriptions
6
+ #
7
+ # The `Attribute` class supports a variety of configuration options, such as marking an attribute as required,
8
+ # defining static or dynamic default values, and specifying custom validators. These features ensure that
9
+ # attributes conform to expected rules and provide useful metadata for documentation or runtime behavior
10
+ #
11
+ # @author {https://aaronmallen.me Aaron Allen}
12
+ # @since 0.1.0
13
+ class Attribute
14
+ # Represents an undefined default value for an attribute. This is used internally to distinguish between
15
+ # an attribute with no default and one with a defined default
16
+ #
17
+ # @return [Object] A frozen object representing an undefined default value
18
+ UNDEFINED_DEFAULT: Object
19
+
20
+ @type_validator: Class | Module | Object | Proc
21
+
22
+ @default: untyped
23
+
24
+ @description: String?
25
+
26
+ @name: Symbol
27
+
28
+ @required: bool
29
+
30
+ # The textual description of the attribute, providing metadata about its purpose or usage
31
+ #
32
+ # @return [String, nil] A description of the attribute
33
+ attr_reader description: String?
34
+
35
+ # The name of the attribute, uniquely identifying it within a command context
36
+ #
37
+ # @return [Symbol] The name of the attribute
38
+ attr_reader name: Symbol
39
+
40
+ # Create a new attribute instance
41
+ #
42
+ # @overload initialize(
43
+ # name, type_validator_or_description = nil, description_or_type_validator = nil, **options
44
+ # )
45
+ # @param name [String, Symbol] The {#name} of the attribute
46
+ # @param type_validator_or_description [Class, Module, Object, Proc, String, nil] A type validator or the
47
+ # {#description} of the attribute
48
+ # @param description_or_type_validator [Class, Module, Object, Proc, String, nil] The {#description} or a
49
+ # type_validator of the attribute
50
+ # @param options [Hash] Configuration options for the attribute
51
+ # @option options [Object] :default The {#default} of the attribute
52
+ # @option options [Proc] :default_generator An alias for :default
53
+ # @option options [Object] :default_value An alias for :default
54
+ # @option options [String, nil] :desc An alias for :description
55
+ # @option options [String, nil] :description The {#description} of the attribute
56
+ # @option options [Boolean] :required Whether the attribute is {#required?}
57
+ # @option options [Class, Module, Object, Proc] :type A type validator for the attribute value
58
+ #
59
+ # @return [Attribute] the new Attribute instance
60
+ def initialize: (String | Symbol name, *(Class | Module | Object | Proc | String)? type_validator_and_description, ?default: untyped, ?default_generator: untyped, ?default_value: untyped, ?desc: String?, ?description: String?, ?required: bool, ?type: Class | Module | Object | Proc) -> void
61
+
62
+ # Retrieves the default value of the attribute. If a default generator is specified, it evaluates the generator
63
+ # and returns the result
64
+ #
65
+ # @return [Object, nil] The default value or the result of the generator
66
+ def default: () -> untyped
67
+
68
+ # Determines whether the attribute has a default value defined
69
+ #
70
+ # @return [Boolean] `true` if a default is set; otherwise, `false`
71
+ def default?: () -> bool
72
+
73
+ # Determines whether the attribute is marked as required
74
+ #
75
+ # @return [Boolean] `true` if the attribute is required; otherwise, `false`
76
+ def required?: () -> bool
77
+
78
+ # Validates the given value against the attribute's type validator
79
+ #
80
+ # @param value [Object] The value to validate
81
+ #
82
+ # @return [Boolean] `true` if the value is valid; otherwise, `false`
83
+ def valid?: (untyped value) -> bool
84
+
85
+ private
86
+
87
+ # Initializes the {#default} value for the attribute, using the provided options
88
+ #
89
+ # @param options [Hash] Configuration options containing default-related keys
90
+ #
91
+ # @return [void]
92
+ def initialize_default: (Hash[Symbol, untyped] options) -> void
93
+
94
+ # Initializes the description and type validator for the attribute based on the given arguments and options
95
+ #
96
+ # @param arguments [Array<Class, Module, Object, Proc, String, nil>] Arguments for validators or description
97
+ # @param options [Hash] Configuration options
98
+ #
99
+ # @return [void]
100
+ def initialize_description_and_type_validator: (Array[(Class | Module | Object | Proc | String)?] arguments, Hash[Symbol, untyped] options) -> void
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,69 @@
1
+ module Domainic
2
+ module Command
3
+ module Context
4
+ # A collection class for managing a set of command context attributes. This class provides a simple interface
5
+ # for storing, accessing, and iterating over {Attribute} instances.
6
+ #
7
+ # @example
8
+ # set = AttributeSet.new
9
+ # set.add(Attribute.new(:name))
10
+ # set[:name] # => #<Attribute name=:name>
11
+ #
12
+ # @author {https://aaronmallen.me Aaron Allen}
13
+ # @since 0.1.0
14
+ class AttributeSet
15
+ @lookup: Hash[Symbol, Attribute]
16
+
17
+ # Creates a new AttributeSet instance
18
+ #
19
+ # @return [AttributeSet]
20
+ def initialize: () -> void
21
+
22
+ # Retrieves an attribute by name
23
+ #
24
+ # @param attribute_name [String, Symbol] The name of the attribute to retrieve
25
+ #
26
+ # @return [Attribute, nil] The attribute with the given name, or nil if not found
27
+ def []: (String | Symbol attribute_name) -> Attribute?
28
+
29
+ # Adds an attribute to the set
30
+ #
31
+ # @param attribute [Attribute] The attribute to add
32
+ #
33
+ # @raise [ArgumentError] If the provided attribute is not an {Attribute} instance
34
+ # @return [void]
35
+ def add: (Attribute attribute) -> void
36
+
37
+ # Returns all attributes in the set
38
+ #
39
+ # @return [Array<Attribute>] An array of all attributes
40
+ def all: () -> Array[Attribute]
41
+
42
+ # Iterates over each attribute in the set
43
+ #
44
+ # @yield [Attribute] Each attribute in the set
45
+ #
46
+ # @return [void]
47
+ def each: () { (Attribute) -> untyped } -> void
48
+
49
+ # Iterates over each attribute in the set with an object
50
+ #
51
+ # @overload each_with_object(object)
52
+ # @param object [Object] The object to pass to the block
53
+ # @yield [Attribute, Object] Each attribute and the object
54
+ #
55
+ # @return [Object] The final state of the object
56
+ def each_with_object: [U] (U object) { (Attribute, U) -> untyped } -> U
57
+
58
+ private
59
+
60
+ # Ensure that Attributes are duplicated when the AttributeSet is duplicated
61
+ #
62
+ # @param source [AttributeSet] The source AttributeSet to copy
63
+ #
64
+ # @return [AttributeSet]
65
+ def initialize_copy: (untyped source) -> untyped
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,82 @@
1
+ module Domainic
2
+ module Command
3
+ module Context
4
+ # A module that provides attribute management for command contexts. When included in a class, it provides
5
+ # a DSL for defining and managing typed attributes with validation, default values, and thread-safe access.
6
+ #
7
+ # ## Thread Safety
8
+ # The attribute system is designed to be thread-safe during class definition and inheritance. A class-level
9
+ # mutex protects the attribute registry during:
10
+ # * Definition of new attributes via the DSL
11
+ # * Inheritance of attributes to subclasses
12
+ #
13
+ # @author {https://aaronmallen.me Aaron Allen}
14
+ # @since 0.1.0
15
+ module Behavior
16
+ def self.included: (Class | Module base) -> void
17
+
18
+ # Provides class-level methods for defining and managing attributes. These methods are
19
+ # automatically extended onto any class that includes {Behavior}.
20
+ #
21
+ # @since 0.1.0
22
+ module ClassMethods
23
+ @attributes: AttributeSet
24
+
25
+ @attribute_lock: Mutex
26
+
27
+ private
28
+
29
+ # Defines a new attribute for the context.
30
+ #
31
+ # @overload attribute(name, *type_validator_and_description, **options)
32
+ # @param name [String, Symbol] The name of the attribute
33
+ # @param type_validator_and_description [Array<Class, Module, Object, Proc, String, nil>] Type validator or
34
+ # description arguments
35
+ # @param options [Hash] Configuration options for the attribute
36
+ # @option options [Object] :default A static default value
37
+ # @option options [Proc] :default_generator A proc that generates the default value
38
+ # @option options [Object] :default_value Alias for :default
39
+ # @option options [String, nil] :desc Short description of the attribute
40
+ # @option options [String, nil] :description Full description of the attribute
41
+ # @option options [Boolean] :required Whether the attribute is required
42
+ # @option options [Class, Module, Object, Proc] :type A type validator
43
+ #
44
+ # @return [void]
45
+ def attribute: (String | Symbol name, *(Class | Module | Object | Proc | String)? type_validator_and_description, ?default: untyped, ?default_generator: untyped, ?default_value: untyped, ?desc: String?, ?description: String?, ?required: bool, ?type: Class | Module | Object | Proc) -> void
46
+
47
+ # Returns the mutex used to synchronize attribute operations.
48
+ #
49
+ # @return [Mutex]
50
+ def attribute_lock: () -> Mutex
51
+
52
+ # Returns the set of attributes defined for this context.
53
+ #
54
+ # @return [AttributeSet]
55
+ def attributes: () -> AttributeSet
56
+
57
+ # Handles inheritance of attributes to subclasses.
58
+ #
59
+ # @param subclass [Class, Module] The inheriting class
60
+ #
61
+ # @return [void]
62
+ def inherited: (Class | Module subclass) -> void
63
+ end
64
+
65
+ # Initializes a new context instance with the given attributes.
66
+ #
67
+ # @param options [Hash{String, Symbol => Object}] Attribute values for initialization
68
+ #
69
+ # @raise [ArgumentError] If any attribute values are invalid
70
+ # @return [Behavior]
71
+ def initialize: (**untyped options) -> void
72
+
73
+ # Returns a hash of all attribute names and their values.
74
+ #
75
+ # @return [Hash{Symbol => Object}] A hash of attribute values
76
+ def to_hash: () -> Hash[Symbol, untyped]
77
+
78
+ alias to_h to_hash
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,40 @@
1
+ module Domainic
2
+ module Command
3
+ module Context
4
+ # A context class for managing command input arguments. This class provides a structured way to define,
5
+ # validate, and access input parameters for commands.
6
+ #
7
+ # @example
8
+ # class MyInputContext < Domainic::Command::Context::InputContext
9
+ # argument :name, String, "The name to process"
10
+ # argument :count, Integer, default: 1
11
+ # end
12
+ #
13
+ # @author {https://aaronmallen.me Aaron Allen}
14
+ # @since 0.1.0
15
+ class InputContext
16
+ extend Behavior::ClassMethods
17
+
18
+ include Behavior
19
+
20
+ # Defines an input argument for the command
21
+ #
22
+ # @overload argument(name, *type_validator_and_description, **options)
23
+ # @param name [String, Symbol] The name of the argument
24
+ # @param type_validator_and_description [Array<Class, Module, Object, Proc, String, nil>] Type validator or
25
+ # description arguments
26
+ # @param options [Hash] Configuration options for the argument
27
+ # @option options [Object] :default A static default value
28
+ # @option options [Proc] :default_generator A proc that generates the default value
29
+ # @option options [Object] :default_value Alias for :default
30
+ # @option options [String, nil] :desc Short description of the argument
31
+ # @option options [String, nil] :description Full description of the argument
32
+ # @option options [Boolean] :required Whether the argument is required
33
+ # @option options [Class, Module, Object, Proc] :type A type validator
34
+ #
35
+ # @return [void]
36
+ def self.argument: (String | Symbol name, *(Class | Module | Object | Proc | String)? type_validator_and_description, ?default: untyped, ?default_generator: untyped, ?default_value: untyped, ?desc: String?, ?description: String?, ?required: bool, ?type: Class | Module | Object | Proc) -> void
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ module Domainic
2
+ module Command
3
+ module Context
4
+ # A context class for managing command output values. This class provides a structured way to define,
5
+ # validate, and access the return values from commands.
6
+ #
7
+ # @example
8
+ # class MyOutputContext < Domainic::Command::Context::OutputContext
9
+ # field :processed_name, String, "The processed name"
10
+ # field :status, Symbol, default: :success
11
+ # end
12
+ #
13
+ # @author {https://aaronmallen.me Aaron Allen}
14
+ # @since 0.1.0
15
+ class OutputContext
16
+ extend Behavior::ClassMethods
17
+
18
+ include Behavior
19
+
20
+ # Defines a return value for the command
21
+ #
22
+ # @overload field(name, *type_validator_and_description, **options)
23
+ # @param name [String, Symbol] The name of the return value
24
+ # @param type_validator_and_description [Array<Class, Module, Object, Proc, String, nil>] Type validator or
25
+ # description arguments
26
+ # @param options [Hash] Configuration options for the return value
27
+ # @option options [Object] :default A static default value
28
+ # @option options [Proc] :default_generator A proc that generates the default value
29
+ # @option options [Object] :default_value Alias for :default
30
+ # @option options [String, nil] :desc Short description of the return value
31
+ # @option options [String, nil] :description Full description of the return value
32
+ # @option options [Boolean] :required Whether the return value is required
33
+ # @option options [Class, Module, Object, Proc] :type A type validator
34
+ #
35
+ # @return [void]
36
+ def self.field: (String | Symbol name, *(Class | Module | Object | Proc | String)? type_validator_and_description, ?default: untyped, ?default_generator: untyped, ?default_value: untyped, ?desc: String?, ?description: String?, ?required: bool, ?type: Class | Module | Object | Proc) -> void
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,90 @@
1
+ module Domainic
2
+ module Command
3
+ module Context
4
+ # A flexible context class for managing command state during execution. This class provides a dynamic
5
+ # storage mechanism for command data, allowing both hash-style and method-style access to values.
6
+ #
7
+ # The RuntimeContext serves as a mutable workspace during command execution, bridging the gap between
8
+ # input parameters and output values. It automatically handles type coercion of keys to symbols and
9
+ # provides safe value duplication when converting to a hash.
10
+ #
11
+ # @example Hash-style access
12
+ # context = RuntimeContext.new(count: 1)
13
+ # context[:count] #=> 1
14
+ # context[:count] = 2
15
+ # context[:count] #=> 2
16
+ #
17
+ # @example Method-style access
18
+ # context = RuntimeContext.new(name: "test")
19
+ # context.name #=> "test"
20
+ # context.name = "new test"
21
+ # context.name #=> "new test"
22
+ #
23
+ # @author {https://aaronmallen.me Aaron Allen}
24
+ # @since 0.1.0
25
+ class RuntimeContext
26
+ @data: Hash[Symbol, untyped]
27
+
28
+ # Creates a new RuntimeContext with the given options
29
+ #
30
+ # @param options [Hash] Initial values for the context
31
+ #
32
+ # @return [RuntimeContext]
33
+ def initialize: (**untyped options) -> void
34
+
35
+ # Retrieves a value by its attribute name
36
+ #
37
+ # @param attribute_name [String, Symbol] The name of the attribute to retrieve
38
+ #
39
+ # @return [Object, nil] The value associated with the attribute name
40
+ def []: (String | Symbol attribute_name) -> untyped
41
+
42
+ # Sets a value for the given attribute name
43
+ #
44
+ # @param attribute_name [String, Symbol] The name of the attribute to set
45
+ # @param value [Object] The value to store
46
+ #
47
+ # @return [Object] The stored value
48
+ def []=: (String | Symbol attribute_name, untyped value) -> untyped
49
+
50
+ # Converts the context to a hash, duplicating values where appropriate
51
+ #
52
+ # @note Class and Module values are not duplicated to prevent potential issues
53
+ #
54
+ # @return [Hash{Symbol => Object}] A hash containing all stored values
55
+ def to_hash: () -> Hash[Symbol, untyped]
56
+
57
+ alias to_h to_hash
58
+
59
+ private
60
+
61
+ # Handles dynamic method calls for reading and writing attributes
62
+ #
63
+ # @return [Object, nil]
64
+ def method_missing: ...
65
+
66
+ # Reads a value from the internal storage
67
+ #
68
+ # @param attribute_name [String, Symbol] The name of the attribute to read
69
+ #
70
+ # @return [Object, nil] The stored value
71
+ def read_from_attribute: (String | Symbol attribute_name) -> untyped
72
+
73
+ # Determines if a method name can be handled dynamically
74
+ #
75
+ # @param method_name [Symbol] The name of the method to check
76
+ # @param _include_private [Boolean] Whether to include private methods
77
+ #
78
+ # @return [Boolean] Whether the method can be handled
79
+ def respond_to_missing?: (String | Symbol method_name, ?bool _include_private) -> bool
80
+
81
+ # Writes a value to the internal storage
82
+ #
83
+ # @param attribute_name [String, Symbol] The name of the attribute to write
84
+ # @param value [Object] The value to store
85
+ # @return [Object] The stored value
86
+ def write_to_attribute: (String | Symbol attribute_name, untyped value) -> untyped
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,21 @@
1
+ module Domainic
2
+ module Command
3
+ # Base error class for command-related errors. This class serves as the root of the command error
4
+ # hierarchy, allowing for specific error handling of command-related issues.
5
+ #
6
+ # @note This is an abstract class and should not be instantiated directly. Instead, use one of its
7
+ # subclasses for specific error cases.
8
+ #
9
+ # @example Rescuing command errors
10
+ # begin
11
+ # # Command execution code
12
+ # rescue Domainic::Command::Error => e
13
+ # # Handle any command-related error
14
+ # end
15
+ #
16
+ # @author {https://aaronmallen.me Aaron Allen}
17
+ # @since 0.1.0
18
+ class Error < StandardError
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module Domainic
2
+ module Command
3
+ # Error class raised when a command encounters an execution failure. This class provides access to
4
+ # both the error message and the {Result} object containing detailed information about the failure.
5
+ #
6
+ # @example Handling execution errors
7
+ # begin
8
+ # command.call!
9
+ # rescue Domainic::Command::ExecutionError => e
10
+ # puts e.message # Access the error message
11
+ # puts e.result.errors # Access the detailed errors
12
+ # puts e.result.status_code # Access the status code
13
+ # end
14
+ #
15
+ # @author {https://aaronmallen.me Aaron Allen}
16
+ # @since 0.1.0
17
+ class ExecutionError < Error
18
+ # The {Result} object containing detailed information about the execution failure
19
+ #
20
+ # @return [Result] The result object associated with the failure
21
+ attr_reader result: Result
22
+
23
+ # Creates a new execution error with the given message and result
24
+ #
25
+ # @param message [String] The error message describing what went wrong
26
+ # @param result [Result] The result object containing detailed failure information
27
+ #
28
+ # @return [void]
29
+ def initialize: (String message, Result result) -> void
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,56 @@
1
+ module Domainic
2
+ module Command
3
+ # Instance methods that are included in any class that includes {Command}. These methods provide
4
+ # the core execution logic and error handling for commands.
5
+ #
6
+ # @author {https://aaronmallen.me Aaron Allen}
7
+ # @since 0.1.0
8
+ module InstanceMethods
9
+ # Executes the command with the given context, handling any errors
10
+ #
11
+ # @param context [Hash] The input context for the command
12
+ #
13
+ # @return [Result] The result of the command execution
14
+ def call: (**untyped context) -> Result
15
+
16
+ # Executes the command with the given context, raising any errors
17
+ #
18
+ # @param input [Hash] The input context for the command
19
+ #
20
+ # @raise [ExecutionError] If the command execution fails
21
+ # @return [Result] The result of the command execution
22
+ def call!: (**untyped context) -> Result
23
+
24
+ # Executes the command's business logic
25
+ #
26
+ # @abstract Subclass and override {#execute} to implement command behavior
27
+ #
28
+ # @raise [NotImplementedError] If the subclass does not implement {#execute}
29
+ # @return [void]
30
+ def execute: () -> void
31
+
32
+ private
33
+
34
+ # The runtime context for the command execution
35
+ #
36
+ # @return [Context::RuntimeContext] The runtime context
37
+ attr_reader context: Context::RuntimeContext
38
+
39
+ # Execute the command with the given input context
40
+ #
41
+ # @param input [Hash] The input context for the command
42
+ #
43
+ # @return [Result] The result of the command execution
44
+ def __execute_command!: (Hash[String | Symbol, untyped] input) -> Result
45
+
46
+ # Validates an input or output context
47
+ #
48
+ # @param context_type [Symbol] The type of context to validate
49
+ # @param context [Hash] The context data to validate
50
+ #
51
+ # @raise [ExecutionError] If the context is invalid
52
+ # @return [Context::InputContext, Context::OutputContext] The validated context
53
+ def __validate_context!: (:input | :output context_type, Hash[String | Symbol, untyped] context) -> (Context::InputContext | Context::OutputContext)
54
+ end
55
+ end
56
+ end