cmdx 1.0.0 → 1.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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/prompts/rspec.md +20 -0
  3. data/.cursor/prompts/yardoc.md +8 -0
  4. data/.rubocop.yml +5 -0
  5. data/CHANGELOG.md +101 -49
  6. data/README.md +2 -1
  7. data/docs/ai_prompts.md +10 -0
  8. data/docs/basics/call.md +11 -2
  9. data/docs/basics/chain.md +10 -1
  10. data/docs/basics/context.md +9 -0
  11. data/docs/basics/setup.md +9 -0
  12. data/docs/callbacks.md +14 -37
  13. data/docs/configuration.md +68 -27
  14. data/docs/getting_started.md +11 -0
  15. data/docs/internationalization.md +148 -0
  16. data/docs/interruptions/exceptions.md +10 -1
  17. data/docs/interruptions/faults.md +11 -2
  18. data/docs/interruptions/halt.md +9 -0
  19. data/docs/logging.md +14 -4
  20. data/docs/middlewares.md +53 -43
  21. data/docs/outcomes/result.md +9 -0
  22. data/docs/outcomes/states.md +9 -0
  23. data/docs/outcomes/statuses.md +9 -0
  24. data/docs/parameters/coercions.md +58 -38
  25. data/docs/parameters/defaults.md +10 -1
  26. data/docs/parameters/definitions.md +9 -0
  27. data/docs/parameters/namespacing.md +9 -0
  28. data/docs/parameters/validations.md +8 -67
  29. data/docs/testing.md +22 -13
  30. data/docs/tips_and_tricks.md +9 -0
  31. data/docs/workflows.md +14 -4
  32. data/lib/cmdx/.DS_Store +0 -0
  33. data/lib/cmdx/callback.rb +36 -56
  34. data/lib/cmdx/callback_registry.rb +82 -73
  35. data/lib/cmdx/chain.rb +65 -122
  36. data/lib/cmdx/chain_inspector.rb +22 -115
  37. data/lib/cmdx/chain_serializer.rb +17 -148
  38. data/lib/cmdx/coercion.rb +49 -0
  39. data/lib/cmdx/coercion_registry.rb +94 -0
  40. data/lib/cmdx/coercions/array.rb +18 -36
  41. data/lib/cmdx/coercions/big_decimal.rb +21 -33
  42. data/lib/cmdx/coercions/boolean.rb +21 -40
  43. data/lib/cmdx/coercions/complex.rb +18 -31
  44. data/lib/cmdx/coercions/date.rb +20 -39
  45. data/lib/cmdx/coercions/date_time.rb +22 -39
  46. data/lib/cmdx/coercions/float.rb +19 -32
  47. data/lib/cmdx/coercions/hash.rb +22 -41
  48. data/lib/cmdx/coercions/integer.rb +20 -33
  49. data/lib/cmdx/coercions/rational.rb +20 -32
  50. data/lib/cmdx/coercions/string.rb +23 -31
  51. data/lib/cmdx/coercions/time.rb +24 -40
  52. data/lib/cmdx/coercions/virtual.rb +14 -31
  53. data/lib/cmdx/configuration.rb +57 -171
  54. data/lib/cmdx/context.rb +22 -165
  55. data/lib/cmdx/core_ext/hash.rb +42 -67
  56. data/lib/cmdx/core_ext/module.rb +35 -79
  57. data/lib/cmdx/core_ext/object.rb +63 -98
  58. data/lib/cmdx/correlator.rb +40 -156
  59. data/lib/cmdx/error.rb +37 -202
  60. data/lib/cmdx/errors.rb +165 -202
  61. data/lib/cmdx/fault.rb +55 -158
  62. data/lib/cmdx/faults.rb +26 -137
  63. data/lib/cmdx/immutator.rb +22 -109
  64. data/lib/cmdx/lazy_struct.rb +103 -187
  65. data/lib/cmdx/log_formatters/json.rb +14 -40
  66. data/lib/cmdx/log_formatters/key_value.rb +14 -40
  67. data/lib/cmdx/log_formatters/line.rb +14 -48
  68. data/lib/cmdx/log_formatters/logstash.rb +14 -57
  69. data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
  70. data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
  71. data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
  72. data/lib/cmdx/log_formatters/raw.rb +19 -49
  73. data/lib/cmdx/logger.rb +20 -82
  74. data/lib/cmdx/logger_ansi.rb +18 -75
  75. data/lib/cmdx/logger_serializer.rb +24 -114
  76. data/lib/cmdx/middleware.rb +38 -60
  77. data/lib/cmdx/middleware_registry.rb +81 -77
  78. data/lib/cmdx/middlewares/correlate.rb +41 -226
  79. data/lib/cmdx/middlewares/timeout.rb +46 -185
  80. data/lib/cmdx/parameter.rb +120 -198
  81. data/lib/cmdx/parameter_evaluator.rb +231 -0
  82. data/lib/cmdx/parameter_inspector.rb +25 -56
  83. data/lib/cmdx/parameter_registry.rb +59 -84
  84. data/lib/cmdx/parameter_serializer.rb +23 -74
  85. data/lib/cmdx/railtie.rb +24 -107
  86. data/lib/cmdx/result.rb +254 -260
  87. data/lib/cmdx/result_ansi.rb +19 -85
  88. data/lib/cmdx/result_inspector.rb +27 -68
  89. data/lib/cmdx/result_logger.rb +18 -81
  90. data/lib/cmdx/result_serializer.rb +28 -132
  91. data/lib/cmdx/rspec/matchers.rb +28 -0
  92. data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
  93. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
  94. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
  95. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
  96. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
  97. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
  98. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
  99. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
  100. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
  101. data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
  102. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
  103. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
  104. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
  105. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
  106. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
  107. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
  108. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
  109. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
  110. data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
  111. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
  112. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
  113. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
  114. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
  115. data/lib/cmdx/task.rb +213 -425
  116. data/lib/cmdx/task_deprecator.rb +55 -0
  117. data/lib/cmdx/task_processor.rb +245 -0
  118. data/lib/cmdx/task_serializer.rb +22 -70
  119. data/lib/cmdx/utils/ansi_color.rb +13 -89
  120. data/lib/cmdx/utils/log_timestamp.rb +13 -42
  121. data/lib/cmdx/utils/monotonic_runtime.rb +13 -63
  122. data/lib/cmdx/utils/name_affix.rb +21 -71
  123. data/lib/cmdx/validator.rb +48 -0
  124. data/lib/cmdx/validator_registry.rb +86 -0
  125. data/lib/cmdx/validators/exclusion.rb +55 -94
  126. data/lib/cmdx/validators/format.rb +31 -85
  127. data/lib/cmdx/validators/inclusion.rb +65 -110
  128. data/lib/cmdx/validators/length.rb +117 -133
  129. data/lib/cmdx/validators/numeric.rb +123 -130
  130. data/lib/cmdx/validators/presence.rb +38 -79
  131. data/lib/cmdx/version.rb +1 -7
  132. data/lib/cmdx/workflow.rb +46 -339
  133. data/lib/cmdx.rb +1 -1
  134. data/lib/generators/cmdx/install_generator.rb +14 -31
  135. data/lib/generators/cmdx/task_generator.rb +39 -55
  136. data/lib/generators/cmdx/templates/install.rb +61 -11
  137. data/lib/generators/cmdx/workflow_generator.rb +41 -66
  138. data/lib/locales/ar.yml +35 -0
  139. data/lib/locales/cs.yml +35 -0
  140. data/lib/locales/da.yml +35 -0
  141. data/lib/locales/de.yml +35 -0
  142. data/lib/locales/el.yml +35 -0
  143. data/lib/locales/en.yml +19 -20
  144. data/lib/locales/es.yml +19 -20
  145. data/lib/locales/fi.yml +35 -0
  146. data/lib/locales/fr.yml +35 -0
  147. data/lib/locales/he.yml +35 -0
  148. data/lib/locales/hi.yml +35 -0
  149. data/lib/locales/it.yml +35 -0
  150. data/lib/locales/ja.yml +35 -0
  151. data/lib/locales/ko.yml +35 -0
  152. data/lib/locales/nl.yml +35 -0
  153. data/lib/locales/no.yml +35 -0
  154. data/lib/locales/pl.yml +35 -0
  155. data/lib/locales/pt.yml +35 -0
  156. data/lib/locales/ru.yml +35 -0
  157. data/lib/locales/sv.yml +35 -0
  158. data/lib/locales/th.yml +35 -0
  159. data/lib/locales/tr.yml +35 -0
  160. data/lib/locales/vi.yml +35 -0
  161. data/lib/locales/zh.yml +35 -0
  162. metadata +57 -8
  163. data/lib/cmdx/parameter_validator.rb +0 -81
  164. data/lib/cmdx/parameter_value.rb +0 -244
  165. data/lib/cmdx/parameters_inspector.rb +0 -72
  166. data/lib/cmdx/parameters_serializer.rb +0 -115
  167. data/lib/cmdx/rspec/result_matchers.rb +0 -917
  168. data/lib/cmdx/rspec/task_matchers.rb +0 -570
  169. data/lib/cmdx/validators/custom.rb +0 -102
