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
@@ -2,232 +2,157 @@
2
2
 
3
3
  module CMDx
4
4
  module Validators
5
- # Validator class for validating numeric values with various constraints.
5
+ # Validates numeric values against various constraints and ranges
6
6
  #
7
7
  # This validator ensures that numeric values meet specified criteria such as
8
- # being within a range, having minimum/maximum values, or matching exact values.
9
- # It supports both inclusive and exclusive range validation, as well as discrete
10
- # value matching and rejection.
11
- class Numeric < Validator
12
-
13
- # Validates that the given numeric value meets the specified constraints.
14
- #
15
- # @param value [Numeric] the numeric value to validate
16
- # @param options [Hash] validation options containing numeric configuration
17
- # @option options [Hash] :numeric numeric validation configuration
18
- # @option options [Range] :numeric.within the range the value must be within
19
- # @option options [Range] :numeric.not_within the range the value must not be within
20
- # @option options [Range] :numeric.in alias for :within
21
- # @option options [Range] :numeric.not_in alias for :not_within
22
- # @option options [Numeric] :numeric.min the minimum allowed value (can be combined with :max)
23
- # @option options [Numeric] :numeric.max the maximum allowed value (can be combined with :min)
24
- # @option options [Numeric] :numeric.is the exact value required
25
- # @option options [Numeric] :numeric.is_not the exact value that is not allowed
26
- # @option options [String] :numeric.message custom error message for any validation
27
- # @option options [String] :numeric.within_message custom error message for within validation
28
- # @option options [String] :numeric.in_message alias for :within_message
29
- # @option options [String] :numeric.not_within_message custom error message for not_within validation
30
- # @option options [String] :numeric.not_in_message alias for :not_within_message
31
- # @option options [String] :numeric.min_message custom error message for min validation
32
- # @option options [String] :numeric.max_message custom error message for max validation
33
- # @option options [String] :numeric.is_message custom error message for is validation
34
- # @option options [String] :numeric.is_not_message custom error message for is_not validation
35
- #
36
- # @return [void]
37
- #
38
- # @raise [ValidationError] if the value doesn't meet the specified constraints
39
- # @raise [ArgumentError] if no known numeric validator options are provided
40
- #
41
- # @example Range validation
42
- # Validators::Numeric.call(5, numeric: { within: 1..10 })
43
- # #=> nil (no error raised)
44
- #
45
- # @example Range exclusion
46
- # Validators::Numeric.call(5, numeric: { not_within: 1..10 })
47
- # # raises ValidationError: "must not be within 1 and 10"
48
- #
49
- # @example Min/max validation
50
- # Validators::Numeric.call(15, numeric: { min: 10, max: 20 })
51
- # #=> nil (no error raised)
52
- #
53
- # @example Minimum value validation
54
- # Validators::Numeric.call(5, numeric: { min: 10 })
55
- # # raises ValidationError: "must be at least 10"
56
- #
57
- # @example Exact value validation
58
- # Validators::Numeric.call(42, numeric: { is: 42 })
59
- # #=> nil (no error raised)
60
- #
61
- # @example Custom error message
62
- # Validators::Numeric.call(5, numeric: { min: 10, message: "Age must be at least %{min}" })
63
- # # raises ValidationError: "Age must be at least 10"
8
+ # minimum/maximum bounds, exact matches, or range inclusions. It supports
9
+ # both inclusive and exclusive range validations with customizable error messages.
10
+ module Numeric
11
+
12
+ extend self
13
+
14
+ # Validates a numeric value against the specified options
15
+ #
16
+ # @param value [Numeric] The numeric value to validate
17
+ # @param options [Hash] Validation configuration options
18
+ # @option options [Range] :within Range that the value must fall within (inclusive)
19
+ # @option options [Range] :not_within Range that the value must not fall within
20
+ # @option options [Range] :in Alias for :within option
21
+ # @option options [Range] :not_in Alias for :not_within option
22
+ # @option options [Numeric] :min Minimum allowed value (inclusive)
23
+ # @option options [Numeric] :max Maximum allowed value (inclusive)
24
+ # @option options [Numeric] :is Exact value that must match
25
+ # @option options [Numeric] :is_not Value that must not match
26
+ # @option options [String] :message Custom error message template
27
+ # @option options [String] :within_message Custom message for range validations
28
+ # @option options [String] :not_within_message Custom message for exclusion validations
29
+ # @option options [String] :min_message Custom message for minimum validation
30
+ # @option options [String] :max_message Custom message for maximum validation
31
+ # @option options [String] :is_message Custom message for exact match validation
32
+ # @option options [String] :is_not_message Custom message for exclusion validation
33
+ #
34
+ # @return [nil] Returns nil if validation passes
35
+ #
36
+ # @raise [ValidationError] When the value fails validation
37
+ # @raise [ArgumentError] When unknown validator options are provided
38
+ #
39
+ # @example Validate value within a range
40
+ # Numeric.call(5, within: 1..10)
41
+ # # => nil (validation passes)
42
+ # @example Validate minimum and maximum bounds
43
+ # Numeric.call(15, min: 10, max: 20)
44
+ # # => nil (validation passes)
45
+ # @example Validate exact value match
46
+ # Numeric.call(42, is: 42)
47
+ # # => nil (validation passes)
48
+ # @example Validate value exclusion
49
+ # Numeric.call(5, not_in: 1..10)
50
+ # # => nil (validation passes - 5 is not in 1..10)
64
51
  def call(value, options = {})
