cmdx 1.1.1 → 1.5.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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/.ruby-version +1 -1
  11. data/CHANGELOG.md +6 -128
  12. data/LLM.md +3317 -0
  13. data/README.md +68 -44
  14. data/docs/attributes/coercions.md +162 -0
  15. data/docs/attributes/defaults.md +90 -0
  16. data/docs/attributes/definitions.md +281 -0
  17. data/docs/attributes/naming.md +78 -0
  18. data/docs/attributes/validations.md +309 -0
  19. data/docs/basics/chain.md +56 -249
  20. data/docs/basics/context.md +56 -289
  21. data/docs/basics/execution.md +114 -0
  22. data/docs/basics/setup.md +37 -334
  23. data/docs/callbacks.md +89 -467
  24. data/docs/deprecation.md +91 -174
  25. data/docs/getting_started.md +212 -202
  26. data/docs/internationalization.md +11 -647
  27. data/docs/interruptions/exceptions.md +23 -198
  28. data/docs/interruptions/faults.md +71 -151
  29. data/docs/interruptions/halt.md +109 -186
  30. data/docs/logging.md +44 -256
  31. data/docs/middlewares.md +113 -426
  32. data/docs/outcomes/result.md +81 -228
  33. data/docs/outcomes/states.md +33 -221
  34. data/docs/outcomes/statuses.md +21 -311
  35. data/docs/tips_and_tricks.md +120 -70
  36. data/docs/workflows.md +99 -283
  37. data/lib/cmdx/.DS_Store +0 -0
  38. data/lib/cmdx/attribute.rb +229 -0
  39. data/lib/cmdx/attribute_registry.rb +94 -0
  40. data/lib/cmdx/attribute_value.rb +193 -0
  41. data/lib/cmdx/callback_registry.rb +69 -77
  42. data/lib/cmdx/chain.rb +56 -73
  43. data/lib/cmdx/coercion_registry.rb +52 -68
  44. data/lib/cmdx/coercions/array.rb +19 -18
  45. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  46. data/lib/cmdx/coercions/boolean.rb +26 -25
  47. data/lib/cmdx/coercions/complex.rb +21 -22
  48. data/lib/cmdx/coercions/date.rb +25 -23
  49. data/lib/cmdx/coercions/date_time.rb +24 -25
  50. data/lib/cmdx/coercions/float.rb +25 -22
  51. data/lib/cmdx/coercions/hash.rb +31 -32
  52. data/lib/cmdx/coercions/integer.rb +30 -24
  53. data/lib/cmdx/coercions/rational.rb +29 -24
  54. data/lib/cmdx/coercions/string.rb +19 -22
  55. data/lib/cmdx/coercions/symbol.rb +37 -0
  56. data/lib/cmdx/coercions/time.rb +26 -25
  57. data/lib/cmdx/configuration.rb +49 -108
  58. data/lib/cmdx/context.rb +222 -44
  59. data/lib/cmdx/deprecator.rb +61 -0
  60. data/lib/cmdx/errors.rb +42 -252
  61. data/lib/cmdx/exceptions.rb +39 -0
  62. data/lib/cmdx/faults.rb +78 -39
  63. data/lib/cmdx/freezer.rb +51 -0
  64. data/lib/cmdx/identifier.rb +30 -0
  65. data/lib/cmdx/locale.rb +52 -0
  66. data/lib/cmdx/log_formatters/json.rb +21 -22
  67. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  68. data/lib/cmdx/log_formatters/line.rb +15 -22
  69. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  70. data/lib/cmdx/log_formatters/raw.rb +16 -22
  71. data/lib/cmdx/middleware_registry.rb +70 -74
  72. data/lib/cmdx/middlewares/correlate.rb +90 -54
  73. data/lib/cmdx/middlewares/runtime.rb +58 -0
  74. data/lib/cmdx/middlewares/timeout.rb +48 -68
  75. data/lib/cmdx/railtie.rb +12 -45
  76. data/lib/cmdx/result.rb +229 -314
  77. data/lib/cmdx/task.rb +194 -366
  78. data/lib/cmdx/utils/call.rb +49 -0
  79. data/lib/cmdx/utils/condition.rb +71 -0
  80. data/lib/cmdx/utils/format.rb +61 -0
  81. data/lib/cmdx/validator_registry.rb +63 -72
  82. data/lib/cmdx/validators/exclusion.rb +38 -67
  83. data/lib/cmdx/validators/format.rb +48 -49
  84. data/lib/cmdx/validators/inclusion.rb +43 -74
  85. data/lib/cmdx/validators/length.rb +91 -154
  86. data/lib/cmdx/validators/numeric.rb +87 -162
  87. data/lib/cmdx/validators/presence.rb +37 -50
  88. data/lib/cmdx/version.rb +1 -1
  89. data/lib/cmdx/worker.rb +178 -0
  90. data/lib/cmdx/workflow.rb +85 -81
  91. data/lib/cmdx.rb +19 -13
  92. data/lib/generators/cmdx/install_generator.rb +14 -13
  93. data/lib/generators/cmdx/task_generator.rb +25 -50
  94. data/lib/generators/cmdx/templates/install.rb +11 -46
  95. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  96. data/lib/locales/en.yml +18 -4
  97. data/src/cmdx-logo.png +0 -0
  98. metadata +32 -116
  99. data/docs/ai_prompts.md +0 -393
  100. data/docs/basics/call.md +0 -317
  101. data/docs/configuration.md +0 -344
  102. data/docs/parameters/coercions.md +0 -396
  103. data/docs/parameters/defaults.md +0 -335
  104. data/docs/parameters/definitions.md +0 -446
  105. data/docs/parameters/namespacing.md +0 -378
  106. data/docs/parameters/validations.md +0 -405
  107. data/docs/testing.md +0 -553
  108. data/lib/cmdx/callback.rb +0 -53
  109. data/lib/cmdx/chain_inspector.rb +0 -56
  110. data/lib/cmdx/chain_serializer.rb +0 -63
  111. data/lib/cmdx/coercion.rb +0 -57
  112. data/lib/cmdx/coercions/virtual.rb +0 -29
  113. data/lib/cmdx/core_ext/hash.rb +0 -83
  114. data/lib/cmdx/core_ext/module.rb +0 -98
  115. data/lib/cmdx/core_ext/object.rb +0 -125
  116. data/lib/cmdx/correlator.rb +0 -122
  117. data/lib/cmdx/error.rb +0 -60
  118. data/lib/cmdx/fault.rb +0 -140
  119. data/lib/cmdx/immutator.rb +0 -52
  120. data/lib/cmdx/lazy_struct.rb +0 -246
  121. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  122. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  123. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  124. data/lib/cmdx/logger.rb +0 -49
  125. data/lib/cmdx/logger_ansi.rb +0 -68
  126. data/lib/cmdx/logger_serializer.rb +0 -116
  127. data/lib/cmdx/middleware.rb +0 -70
  128. data/lib/cmdx/parameter.rb +0 -312
  129. data/lib/cmdx/parameter_evaluator.rb +0 -231
  130. data/lib/cmdx/parameter_inspector.rb +0 -66
  131. data/lib/cmdx/parameter_registry.rb +0 -106
  132. data/lib/cmdx/parameter_serializer.rb +0 -59
  133. data/lib/cmdx/result_ansi.rb +0 -71
  134. data/lib/cmdx/result_inspector.rb +0 -71
  135. data/lib/cmdx/result_logger.rb +0 -59
  136. data/lib/cmdx/result_serializer.rb +0 -104
  137. data/lib/cmdx/rspec/matchers.rb +0 -28
  138. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  139. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  141. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  142. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  143. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  144. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  145. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  146. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  147. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  148. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  149. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  150. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  151. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  152. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  153. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  154. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  155. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  156. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  157. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  158. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  159. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  160. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  161. data/lib/cmdx/task_deprecator.rb +0 -52
  162. data/lib/cmdx/task_processor.rb +0 -246
  163. data/lib/cmdx/task_serializer.rb +0 -57
  164. data/lib/cmdx/utils/ansi_color.rb +0 -73
  165. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  166. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  167. data/lib/cmdx/utils/name_affix.rb +0 -52
  168. data/lib/cmdx/validator.rb +0 -57
  169. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  170. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  171. data/lib/locales/ar.yml +0 -35
  172. data/lib/locales/cs.yml +0 -35
  173. data/lib/locales/da.yml +0 -35
  174. data/lib/locales/de.yml +0 -35
  175. data/lib/locales/el.yml +0 -35
  176. data/lib/locales/es.yml +0 -35
  177. data/lib/locales/fi.yml +0 -35
  178. data/lib/locales/fr.yml +0 -35
  179. data/lib/locales/he.yml +0 -35
  180. data/lib/locales/hi.yml +0 -35
  181. data/lib/locales/it.yml +0 -35
  182. data/lib/locales/ja.yml +0 -35
  183. data/lib/locales/ko.yml +0 -35
  184. data/lib/locales/nl.yml +0 -35
  185. data/lib/locales/no.yml +0 -35
  186. data/lib/locales/pl.yml +0 -35
  187. data/lib/locales/pt.yml +0 -35
  188. data/lib/locales/ru.yml +0 -35
  189. data/lib/locales/sv.yml +0 -35
  190. data/lib/locales/th.yml +0 -35
  191. data/lib/locales/tr.yml +0 -35
  192. data/lib/locales/vi.yml +0 -35
  193. data/lib/locales/zh.yml +0 -35
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- # Serialization module for converting chain objects to hash representation.
5
- #
6
- # ChainSerializer provides functionality to serialize chain objects into a
7
- # standardized hash format that includes essential metadata about the chain
8
- # execution including unique identification, execution state, status, outcome,
9
- # runtime, and all contained task results. The serialized format is commonly
10
- # used for debugging, logging, introspection, and data exchange throughout
11
- # the task execution pipeline.
12
- module ChainSerializer
13
-
14
- module_function
15
-
16
- # Serializes a chain object into a hash representation.
17
- #
18
- # Converts a chain instance into a standardized hash format containing
19
- # key metadata about the chain's execution context and all contained results.
20
- # The serialization includes information delegated from the first result in
21
- # the chain (state, status, outcome, runtime) along with the chain's unique
22
- # identifier and complete collection of task results converted to hashes.
23
- #
24
- # @param chain [CMDx::Chain] the chain object to serialize
25
- #
26
- # @return [Hash] a hash containing the chain's metadata and execution information
27
- # @option return [String] :id the unique identifier of the chain
28
- # @option return [String] :state the execution state delegated from first result
29
- # @option return [String] :status the execution status delegated from first result
30
- # @option return [String] :outcome the execution outcome delegated from first result
31
- # @option return [Float] :runtime the execution runtime in seconds delegated from first result
32
- # @option return [Array<Hash>] :results array of serialized result hashes from all tasks in the chain
33
- #
34
- # @raise [NoMethodError] if the chain doesn't respond to required methods (id, state, status, outcome, runtime, results)
35
- #
36
- # @example Serialize a workflow chain with multiple tasks
37
- # workflow = DataProcessingWorkflow.call(input: "data")
38
- # ChainSerializer.call(workflow.chain)
39
- # #=> {
40
- # # id: "def456",
41
- # # state: "complete",
42
- # # status: "success",
43
- # # outcome: "success",
44
- # # runtime: 0.123,
45
- # # results: [
46
- # # { index: 0, class: "ValidateDataTask", status: "success", ... },
47
- # # { index: 1, class: "ProcessDataTask", status: "success", ... },
48
- # # { index: 2, class: "SaveDataTask", status: "success", ... }
49
- # # ]
50
- # # }
51
- def call(chain)
52
- {
53
- id: chain.id,
54
- state: chain.state,
55
- status: chain.status,
56
- outcome: chain.outcome,
57
- runtime: chain.runtime,
58
- results: chain.results.map(&:to_h)
59
- }
60
- end
61
-
62
- end
63
- end
data/lib/cmdx/coercion.rb DELETED
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- # Base class for implementing parameter coercion functionality in task processing.
5
- #
6
- # Coercions are used to convert parameter values from one type to another during
7
- # task execution, enabling automatic type conversion and normalization. All coercion
8
- # implementations must inherit from this class and implement the abstract call method.
9
- class Coercion
10
-
11
- # Executes a coercion by creating a new instance and calling it.
12
- #
13
- # @param value [Object] the value to be coerced
14
- # @param options [Hash] additional options for the coercion
15
- #
16
- # @return [Object] the coerced value
17
- #
18
- # @raise [UndefinedCallError] when the coercion subclass doesn't implement call
19
- # @raise [CoercionError] when coercion fails in subclass implementations
20
- #
21
- # @example Execute a coercion on a value
22
- # StringCoercion.call(123) #=> "123"
23
- #
24
- # @example Execute with options
25
- # CustomCoercion.call("value", strict: true) #=> processed_value
26
- def self.call(value, options = {})
27
- new.call(value, options)
28
- end
29
-
30
- # Abstract method that must be implemented by coercion subclasses.
31
- #
32
- # This method contains the actual coercion logic to convert the input
33
- # value to the desired type. Subclasses must override this method
34
- # to provide their specific coercion implementation.
35
- #
36
- # @param value [Object] the value to be coerced (unused in base class)
37
- # @param options [Hash] additional options for the coercion (unused in base class)
38
- #
39
- # @return [Object] the coerced value
40
- #
41
- # @raise [UndefinedCallError] always raised in the base class
42
- # @raise [CoercionError] when coercion fails in subclass implementations
43
- #
44
- # @example Implement in a subclass
45
- # class StringCoercion < CMDx::Coercion
46
- # def call(value, _options = {})
47
- # String(value)
48
- # rescue ArgumentError, TypeError
49
- # raise CoercionError, "could not coerce into a string"
50
- # end
51
- # end
52
- def call(value, options = {}) # rubocop:disable Lint/UnusedMethodArgument
53
- raise UndefinedCallError, "call method not defined in #{self.class.name}"
54
- end
55
-
56
- end
57
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- module Coercions
5
- # Coercion class for virtual values that performs no conversion.
6
- #
7
- # This coercion acts as a pass-through, returning the input value unchanged.
8
- # It's useful when you want to maintain the original value type and format
9
- # without any transformation.
10
- class Virtual < Coercion
11
-
12
- # Returns the given value unchanged.
13
- #
14
- # @param value [Object] the value to return as-is
15
- # @param _options [Hash] optional configuration (currently unused)
16
- #
17
- # @return [Object] the original value without any conversion
18
- #
19
- # @example Returning values unchanged
20
- # Coercions::Virtual.call("hello") #=> "hello"
21
- # Coercions::Virtual.call(123) #=> 123
22
- # Coercions::Virtual.call(nil) #=> nil
23
- def call(value, _options = {})
24
- value
25
- end
26
-
27
- end
28
- end
29
- end
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- module CoreExt
5
- # Extensions for Ruby's Hash class that provide flexible key access and querying.
6
- # These extensions are automatically included in all hashes when CMDx is loaded, providing
7
- # seamless symbol/string key interoperability and enhanced key existence checking.
8
- module HashExtensions
9
-
10
- # Fetches a value from the hash with flexible key matching.
11
- # Tries the exact key first, then attempts symbol/string conversion if not found.
12
- #
13
- # @param key [Symbol, String, Object] the key to fetch from the hash
14
- #
15
- # @return [Object, nil] the value associated with the key, or nil if not found
16
- #
17
- # @example Fetch with symbol key
18
- # hash = { name: "John", "age" => 30 }
19
- # hash.cmdx_fetch(:name) #=> "John"
20
- # hash.cmdx_fetch(:age) #=> 30
21
- #
22
- # @example Fetch with string key
23
- # hash = { name: "John", "age" => 30 }
24
- # hash.cmdx_fetch("name") #=> "John"
25
- # hash.cmdx_fetch("age") #=> 30
26
- def cmdx_fetch(key)
27
- case key
28
- when Symbol then fetch(key) { self[key.to_s] }
29
- when String then fetch(key) { self[key.to_sym] }
30
- else self[key]
31
- end
32
- end
33
-
34
- # Checks if a key exists in the hash with flexible key matching.
35
- # Tries the exact key first, then attempts symbol/string conversion.
36
- #
37
- # @param key [Symbol, String, Object] the key to check for existence
38
- #
39
- # @return [Boolean] true if the key exists (in any form), false otherwise
40
- #
41
- # @example Check key existence
42
- # hash = { name: "John", "age" => 30 }
43
- # hash.cmdx_key?(:name) #=> true
44
- # hash.cmdx_key?("name") #=> true
45
- # hash.cmdx_key?(:age) #=> true
46
- # hash.cmdx_key?("age") #=> true
47
- # hash.cmdx_key?(:missing) #=> false
48
- def cmdx_key?(key)
49
- key?(key) || key?(
50
- case key
51
- when Symbol then key.to_s
52
- when String then key.to_sym
53
- end
54
- )
55
- rescue NoMethodError
56
- false
57
- end
58
-
59
- # Checks if the hash responds to a method or contains a key.
60
- # Combines method existence checking with flexible key existence checking.
61
- #
62
- # @param key [Symbol, String] the method name or key to check
63
- # @param include_private [Boolean] whether to include private methods in the check
64
- #
65
- # @return [Boolean] true if the hash responds to the method or contains the key
66
- #
67
- # @example Check method or key response
68
- # hash = { name: "John", "age" => 30 }
69
- # hash.cmdx_respond_to?(:keys) #=> true (method exists)
70
- # hash.cmdx_respond_to?(:name) #=> true (key exists)
71
- # hash.cmdx_respond_to?("age") #=> true (key exists)
72
- # hash.cmdx_respond_to?(:missing) #=> false
73
- def cmdx_respond_to?(key, include_private = false)
74
- respond_to?(key.to_sym, include_private) || cmdx_key?(key)
75
- rescue NoMethodError
76
- cmdx_key?(key)
77
- end
78
-
79
- end
80
- end
81
- end
82
-
83
- Hash.include(CMDx::CoreExt::HashExtensions)
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- module CoreExt
5
- # Extensions for Ruby's Module class that provide attribute delegation and settings functionality.
6
- # These extensions are automatically included in all modules when CMDx is loaded.
7
- module ModuleExtensions
8
-
9
- # Creates delegated methods that forward calls to another object or class.
10
- # Supports method name prefixing, privacy levels, and optional method existence checking.
11
- #
12
- # @param methods [Array<Symbol>] the method names to delegate
13
- # @param options [Hash] delegation options
14
- # @option options [Symbol] :to the target object or :class to delegate to
15
- # @option options [Boolean] :allow_missing (false) whether to allow delegation to non-existent methods
16
- # @option options [Boolean] :protected (false) whether to make the delegated method protected
17
- # @option options [Boolean] :private (false) whether to make the delegated method private
18
- # @option options [String, Symbol] :prefix optional prefix for the delegated method name
19
- # @option options [String, Symbol] :suffix optional suffix for the delegated method name
20
- #
21
- # @return [void]
22
- # @raise [NoMethodError] when delegating to a non-existent method and :allow_missing is false
23
- #
24
- # @example Delegate methods to an instance variable
25
- # class Task
26
- # def initialize
27
- # @logger = Logger.new
28
- # end
29
- #
30
- # cmdx_attr_delegator :info, :warn, :error, to: :@logger
31
- # end
32
- #
33
- # @example Delegate with prefix and privacy
34
- # class Workflow
35
- # cmdx_attr_delegator :perform, to: :task, prefix: 'execute_', private: true
36
- # end
37
- def cmdx_attr_delegator(*methods, **options)
38
- methods.each do |method|
39
- method_name = Utils::NameAffix.call(method, options.fetch(:to), options)
40
-
41
- define_method(method_name) do |*args, **kwargs, &block|
42
- object = (options[:to] == :class ? self.class : send(options[:to]))
43
-
44
- unless options[:allow_missing] || object.respond_to?(method, true)
45
- raise NoMethodError,
46
- "undefined method `#{method}' for #{options[:to]}"
47
- end
48
-
49
- object.send(method, *args, **kwargs, &block)
50
- end
51
-
52
- case options
53
- in { protected: true } then send(:protected, method_name)
54
- in { private: true } then send(:private, method_name)
55
- else # Leave public
56
- end
57
- end
58
- end
59
-
60
- # Creates a singleton method for accessing inheritable settings with caching and default values.
61
- # Settings are inherited from superclass and can have default values via blocks or static values.
62
- #
63
- # @param method [Symbol] the name of the setting method to create
64
- # @param options [Hash] setting options
65
- # @option options [Object, Proc] :default the default value or a proc that returns the default value
66
- #
67
- # @return [void]
68
- #
69
- # @example Define a setting with a default value
70
- # class BaseTask
71
- # cmdx_attr_setting :timeout, default: 30
72
- # end
73
- #
74
- # BaseTask.timeout #=> 30
75
- #
76
- # @example Define a setting with a dynamic default
77
- # class Task
78
- # cmdx_attr_setting :retry_count, default: -> { ENV['RETRY_COUNT']&.to_i || 3 }
79
- # end
80
- def cmdx_attr_setting(method, **options)
81
- define_singleton_method(method) do
82
- @cmd_facets ||= {}
83
- return @cmd_facets[method] if @cmd_facets.key?(method)
84
-
85
- value = superclass.cmdx_try(method)
86
- return @cmd_facets[method] = value.dup unless value.nil?
87
-
88
- default = options[:default]
89
- value = default.cmdx_call
90
- @cmd_facets[method] = default.is_a?(Proc) ? value : value.dup
91
- end
92
- end
93
-
94
- end
95
- end
96
- end
97
-
98
- Module.include(CMDx::CoreExt::ModuleExtensions)
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- module CoreExt
5
- # Extensions for Ruby's Object class that provide flexible method calling and evaluation utilities.
6
- # These extensions are automatically included in all objects when CMDx is loaded, providing
7
- # safe method invocation, conditional evaluation, and dynamic yielding capabilities.
8
- module ObjectExtensions
9
-
10
- alias cmdx_respond_to? respond_to?
11
-
12
- # Safely tries to call a method, evaluate a proc, or access a hash key.
13
- # Provides flexible invocation that handles different types of callables gracefully.
14
- #
15
- # @param key [Symbol, String, Proc, Object] the method name, proc, or hash key to try
16
- # @param args [Array] arguments to pass to the method or proc
17
- #
18
- # @return [Object, nil] the result of the method call, proc evaluation, or hash access; nil if not found
19
- #
20
- # @example Try calling a method
21
- # "hello".cmdx_try(:upcase) #=> "HELLO"
22
- # "hello".cmdx_try(:missing) #=> nil
23
- #
24
- # @example Try evaluating a proc
25
- # obj.cmdx_try(-> { self.class.name }) #=> "String"
26
- #
27
- # @example Try accessing a hash key
28
- # {name: "John"}.cmdx_try(:name) #=> "John"
29
- def cmdx_try(key, *args, **kwargs, &)
30
- if key.is_a?(Proc)
31
- return instance_eval(&key) unless is_a?(Module) || key.inspect.include?("(lambda)")
32
-
33
- if key.arity.positive? && args.empty?
34
- key.call(self, *args, **kwargs, &)
35
- else
36
- key.call(*args, **kwargs, &)
37
- end
38
- elsif respond_to?(key, true)
39
- send(key, *args, **kwargs, &)
40
- elsif is_a?(Hash)
41
- cmdx_fetch(key)
42
- end
43
- end
44
-
45
- # Evaluates conditional options using :if and :unless logic.
46
- # Supports both method names and procs for conditional evaluation.
47
- #
48
- # @param options [Hash] evaluation options
49
- # @option options [Symbol, Proc] :if condition that must be truthy
50
- # @option options [Symbol, Proc] :unless condition that must be falsy
51
- # @option options [Object] :default (true) default value when no conditions are specified
52
- #
53
- # @return [Boolean] true if conditions are met, false otherwise
54
- #
55
- # @example Evaluate with if condition
56
- # user.cmdx_eval(if: :active?) #=> true if user.active? is truthy
57
- #
58
- # @example Evaluate with unless condition
59
- # user.cmdx_eval(unless: :banned?) #=> true if user.banned? is falsy
60
- #
61
- # @example Evaluate with both conditions
62
- # user.cmdx_eval(if: :active?, unless: :banned?) #=> true if active and not banned
63
- def cmdx_eval(options = {})
64
- if options[:if] && options[:unless]
65
- cmdx_try(options[:if]) && !cmdx_try(options[:unless])
66
- elsif options[:if]
67
- cmdx_try(options[:if])
68
- elsif options[:unless]
69
- !cmdx_try(options[:unless])
70
- else
71
- options.fetch(:default, true)
72
- end
73
- end
74
-
75
- # Yields or returns a value based on its type, with smart method calling.
76
- # Handles symbols/strings as method names, procs/hashes via cmdx_try, and returns other values as-is.
77
- #
78
- # @param key [Symbol, String, Proc, Hash, Object] the value to yield or method to call
79
- # @param args [Array] arguments to pass to method calls
80
- #
81
- # @return [Object] the result of method call, proc evaluation, or the value itself
82
- #
83
- # @example Yield a method call
84
- # "hello".cmdx_yield(:upcase) #=> "HELLO"
85
- #
86
- # @example Yield a static value
87
- # obj.cmdx_yield("static") #=> "static"
88
- #
89
- # @example Yield a proc
90
- # obj.cmdx_yield(-> { Time.now }) #=> 2023-01-01 12:00:00 UTC
91
- def cmdx_yield(key, ...)
92
- if key.is_a?(Symbol) || key.is_a?(String)
93
- return key unless respond_to?(key, true)
94
-
95
- send(key, ...)
96
- elsif is_a?(Hash) || key.is_a?(Proc)
97
- cmdx_try(key, ...)
98
- else
99
- key
100
- end
101
- end
102
-
103
- # Invokes the object if it responds to :call, otherwise returns the object itself.
104
- # Useful for handling both callable and non-callable objects uniformly.
105
- #
106
- # @param args [Array] arguments to pass to the call method
107
- #
108
- # @return [Object] the result of calling the object, or the object itself if not callable
109
- #
110
- # @example Invoke a proc
111
- # proc { "hello" }.cmdx_call #=> "hello"
112
- #
113
- # @example Invoke a non-callable object
114
- # "hello".cmdx_call #=> "hello"
115
- def cmdx_call(...)
116
- return self unless respond_to?(:call)
117
-
118
- call(...)
119
- end
120
-
121
- end
122
- end
123
- end
124
-
125
- Object.include(CMDx::CoreExt::ObjectExtensions)
@@ -1,122 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
- # Thread-safe correlation ID management for distributed tracing and request tracking.
5
- #
6
- # Correlator provides functionality to generate, store, and manage correlation IDs
7
- # across thread boundaries for request tracing, logging correlation, and distributed
8
- # system monitoring. Correlation IDs are stored in thread-local storage to ensure
9
- # thread safety and isolation between concurrent operations.
10
- module Correlator
11
-
12
- THREAD_KEY = :cmdx_correlation_id
13
-
14
- module_function
15
-
16
- # Generates a new correlation ID using the best available UUID algorithm.
17
- #
18
- # Attempts to use UUID v7 (time-ordered) if available in Ruby 3.3+, otherwise
19
- # falls back to standard UUID v4. UUID v7 provides better database indexing
20
- # performance and natural time-based ordering for correlation tracking.
21
- #
22
- # @return [String] a new UUID correlation ID
23
- #
24
- # @example Generate a correlation ID
25
- # Correlator.generate #=> "01234567-89ab-7def-0123-456789abcdef"
26
- #
27
- # @example Using the generated ID for logging
28
- # correlation_id = Correlator.generate
29
- # logger.info "Request started", correlation_id: correlation_id
30
- def generate
31
- return SecureRandom.uuid_v7 if SecureRandom.respond_to?(:uuid_v7)
32
-
33
- SecureRandom.uuid
34
- end
35
-
36
- # Retrieves the current correlation ID for the active thread.
37
- #
38
- # Returns the correlation ID that has been set for the current thread's
39
- # execution context. Returns nil if no correlation ID has been established
40
- # for the current thread.
41
- #
42
- # @return [String, nil] the current thread's correlation ID, or nil if not set
43
- #
44
- # @example Get current correlation ID
45
- # Correlator.id #=> "01234567-89ab-7def-0123-456789abcdef"
46
- def id
47
- Thread.current[THREAD_KEY]
48
- end
49
-
50
- # Sets the correlation ID for the current thread.
51
- #
52
- # Establishes a correlation ID in thread-local storage that will be
53
- # accessible to all operations within the current thread's execution
54
- # context. This ID will persist until explicitly changed or cleared.
55
- #
56
- # @param value [String, Symbol] the correlation ID to set for this thread
57
- #
58
- # @return [String, Symbol] the assigned correlation ID value
59
- #
60
- # @example Set a custom correlation ID
61
- # Correlator.id = "custom-trace-123"
62
- #
63
- # @example Set a generated correlation ID
64
- # Correlator.id = Correlator.generate
65
- def id=(value)
66
- Thread.current[THREAD_KEY] = value
67
- end
68
-
69
- # Clears the correlation ID for the current thread.
70
- #
71
- # Removes the correlation ID from thread-local storage, effectively
72
- # resetting the correlation context for the current thread. Useful
73
- # for cleanup between request processing or test scenarios.
74
- #
75
- # @return [nil] always returns nil after clearing
76
- #
77
- # @example Clear correlation ID
78
- # Correlator.clear
79
- # Correlator.id #=> nil
80
- def clear
81
- Thread.current[THREAD_KEY] = nil
82
- end
83
-
84
- # Temporarily sets a correlation ID for the duration of a block execution.
85
- #
86
- # Establishes a correlation ID context for the provided block, automatically
87
- # restoring the previous correlation ID when the block completes. This ensures
88
- # proper correlation ID isolation for nested operations or temporary contexts.
89
- #
90
- # @param value [String, Symbol] the temporary correlation ID to use during block execution
91
- #
92
- # @return [Object] the return value of the executed block
93
- #
94
- # @raise [TypeError] if the provided value is not a String or Symbol
95
- #
96
- # @example Use temporary correlation ID
97
- # Correlator.use("temp-id-123") do
98
- # logger.info "Processing with temporary ID"
99
- # perform_operation
100
- # end
101
- #
102
- # @example Nested correlation contexts
103
- # Correlator.id = "parent-id"
104
- # Correlator.use("child-id") do
105
- # puts Correlator.id #=> "child-id"
106
- # end
107
- # puts Correlator.id #=> "parent-id"
108
- def use(value)
109
- unless value.is_a?(String) || value.is_a?(Symbol)
110
- raise TypeError,
111
- "must be a String or Symbol"
112
- end
113
-
114
- previous_id = id
115
- self.id = value
116
- yield
117
- ensure
118
- self.id = previous_id
119
- end
120
-
121
- end
122
- end
data/lib/cmdx/error.rb DELETED
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CMDx
4
-
5
- # Base exception class for all CMDx-related errors.
6
- #
7
- # This serves as the root exception class for all errors raised by the CMDx
8
- # framework. It inherits from StandardError and provides a common base for
9
- # handling CMDx-specific exceptions.
10
- Error = Class.new(StandardError)
11
-
12
- # Raised when parameter coercion fails during task execution.
13
- #
14
- # This error occurs when a parameter value cannot be converted to the expected
15
- # type using the registered coercion handlers. It indicates that the provided
16
- # value is incompatible with the parameter's defined type.
17
- CoercionError = Class.new(Error)
18
-
19
- # Raised when a deprecated task is used.
20
- #
21
- # This error occurs when a deprecated task is called. It indicates that the
22
- # task is no longer supported and should be replaced with a newer alternative.
23
- DeprecationError = Class.new(Error)
24
-
25
- # Raised when an abstract method is called without being implemented.
26
- #
27
- # This error occurs when a subclass fails to implement required abstract methods
28
- # such as call methods in validators, callbacks, or middleware. It indicates
29
- # incomplete implementation of required functionality.
30
- UndefinedCallError = Class.new(Error)
31
-
32
- # Raised when attempting to use an unregistered callback.
33
- #
34
- # This error occurs when trying to reference a callback that hasn't been
35
- # registered in the callback registry. It indicates that the callback name
36
- # is not recognized or was misspelled.
37
- UnknownCallbackError = Class.new(Error)
38
-
39
- # Raised when attempting to use an unregistered coercion type.
40
- #
41
- # This error occurs when trying to use a parameter type that doesn't have
42
- # a corresponding coercion handler registered. It indicates that the specified
43
- # type is not supported by the coercion system.
44
- UnknownCoercionError = Class.new(Error)
45
-
46
- # Raised when attempting to use an unregistered validator.
47
- #
48
- # This error occurs when trying to reference a validator that hasn't been
49
- # registered in the validator registry. It indicates that the validator name
50
- # is not recognized or was misspelled.
51
- UnknownValidatorError = Class.new(Error)
52
-
53
- # Raised when parameter validation fails during task execution.
54
- #
55
- # This error occurs when a parameter value doesn't meet the validation criteria
56
- # defined by the validator. It indicates that the provided value violates
57
- # business rules or data integrity constraints.
58
- ValidationError = Class.new(Error)
59
-
60
- end