data/lib/cmdx/error.rb CHANGED
@@ -2,224 +2,59 @@
2
2
 
3
3
  module CMDx
4
4
 
5
- ##
6
- # Base exception class for all CMDx-specific errors.
7
- # All other CMDx exceptions inherit from this class, providing a common
8
- # hierarchy for error handling and rescue operations.
5
+ # Base exception class for all CMDx-related errors.
9
6
  #
10
- # This allows for catching all CMDx-related exceptions with a single rescue clause
11
- # while still maintaining specific error types for detailed error handling.
12
- #
13
- # @example Catching all CMDx errors
14
- # begin
15
- # ProcessOrderTask.call(invalid_params)
16
- # rescue CMDx::Error => e
17
- # logger.error "CMDx error occurred: #{e.message}"
18
- # end
19
- #
20
- # @example Specific error handling
21
- # begin
22
- # ProcessOrderTask.call(order_id: "invalid")
23
- # rescue CMDx::CoercionError => e
24
- # # Handle type coercion failures
25
- # rescue CMDx::ValidationError => e
26
- # # Handle validation failures
27
- # rescue CMDx::Error => e
28
- # # Handle any other CMDx errors
29
- # end
30
- #
31
- # @see StandardError Ruby's standard error base class
32
- # @since 1.0.0
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.
33
10
  Error = Class.new(StandardError)