65
52
  case options
66
- in { within: within }
53
+ in within:
67
54
  raise_within_validation_error!(within.begin, within.end, options) unless within.cover?(value)
68
- in { not_within: not_within }
55
+ in not_within:
69
56
  raise_not_within_validation_error!(not_within.begin, not_within.end, options) if not_within.cover?(value)
70
- in { in: yn }
71
- raise_within_validation_error!(yn.begin, yn.end, options) unless yn.cover?(value)
72
- in { not_in: not_in }
57
+ in in: xin
58
+ raise_within_validation_error!(xin.begin, xin.end, options) unless xin.cover?(value)
59
+ in not_in:
73
60
  raise_not_within_validation_error!(not_in.begin, not_in.end, options) if not_in.cover?(value)
74
- in { min: min, max: max }
61
+ in min:, max:
75
62
  raise_within_validation_error!(min, max, options) unless value.between?(min, max)
76
- in { min: min }
63
+ in min:
77
64
  raise_min_validation_error!(min, options) unless min <= value
78
- in { max: max }
65
+ in max:
79
66
  raise_max_validation_error!(max, options) unless value <= max
80
- in { is: is }
67
+ in is:
81
68
  raise_is_validation_error!(is, options) unless value == is
82
- in { is_not: is_not }
69
+ in is_not:
83
70
  raise_is_not_validation_error!(is_not, options) if value == is_not
84
71
  else
85
- raise ArgumentError, "no known numeric validator options given"
72
+ raise ArgumentError, "unknown numeric validator options given"
86
73
  end
87
74
  end
88
75
 
89
76
  private
90
77
 
91
- # Raises a validation error for within/range validation.
78
+ # Raises validation error for range inclusion validation
92
79
  #
93
- # @param min [Numeric] the minimum value of the range
94
- # @param max [Numeric] the maximum value of the range
95
- # @param options [Hash] validation options
80
+ # @param min [Numeric] The minimum value of the allowed range
81
+ # @param max [Numeric] The maximum value of the allowed range
82
+ # @param options [Hash] Validation options containing custom messages
96
83
  #
97
- # @return [void]
98
- #
99
- # @raise [ValidationError] always raised with appropriate message
100
- #
101
- # @example
102
- # raise_within_validation_error!(1, 10, {})
103
- # # raises ValidationError: "must be within 1 and 10"
84
+ # @raise [ValidationError] With appropriate error message
104
85
  def raise_within_validation_error!(min, max, options)
105
86
  message = options[:within_message] || options[:in_message] || options[:message]
106
87
  message %= { min:, max: } unless message.nil?
107
88
 
