domainic-command 0.1.0.alpha.1.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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