34
11
 
35
- ##
36
- # Raised when a value cannot be coerced to the specified type.
37
- # This exception occurs during parameter processing when type coercion fails,
38
- # typically due to incompatible data formats or invalid input values.
39
- #
40
- # CoercionError is raised by the various coercion modules when they encounter
41
- # values that cannot be converted to the target type. Each coercion module
42
- # provides specific error messages indicating the expected type and the
43
- # problematic value.
44
- #
45
- # @example Integer coercion failure
46
- # class MyTask < CMDx::Task
47
- # required :count, type: :integer
48
- # end
49
- #
50
- # # This will raise CoercionError during parameter processing
51
- # MyTask.call(count: "not_a_number")
52
- # # => CMDx::CoercionError: could not coerce into an integer
53
- #
54
- # @example Date coercion failure
55
- # class ScheduleTask < CMDx::Task
56
- # required :due_date, type: :date
57
- # end
58
- #
59
- # ScheduleTask.call(due_date: "invalid_date")
60
- # # => CMDx::CoercionError: could not coerce into a date
12
+ # Raised when parameter coercion fails during task execution.
61
13
  #
62
- # @example Handling coercion errors
63
- # begin
64
- # MyTask.call(count: "invalid")
65
- # rescue CMDx::CoercionError => e
66
- # # Log the coercion failure and provide user-friendly message
67
- # logger.warn "Invalid input format: #{e.message}"
68
- # render json: { error: "Please provide a valid number" }
69
- # end
70
- #
71
- # @see Parameter Parameter type definitions and coercion
72
- # @see ParameterValue Parameter value processing and coercion
73
- # @since 1.0.0
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.
74
17
  CoercionError = Class.new(Error)
75
18
 
76
- ##
77
- # Raised when a task class doesn't implement the required `call` method.
78
- # This exception enforces the CMDx contract that all task classes must
79
- # provide a `call` method containing their business logic.
80
- #
81
- # This error typically occurs during development when creating new task
82
- # classes that inherit from CMDx::Task but forget to implement the
83
- # abstract `call` method.
84
- #
85
- # @example Missing call method
86
- # class IncompleteTask < CMDx::Task
87
- # required :data, type: :string
88
- # # Missing call method implementation
89
- # end
90
- #
91
- # IncompleteTask.call(data: "test")
92
- # # => CMDx::UndefinedCallError: call method not defined in IncompleteTask
19
+ # Raised when a deprecated task is used.
93
20
  #
94
- # @example Proper task implementation
95
- # class CompleteTask < CMDx::Task
96
- # required :data, type: :string
97
- #
98
- # def call
99
- # # Business logic implementation
100
- # context.result = process(data)
101
- # end
102
- # end
103
- #
104
- # @example Handling undefined call errors
105
- # begin
106
- # SomeTask.call(params)
107
- # rescue CMDx::UndefinedCallError => e
108
- # # This should typically only happen during development
109
- # logger.error "Task implementation incomplete: #{e.message}"
110
- # raise # Re-raise as this is a programming error
111
- # end
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.
112
26
  #