108
- raise ValidationError, message || I18n.t(
109
- "cmdx.validators.numeric.within",
110
- min:,
111
- max:,
112
- default: "must be within #{min} and #{max}"
113
- )
89
+ raise ValidationError, message || Locale.t("cmdx.validators.numeric.within", min:, max:)
114
90
  end
115
91
 
116
- # Raises a validation error for not_within/range exclusion validation.
117
- #
118
- # @param min [Numeric] the minimum value of the excluded range
119
- # @param max [Numeric] the maximum value of the excluded range
120
- # @param options [Hash] validation options
92
+ # Raises validation error for range exclusion validation
121
93
  #
122
- # @return [void]
94
+ # @param min [Numeric] The minimum value of the excluded range
95
+ # @param max [Numeric] The maximum value of the excluded range
96
+ # @param options [Hash] Validation options containing custom messages
123
97
  #
124
- # @raise [ValidationError] always raised with appropriate message
125
- #
126
- # @example
127
- # raise_not_within_validation_error!(1, 10, {})
128
- # # raises ValidationError: "must not be within 1 and 10"
98
+ # @raise [ValidationError] With appropriate error message
129
99
  def raise_not_within_validation_error!(min, max, options)
130
100
  message = options[:not_within_message] || options[:not_in_message] || options[:message]
131
101
  message %= { min:, max: } unless message.nil?
132
102
 
133
- raise ValidationError, message || I18n.t(
134
- "cmdx.validators.numeric.not_within",
135
- min:,
136
- max:,
137
- default: "must not be within #{min} and #{max}"
138
- )
103
+ raise ValidationError, message || Locale.t("cmdx.validators.numeric.not_within", min:, max:)
139
104
  end
140
105
 
141
- # Raises a validation error for minimum value validation.
142
- #
143
- # @param min [Numeric] the minimum allowed value
144
- # @param options [Hash] validation options
145
- #
146
- # @return [void]
106
+ # Raises validation error for minimum value validation
147
107
  #
148
- # @raise [ValidationError] always raised with appropriate message
108
+ # @param min [Numeric] The minimum allowed value
109
+ # @param options [Hash] Validation options containing custom messages
149
110
  #
150
- # @example
151
- # raise_min_validation_error!(10, {})
152
- # # raises ValidationError: "must be at least 10"
111
+ # @raise [ValidationError] With appropriate error message
153
112
  def raise_min_validation_error!(min, options)
154
113
  message = options[:min_message] || options[:message]
155
114
  message %= { min: } unless message.nil?
156
115
 
157
- raise ValidationError, message || I18n.t(
158
- "cmdx.validators.numeric.min",
159
- min:,
160
- default: "must be at least #{min}"
161
- )
116
+ raise ValidationError, message || Locale.t("cmdx.validators.numeric.min", min:)
162
117
  end
163
118
 
164
- # Raises a validation error for maximum value validation.
119
+ # Raises validation error for maximum value validation
165
120
  #
166
- # @param max [Numeric] the maximum allowed value
167
- # @param options [Hash] validation options
121
+ # @param max [Numeric] The maximum allowed value
122
+ # @param options [Hash] Validation options containing custom messages
168
123
  #
169
- # @return [void]
170
- #
171
- # @raise [ValidationError] always raised with appropriate message
172
- #
173
- # @example
174
- # raise_max_validation_error!(100, {})
175
- # # raises ValidationError: "must be at most 100"
124
+ # @raise [ValidationError] With appropriate error message
176
125
  def raise_max_validation_error!(max, options)
177
126
  message = options[:max_message] || options[:message]
178
127
  message %= { max: } unless message.nil?
179
128
 
180
- raise ValidationError, message || I18n.t(
181
- "cmdx.validators.numeric.max",
182
- max:,
183
- default: "must be at most #{max}"
184
- )
129
+ raise ValidationError, message || Locale.t("cmdx.validators.numeric.max", max:)
185
130
  end
186
131
 
187
- # Raises a validation error for exact value validation.
188
- #
189
- # @param is [Numeric] the exact value required
190
- # @param options [Hash] validation options
132
+ # Raises validation error for exact value match validation
191
133
  #
192
- # @return [void]
134
+ # @param is [Numeric] The exact value that was expected
135
+ # @param options [Hash] Validation options containing custom messages
193
136
  #
194
- # @raise [ValidationError] always raised with appropriate message
195
- #
196
- # @example
197
- # raise_is_validation_error!(42, {})
198
- # # raises ValidationError: "must be 42"
137
+ # @raise [ValidationError] With appropriate error message
199
138
  def raise_is_validation_error!(is, options)
200
139
  message = options[:is_message] || options[:message]
201
140
  message %= { is: } unless message.nil?
202
141
 
203
- raise ValidationError, message || I18n.t(
204
- "cmdx.validators.numeric.is",
205
- is:,
206
- default: "must be #{is}"
207
- )
142
+ raise ValidationError, message || Locale.t("cmdx.validators.numeric.is", is:)
208
143
  end
209
144
 
210
- # Raises a validation error for exact value exclusion validation.
211
- #
212
- # @param is_not [Numeric] the exact value that is not allowed
213
- # @param options [Hash] validation options
214
- #
215
- # @return [void]
145
+ # Raises validation error for value exclusion validation
216
146
  #
217
- # @raise [ValidationError] always raised with appropriate message
147
+ # @param is_not [Numeric] The value that was not allowed
148
+ # @param options [Hash] Validation options containing custom messages
218
149
  #
219
- # @example
220
- # raise_is_not_validation_error!(0, {})
221
- # # raises ValidationError: "must not be 0"
150
+ # @raise [ValidationError] With appropriate error message
222
151
  def raise_is_not_validation_error!(is_not, options)
223
152
  message = options[:is_not_message] || options[:message]
224
153
  message %= { is_not: } unless message.nil?
225
154
 
226
- raise ValidationError, message || I18n.t(
227
- "cmdx.validators.numeric.is_not",
228
- is_not:,
229
- default: "must not be #{is_not}"
230
- )
155
+ raise ValidationError, message || Locale.t("cmdx.validators.numeric.is_not", is_not:)
231
156
  end
232
157
 
233
158
  end
@@ -2,54 +2,44 @@
2
2
 
3
3
  module CMDx
4
4
  module Validators
5
- # Validator class for ensuring values are present (not empty or nil).
5
+ # Validates that a value is present and not empty
6
6
  #
7
- # This validator checks that a value is not empty, blank, or nil. For strings,
8
- # it validates that there are non-whitespace characters. For objects that respond
9
- # to empty?, it ensures they are not empty. For all other objects, it validates
10
- # they are not nil.
11
- class Presence < Validator
7
+ # This validator ensures that the given value exists and contains meaningful content.
8
+ # It handles different value types appropriately:
9
+ # - Strings: checks for non-whitespace characters
10
+ # - Collections: checks for non-empty collections
11
+ # - Other objects: checks for non-nil values
12
+ module Presence
12
13
 