113
- # @see Task Task base class and call method requirement
114
- # @see Workflow Workflow base class and call method requirement
115
- # @since 1.0.0
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.
116
30
  UndefinedCallError = Class.new(Error)
117
31
 
118
- ##
119
- # Raised when an unknown or unsupported coercion type is specified.
120
- # This exception occurs when parameter definitions reference type coercions
121
- # that don't exist or aren't registered in the CMDx coercion system.
122
- #
123
- # This error helps catch typos in type specifications and ensures that
124
- # only supported data types are used in parameter definitions.
32
+ # Raised when attempting to use an unregistered callback.
125
33
  #
126
- # @example Unknown type specification
127
- # class MyTask < CMDx::Task
128
- # required :value, type: :unknown_type # Typo or unsupported type
129
- # end
130
- #
131
- # MyTask.call(value: "test")
132
- # # => CMDx::UnknownCoercionError: unknown coercion unknown_type
133
- #
134
- # @example Common typos
135
- # class TaskWithTypo < CMDx::Task
136
- # required :count, type: :integr # Should be :integer
137
- # required :flag, type: :bool # Should be :boolean
138
- # required :data, type: :json # Should be :hash
139
- # end
140
- #
141
- # @example Supported types
142
- # class ProperTask < CMDx::Task
143
- # required :id, type: :integer
144
- # required :active, type: :boolean
145
- # required :metadata, type: :hash
146
- # required :tags, type: :array
147
- # required :name, type: :string
148
- # required :score, type: :float
149
- # required :created_at, type: :date_time
150
- # end
151
- #
152
- # @example Handling unknown coercion errors
153
- # begin
154
- # MyTask.call(params)
155
- # rescue CMDx::UnknownCoercionError => e
156
- # # This indicates a programming error in parameter definition
157
- # logger.error "Invalid type specification: #{e.message}"
158
- # raise # Re-raise as this should be fixed in code
159
- # end
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.
160
40
  #
161
- # @see Parameter Parameter type definitions
162
- # @see ParameterValue Type coercion processing
163
- # @since 1.0.0
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.
164
44
  UnknownCoercionError = Class.new(Error)
165
45
 
166
- ##
167
- # Raised when a parameter value fails validation rules.
168
- # This exception occurs during parameter processing when values don't meet
169
- # the specified validation criteria, such as format requirements, length
170
- # constraints, or custom validation logic.
171
- #
172
- # ValidationError provides detailed feedback about why validation failed,
173
- # helping developers and users understand what corrections are needed.
174
- #
175
- # @example Presence validation failure
176
- # class CreateUserTask < CMDx::Task
177
- # required :email, type: :string, presence: true
178
- # end
179
- #
180
- # CreateUserTask.call(email: "")
181
- # # => CMDx::ValidationError: cannot be empty
46
+ # Raised when attempting to use an unregistered validator.
182
47
  #
183
- # @example Format validation failure
184
- # class ValidateEmailTask < CMDx::Task
185
- # required :email, type: :string, format: { with: /@/ }
186
- # end
187
- #
188
- # ValidateEmailTask.call(email: "invalid-email")
189
- # # => CMDx::ValidationError: is an invalid format
190
- #
191
- # @example Length validation failure
192
- # class SetPasswordTask < CMDx::Task
193
- # required :password, type: :string, length: { min: 8 }
194
- # end
195
- #
196
- # SetPasswordTask.call(password: "short")
197
- # # => CMDx::ValidationError: length must be at least 8
198
- #
199
- # @example Custom validation failure
200
- # class ProcessOrderTask < CMDx::Task
201
- # required :quantity, type: :integer, custom: -> (val) { val > 0 }
202
- # end
203
- #
204
- # ProcessOrderTask.call(quantity: -1)
205
- # # => CMDx::ValidationError: is not valid
206
- #
207
- # @example Handling validation errors
208
- # begin
209
- # CreateUserTask.call(email: "", password: "short")
210
- # rescue CMDx::ValidationError => e
211
- # # Provide user-friendly feedback
212
- # render json: {
213
- # error: "Validation failed",
214
- # message: e.message,
215
- # field: extract_field_from_context(e)
216
- # }
217
- # end
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.
218
54
  #
219
- # @see Parameter Parameter validation options
220
- # @see ParameterValue Validation processing
221
- # @see Validators Validation modules (Presence, Format, Length, etc.)
222
- # @since 1.0.0
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.
223
58
  ValidationError = Class.new(Error)
224
59
 
225
60
  end