13
- # Validates that the given value is present (not empty or nil).
14
- #
15
- # @param value [Object] the value to validate
16
- # @param options [Hash] validation options containing presence configuration
17
- # @option options [Hash] :presence presence validation configuration
18
- # @option options [String] :presence.message custom error message
19
- #
20
- # @return [void] returns nothing when validation passes
21
- #
22
- # @raise [ValidationError] if the value is empty, blank, or nil
23
- #
24
- # @example Validating a non-empty string
25
- # Validators::Presence.call("hello", presence: {})
26
- # #=> nil (no error raised)
27
- #
28
- # @example Validating an empty string
29
- # Validators::Presence.call("", presence: {})
30
- # # raises ValidationError: "cannot be empty"
31
- #
32
- # @example Validating a whitespace-only string
33
- # Validators::Presence.call(" ", presence: {})
34
- # # raises ValidationError: "cannot be empty"
35
- #
36
- # @example Validating a non-empty array
37
- # Validators::Presence.call([1, 2, 3], presence: {})
38
- # #=> nil (no error raised)
39
- #
40
- # @example Validating an empty array
41
- # Validators::Presence.call([], presence: {})
42
- # # raises ValidationError: "cannot be empty"
43
- #
44
- # @example Validating a nil value
45
- # Validators::Presence.call(nil, presence: {})
46
- # # raises ValidationError: "cannot be empty"
47
- #
48
- # @example Using a custom message
49
- # Validators::Presence.call("", presence: { message: "This field is required" })
50
- # # raises ValidationError: "This field is required"
14
+ extend self
15
+
16
+ # Validates that a value is present and not empty
17
+ #
18
+ # @param value [Object] The value to validate for presence
19
+ # @param options [Hash] Validation configuration options
20
+ # @option options [String] :message Custom error message
21
+ #
22
+ # @return [nil] Returns nil if validation passes
23
+ #
24
+ # @raise [ValidationError] When the value is empty, nil, or contains only whitespace
25
+ #
26
+ # @example Validate string presence
27
+ # Presence.call("hello world")
28
+ # # => nil (validation passes)
29
+ # @example Validate empty string
30
+ # Presence.call(" ")
31
+ # # => raises ValidationError
32
+ # @example Validate array presence
33
+ # Presence.call([1, 2, 3])
34
+ # # => nil (validation passes)
35
+ # @example Validate empty array
36
+ # Presence.call([])
37
+ # # => raises ValidationError
38
+ # @example Validate with custom message
39
+ # Presence.call(nil, message: "Value cannot be blank")
40
+ # # => raises ValidationError with custom message
51
41
  def call(value, options = {})
52
- present =
42
+ match =
53
43
  if value.is_a?(String)
54
44
  /\S/.match?(value)
55
45
  elsif value.respond_to?(:empty?)
@@ -58,13 +48,10 @@ module CMDx
58
48
  !value.nil?
59
49
  end
60
50
 
61
- return if present
51
+ return if match
62
52
 
63
53
  message = options[:message] if options.is_a?(Hash)
64
- raise ValidationError, message || I18n.t(
65
- "cmdx.validators.presence",
66
- default: "cannot be empty"
67
- )
54
+ raise ValidationError, message || Locale.t("cmdx.validators.presence")
68
55
  end
69
56
 
70
57
  end
data/lib/cmdx/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CMDx
4
4
 
5
- VERSION = "1.1.1"
5
+ VERSION = "1.5.0"
6
6
 
7
7
  end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CMDx
4
+ # Executes CMDx tasks with middleware support, error handling, and lifecycle management.
5
+ #
6
+ # The Worker class is responsible for orchestrating task execution, including
7
+ # pre-execution validation, execution with middleware, post-execution callbacks,
8
+ # and proper error handling for different types of failures.
9
+ class Worker
10
+
11
+ attr_reader :task
12
+
13
+ # @param task [CMDx::Task] The task to execute
14
+ #
15
+ # @return [CMDx::Worker] A new worker instance
16
+ #
17
+ # @example
18
+ # worker = CMDx::Worker.new(my_task)
19
+ def initialize(task)
20
+ @task = task
21
+ end
22
+
23
+ # Executes a task with optional exception raising.
24
+ #
25
+ # @param task [CMDx::Task] The task to execute
26
+ # @param raise [Boolean] Whether to raise exceptions (default: false)
27
+ #
28
+ # @return [CMDx::Result] The execution result
29
+ #
30
+ # @raise [StandardError] When raise is true and execution fails
31
+ #
32
+ # @example
33
+ # CMDx::Worker.execute(my_task)
34
+ # CMDx::Worker.execute(my_task, raise: true)
35
+ def self.execute(task, raise: false)
36
+ instance = new(task)
37
+ raise ? instance.execute! : instance.execute
38
+ end
39
+
40
+ # Executes the task with graceful error handling.
41
+ #
42
+ # @return [CMDx::Result] The execution result
43
+ #
44
+ # @example
45
+ # worker = CMDx::Worker.new(my_task)
46
+ # result = worker.execute
47
+ def execute
48
+ task.class.settings[:middlewares].call!(task) do
49
+ pre_execution!
50
+ execution!
51
+ rescue UndefinedMethodError => e
52
+ raise(e) # No need to clear the Chain since exception is not being re-raised
53
+ rescue Fault => e
54
+ task.result.throw!(e.result, halt: false, cause: e)
55
+ rescue StandardError => e
56
+ task.result.fail!("[#{e.class}] #{e.message}", halt: false, cause: e)
57
+ ensure
58
+ task.result.executed!
59
+ post_execution!
60
+ end
61
+
62
+ finalize_execution!
63
+ end
64
+
65
+ # Executes the task with exception raising on failure.
66
+ #
67
+ # @return [CMDx::Result] The execution result
68
+ #
69
+ # @raise [StandardError] When execution fails
70
+ #
71
+ # @example
72
+ # worker = CMDx::Worker.new(my_task)
73
+ # result = worker.execute!
74
+ def execute!
75
+ task.class.settings[:middlewares].call!(task) do
76
+ pre_execution!
77
+ execution!
78
+ rescue UndefinedMethodError => e
79
+ raise_exception(e)
80
+ rescue Fault => e
81
+ task.result.throw!(e.result, halt: false, cause: e)
82
+ halt_execution?(e) ? raise_exception(e) : post_execution!
83
+ rescue StandardError => e
84
+ task.result.fail!("[#{e.class}] #{e.message}", halt: false, cause: e)
85
+ raise_exception(e)
86
+ else
87
+ task.result.executed!
88
+ post_execution!
89
+ end
90
+
91
+ finalize_execution!
92
+ end
93
+
94
+ protected
95
+
96
+ # Determines if execution should halt based on breakpoint configuration.
97
+ #
98
+ # @param exception [Exception] The exception that occurred
99
+ #
100
+ # @return [Boolean] Whether execution should halt
101
+ #
102
+ # @example
103
+ # halt_execution?(fault_exception)
104
+ def halt_execution?(exception)
105
+ breakpoints = task.class.settings[:breakpoints] || task.class.settings[:task_breakpoints]
106
+ breakpoints = Array(breakpoints).map(&:to_s).uniq
107
+
108
+ breakpoints.include?(exception.result.status)
109
+ end
110
+
111
+ # Raises an exception and clears the chain.
112
+ #
113
+ # @param exception [Exception] The exception to raise
114
+ #
115
+ # @raise [Exception] The provided exception
116
+ #
117
+ # @example
118
+ # raise_exception(standard_error)
119
+ def raise_exception(exception)
120
+ Chain.clear
121
+ raise(exception)
122
+ end
123
+
124
+ # Invokes callbacks of a specific type for the task.
125
+ #
126
+ # @param type [Symbol] The type of callback to invoke
127
+ #
128
+ # @return [void]
129
+ #
130
+ # @example
131
+ # invoke_callbacks(:before_execution)
132
+ def invoke_callbacks(type)
133
+ task.class.settings[:callbacks].invoke(type, task)
134
+ end
135
+
136
+ private
137
+
138
+ # Performs pre-execution tasks including validation and attribute verification.
139
+ def pre_execution!
140
+ invoke_callbacks(:before_validation)
141
+
142
+ task.class.settings[:attributes].define_and_verify(task)
143
+ return if task.errors.empty?
144
+
145
+ task.result.fail!(task.errors.to_s, messages: task.errors.to_h)
146
+ end
147
+
148
+ # Executes the main task logic.
149
+ def execution!
150
+ invoke_callbacks(:before_execution)
151
+
152
+ task.result.executing!
153
+ task.work
154
+ end
155
+
156
+ # Performs post-execution tasks including callback invocation.
157
+ def post_execution!
158
+ invoke_callbacks(:"on_#{task.result.state}")
159
+ invoke_callbacks(:on_executed) if task.result.executed?
160
+
161
+ invoke_callbacks(:"on_#{task.result.status}")
162
+ invoke_callbacks(:on_good) if task.result.good?
163
+ invoke_callbacks(:on_bad) if task.result.bad?
164
+ end
165
+
166
+ # Finalizes execution by freezing the task and logging results.
167
+ def finalize_execution!
168
+ Freezer.immute(task)
169
+
170
+ task.logger.tap do |logger|
171
+ logger.with_level(:info) do
172
+ logger.info { task.result.to_h }
173
+ end
174
+ end
175
+ end
176
+
177
+ end
178
+ end