cmdx 1.0.1 → 1.1.1
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.
- checksums.yaml +4 -4
- data/.cursor/prompts/docs.md +9 -0
- data/.cursor/prompts/rspec.md +21 -0
- data/.cursor/prompts/yardoc.md +13 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +29 -3
- data/README.md +2 -1
- data/docs/ai_prompts.md +269 -195
- data/docs/basics/call.md +126 -60
- data/docs/basics/chain.md +190 -160
- data/docs/basics/context.md +242 -154
- data/docs/basics/setup.md +302 -32
- data/docs/callbacks.md +382 -119
- data/docs/configuration.md +211 -49
- data/docs/deprecation.md +245 -0
- data/docs/getting_started.md +161 -39
- data/docs/internationalization.md +590 -70
- data/docs/interruptions/exceptions.md +135 -118
- data/docs/interruptions/faults.md +152 -127
- data/docs/interruptions/halt.md +134 -80
- data/docs/logging.md +183 -120
- data/docs/middlewares.md +165 -392
- data/docs/outcomes/result.md +140 -112
- data/docs/outcomes/states.md +134 -99
- data/docs/outcomes/statuses.md +204 -146
- data/docs/parameters/coercions.md +251 -289
- data/docs/parameters/defaults.md +224 -169
- data/docs/parameters/definitions.md +289 -141
- data/docs/parameters/namespacing.md +250 -161
- data/docs/parameters/validations.md +247 -159
- data/docs/testing.md +196 -203
- data/docs/workflows.md +146 -101
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/callback.rb +39 -55
- data/lib/cmdx/callback_registry.rb +80 -73
- data/lib/cmdx/chain.rb +65 -122
- data/lib/cmdx/chain_inspector.rb +23 -116
- data/lib/cmdx/chain_serializer.rb +34 -146
- data/lib/cmdx/coercion.rb +57 -0
- data/lib/cmdx/coercion_registry.rb +113 -0
- data/lib/cmdx/coercions/array.rb +18 -36
- data/lib/cmdx/coercions/big_decimal.rb +21 -33
- data/lib/cmdx/coercions/boolean.rb +21 -40
- data/lib/cmdx/coercions/complex.rb +18 -31
- data/lib/cmdx/coercions/date.rb +20 -39
- data/lib/cmdx/coercions/date_time.rb +22 -39
- data/lib/cmdx/coercions/float.rb +19 -32
- data/lib/cmdx/coercions/hash.rb +22 -41
- data/lib/cmdx/coercions/integer.rb +20 -33
- data/lib/cmdx/coercions/rational.rb +20 -32
- data/lib/cmdx/coercions/string.rb +23 -31
- data/lib/cmdx/coercions/time.rb +24 -40
- data/lib/cmdx/coercions/virtual.rb +14 -31
- data/lib/cmdx/configuration.rb +101 -162
- data/lib/cmdx/context.rb +34 -166
- data/lib/cmdx/core_ext/hash.rb +42 -67
- data/lib/cmdx/core_ext/module.rb +35 -79
- data/lib/cmdx/core_ext/object.rb +63 -98
- data/lib/cmdx/correlator.rb +59 -154
- data/lib/cmdx/error.rb +37 -202
- data/lib/cmdx/errors.rb +153 -216
- data/lib/cmdx/fault.rb +68 -150
- data/lib/cmdx/faults.rb +26 -137
- data/lib/cmdx/immutator.rb +22 -110
- data/lib/cmdx/lazy_struct.rb +110 -186
- data/lib/cmdx/log_formatters/json.rb +14 -40
- data/lib/cmdx/log_formatters/key_value.rb +14 -40
- data/lib/cmdx/log_formatters/line.rb +14 -48
- data/lib/cmdx/log_formatters/logstash.rb +14 -57
- data/lib/cmdx/log_formatters/pretty_json.rb +14 -50
- data/lib/cmdx/log_formatters/pretty_key_value.rb +13 -46
- data/lib/cmdx/log_formatters/pretty_line.rb +16 -54
- data/lib/cmdx/log_formatters/raw.rb +19 -49
- data/lib/cmdx/logger.rb +22 -79
- data/lib/cmdx/logger_ansi.rb +31 -72
- data/lib/cmdx/logger_serializer.rb +74 -103
- data/lib/cmdx/middleware.rb +56 -60
- data/lib/cmdx/middleware_registry.rb +82 -77
- data/lib/cmdx/middlewares/correlate.rb +41 -226
- data/lib/cmdx/middlewares/timeout.rb +46 -185
- data/lib/cmdx/parameter.rb +167 -183
- data/lib/cmdx/parameter_evaluator.rb +231 -0
- data/lib/cmdx/parameter_inspector.rb +37 -55
- data/lib/cmdx/parameter_registry.rb +65 -84
- data/lib/cmdx/parameter_serializer.rb +32 -76
- data/lib/cmdx/railtie.rb +24 -107
- data/lib/cmdx/result.rb +254 -259
- data/lib/cmdx/result_ansi.rb +28 -80
- data/lib/cmdx/result_inspector.rb +34 -70
- data/lib/cmdx/result_logger.rb +23 -77
- data/lib/cmdx/result_serializer.rb +59 -125
- data/lib/cmdx/rspec/matchers.rb +28 -0
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +42 -0
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +94 -0
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +57 -0
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +87 -0
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +51 -0
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +58 -0
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +59 -0
- data/lib/cmdx/rspec/result_matchers/have_context.rb +86 -0
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +54 -0
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +52 -0
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +114 -0
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +66 -0
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +64 -0
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +78 -0
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +76 -0
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +62 -0
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +85 -0
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +68 -0
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +92 -0
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +46 -0
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +181 -0
- data/lib/cmdx/task.rb +336 -427
- data/lib/cmdx/task_deprecator.rb +52 -0
- data/lib/cmdx/task_processor.rb +246 -0
- data/lib/cmdx/task_serializer.rb +34 -69
- data/lib/cmdx/utils/ansi_color.rb +13 -89
- data/lib/cmdx/utils/log_timestamp.rb +13 -42
- data/lib/cmdx/utils/monotonic_runtime.rb +11 -63
- data/lib/cmdx/utils/name_affix.rb +21 -71
- data/lib/cmdx/validator.rb +57 -0
- data/lib/cmdx/validator_registry.rb +108 -0
- data/lib/cmdx/validators/exclusion.rb +55 -94
- data/lib/cmdx/validators/format.rb +31 -85
- data/lib/cmdx/validators/inclusion.rb +65 -110
- data/lib/cmdx/validators/length.rb +117 -133
- data/lib/cmdx/validators/numeric.rb +123 -130
- data/lib/cmdx/validators/presence.rb +38 -79
- data/lib/cmdx/version.rb +1 -7
- data/lib/cmdx/workflow.rb +58 -330
- data/lib/cmdx.rb +1 -1
- data/lib/generators/cmdx/install_generator.rb +14 -31
- data/lib/generators/cmdx/task_generator.rb +39 -55
- data/lib/generators/cmdx/templates/install.rb +24 -6
- data/lib/generators/cmdx/workflow_generator.rb +41 -66
- data/lib/locales/ar.yml +0 -1
- data/lib/locales/cs.yml +0 -1
- data/lib/locales/da.yml +0 -1
- data/lib/locales/de.yml +0 -1
- data/lib/locales/el.yml +0 -1
- data/lib/locales/en.yml +0 -1
- data/lib/locales/es.yml +0 -1
- data/lib/locales/fi.yml +0 -1
- data/lib/locales/fr.yml +0 -1
- data/lib/locales/he.yml +0 -1
- data/lib/locales/hi.yml +0 -1
- data/lib/locales/it.yml +0 -1
- data/lib/locales/ja.yml +0 -1
- data/lib/locales/ko.yml +0 -1
- data/lib/locales/nl.yml +0 -1
- data/lib/locales/no.yml +0 -1
- data/lib/locales/pl.yml +0 -1
- data/lib/locales/pt.yml +0 -1
- data/lib/locales/ru.yml +0 -1
- data/lib/locales/sv.yml +0 -1
- data/lib/locales/th.yml +0 -1
- data/lib/locales/tr.yml +0 -1
- data/lib/locales/vi.yml +0 -1
- data/lib/locales/zh.yml +0 -1
- metadata +36 -8
- data/lib/cmdx/parameter_validator.rb +0 -81
- data/lib/cmdx/parameter_value.rb +0 -244
- data/lib/cmdx/parameters_inspector.rb +0 -72
- data/lib/cmdx/parameters_serializer.rb +0 -115
- data/lib/cmdx/rspec/result_matchers.rb +0 -917
- data/lib/cmdx/rspec/task_matchers.rb +0 -570
- data/lib/cmdx/validators/custom.rb +0 -102
@@ -2,108 +2,58 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module Validators
|
5
|
-
#
|
5
|
+
# Validator class for validating the length of values.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# required :username, length: { within: 3..20 }
|
14
|
-
# required :password, length: { in: 8..128 }
|
15
|
-
# required :bio, length: { not_within: 500..1000 } # Avoid medium length
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# @example Boundary length validation
|
19
|
-
# class ProcessContentTask < CMDx::Task
|
20
|
-
# required :title, length: { min: 5 }
|
21
|
-
# required :description, length: { max: 500 }
|
22
|
-
# required :slug, length: { min: 3, max: 50 } # Combined min/max
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# @example Exact length validation
|
26
|
-
# class ProcessCodeTask < CMDx::Task
|
27
|
-
# required :country_code, length: { is: 2 } # ISO country codes
|
28
|
-
# required :postal_code, length: { is_not: 4 } # Avoid 4-digit codes
|
29
|
-
# end
|
30
|
-
#
|
31
|
-
# @example Custom error messages
|
32
|
-
# class ProcessUserTask < CMDx::Task
|
33
|
-
# required :username, length: {
|
34
|
-
# within: 3..20,
|
35
|
-
# within_message: "must be between %{min} and %{max} characters"
|
36
|
-
# }
|
37
|
-
# required :password, length: {
|
38
|
-
# min: 8,
|
39
|
-
# min_message: "must be at least %{min} characters for security"
|
40
|
-
# }
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# @example Length validation behavior
|
44
|
-
# # String length validation
|
45
|
-
# Length.call("hello", length: { min: 3 }) # passes (length: 5)
|
46
|
-
# Length.call("hi", length: { min: 3 }) # raises ValidationError
|
47
|
-
#
|
48
|
-
# # Array length validation
|
49
|
-
# Length.call([1, 2, 3], length: { is: 3 }) # passes
|
50
|
-
# Length.call([1, 2], length: { is: 3 }) # raises ValidationError
|
51
|
-
#
|
52
|
-
# @see CMDx::Validators::Numeric For numeric value validation
|
53
|
-
# @see CMDx::Parameter Parameter validation integration
|
54
|
-
# @see CMDx::ValidationError Raised when validation fails
|
55
|
-
module Length
|
56
|
-
|
57
|
-
extend self
|
58
|
-
|
59
|
-
# Validates that a parameter value meets the specified length constraints.
|
60
|
-
#
|
61
|
-
# Validates the length of the value using the specified constraint type.
|
62
|
-
# Only one constraint option can be used at a time, except for :min and :max
|
63
|
-
# which can be combined together.
|
64
|
-
#
|
65
|
-
# @param value [#length] The parameter value to validate (must respond to #length)
|
66
|
-
# @param options [Hash] Validation configuration options
|
67
|
-
# @option options [Hash] :length Length validation configuration
|
68
|
-
# @option options [Range] :length.within Allowed length range
|
69
|
-
# @option options [Range] :length.not_within Forbidden length range
|
70
|
-
# @option options [Range] :length.in Alias for :within
|
71
|
-
# @option options [Range] :length.not_in Alias for :not_within
|
72
|
-
# @option options [Integer] :length.min Minimum allowed length
|
73
|
-
# @option options [Integer] :length.max Maximum allowed length
|
74
|
-
# @option options [Integer] :length.is Exact required length
|
75
|
-
# @option options [Integer] :length.is_not Forbidden exact length
|
76
|
-
# @option options [String] :length.*_message Custom error messages for each constraint
|
7
|
+
# This validator ensures that a value's length meets specified criteria.
|
8
|
+
# It supports various length validation options including exact length,
|
9
|
+
# minimum/maximum bounds, range validation, and exclusion patterns.
|
10
|
+
class Length < Validator
|
11
|
+
|
12
|
+
# Validates that the given value's length meets the specified criteria.
|
77
13
|
#
|
78
|
-
# @
|
79
|
-
# @
|
80
|
-
# @
|
14
|
+
# @param value [Object] the value to validate (must respond to #length)
|
15
|
+
# @param options [Hash] validation options containing length configuration
|
16
|
+
# @option options [Hash] :length length validation configuration
|
17
|
+
# @option options [Range] :length.within acceptable length range
|
18
|
+
# @option options [Range] :length.not_within unacceptable length range
|
19
|
+
# @option options [Range] :length.in alias for :within
|
20
|
+
# @option options [Range] :length.not_in alias for :not_within
|
21
|
+
# @option options [Integer] :length.min minimum acceptable length
|
22
|
+
# @option options [Integer] :length.max maximum acceptable length
|
23
|
+
# @option options [Integer] :length.is exact required length
|
24
|
+
# @option options [Integer] :length.is_not exact forbidden length
|
25
|
+
# @option options [String] :length.message custom error message
|
26
|
+
# @option options [String] :length.within_message custom error message for within validation
|
27
|
+
# @option options [String] :length.not_within_message custom error message for not_within validation
|
28
|
+
# @option options [String] :length.in_message alias for :within_message
|
29
|
+
# @option options [String] :length.not_in_message alias for :not_within_message
|
30
|
+
# @option options [String] :length.min_message custom error message for minimum validation
|
31
|
+
# @option options [String] :length.max_message custom error message for maximum validation
|
32
|
+
# @option options [String] :length.is_message custom error message for exact validation
|
33
|
+
# @option options [String] :length.is_not_message custom error message for exact exclusion validation
|
81
34
|
#
|
82
|
-
# @
|
83
|
-
# Length.call("hello", length: { within: 3..10 })
|
84
|
-
# # => passes without error
|
35
|
+
# @return [void]
|
85
36
|
#
|
86
|
-
# @
|
87
|
-
#
|
88
|
-
# # => raises ValidationError: "length must be within 3 and 10"
|
37
|
+
# @raise [ValidationError] if the value's length doesn't meet the criteria
|
38
|
+
# @raise [ArgumentError] if no known length validator options are provided
|
89
39
|
#
|
90
|
-
# @example
|
91
|
-
# Length.call("
|
92
|
-
#
|
40
|
+
# @example Validating within a range
|
41
|
+
# Validators::Length.call("hello", length: { within: 1..10 })
|
42
|
+
# #=> nil (no error raised)
|
93
43
|
#
|
94
|
-
# @example
|
95
|
-
# Length.call("
|
96
|
-
# #
|
44
|
+
# @example Validating minimum length
|
45
|
+
# Validators::Length.call("hi", length: { min: 5 })
|
46
|
+
# # raises ValidationError: "length must be at least 5"
|
97
47
|
#
|
98
|
-
# @example
|
99
|
-
# Length.call("
|
100
|
-
#
|
48
|
+
# @example Validating exact length
|
49
|
+
# Validators::Length.call("test", length: { is: 4 })
|
50
|
+
# #=> nil (no error raised)
|
101
51
|
#
|
102
|
-
# @example
|
103
|
-
# Length.call(
|
104
|
-
# #
|
52
|
+
# @example Validating with custom message
|
53
|
+
# Validators::Length.call("", length: { min: 1, message: "cannot be empty" })
|
54
|
+
# # raises ValidationError: "cannot be empty"
|
105
55
|
def call(value, options = {})
|
106
|
-
case options
|
56
|
+
case options
|
107
57
|
in { within: within }
|
108
58
|
raise_within_validation_error!(within.begin, within.end, options) unless within.cover?(value.length)
|
109
59
|
in { not_within: not_within }
|
@@ -129,16 +79,21 @@ module CMDx
|
|
129
79
|
|
130
80
|
private
|
131
81
|
|
132
|
-
# Raises validation error for range
|
82
|
+
# Raises a validation error for within/in range validation.
|
83
|
+
#
|
84
|
+
# @param min [Integer] the minimum acceptable length
|
85
|
+
# @param max [Integer] the maximum acceptable length
|
86
|
+
# @param options [Hash] validation options
|
87
|
+
#
|
88
|
+
# @return [void]
|
133
89
|
#
|
134
|
-
# @
|
135
|
-
#
|
136
|
-
# @
|
137
|
-
#
|
90
|
+
# @raise [ValidationError] always raised with appropriate message
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# raise_within_validation_error!(5, 10, {})
|
94
|
+
# # raises ValidationError: "length must be within 5 and 10"
|
138
95
|
def raise_within_validation_error!(min, max, options)
|
139
|
-
message = options
|
140
|
-
options.dig(:length, :in_message) ||
|
141
|
-
options.dig(:length, :message)
|
96
|
+
message = options[:within_message] || options[:in_message] || options[:message]
|
142
97
|
message %= { min:, max: } unless message.nil?
|
143
98
|
|
144
99
|
raise ValidationError, message || I18n.t(
|
@@ -149,16 +104,21 @@ module CMDx
|
|
149
104
|
)
|
150
105
|
end
|
151
106
|
|
152
|
-
# Raises validation error for
|
107
|
+
# Raises a validation error for not_within/not_in range validation.
|
108
|
+
#
|
109
|
+
# @param min [Integer] the minimum forbidden length
|
110
|
+
# @param max [Integer] the maximum forbidden length
|
111
|
+
# @param options [Hash] validation options
|
112
|
+
#
|
113
|
+
# @return [void]
|
153
114
|
#
|
154
|
-
# @
|
155
|
-
#
|
156
|
-
# @
|
157
|
-
#
|
115
|
+
# @raise [ValidationError] always raised with appropriate message
|
116
|
+
#
|
117
|
+
# @example
|
118
|
+
# raise_not_within_validation_error!(5, 10, {})
|
119
|
+
# # raises ValidationError: "length must not be within 5 and 10"
|
158
120
|
def raise_not_within_validation_error!(min, max, options)
|
159
|
-
message = options
|
160
|
-
options.dig(:length, :not_in_message) ||
|
161
|
-
options.dig(:length, :message)
|
121
|
+
message = options[:not_within_message] || options[:not_in_message] || options[:message]
|
162
122
|
message %= { min:, max: } unless message.nil?
|
163
123
|
|
164
124
|
raise ValidationError, message || I18n.t(
|
@@ -169,14 +129,20 @@ module CMDx
|
|
169
129
|
)
|
170
130
|
end
|
171
131
|
|
172
|
-
# Raises validation error for minimum length
|
132
|
+
# Raises a validation error for minimum length validation.
|
133
|
+
#
|
134
|
+
# @param min [Integer] the minimum acceptable length
|
135
|
+
# @param options [Hash] validation options
|
136
|
+
#
|
137
|
+
# @return [void]
|
138
|
+
#
|
139
|
+
# @raise [ValidationError] always raised with appropriate message
|
173
140
|
#
|
174
|
-
# @
|
175
|
-
#
|
176
|
-
#
|
141
|
+
# @example
|
142
|
+
# raise_min_validation_error!(5, {})
|
143
|
+
# # raises ValidationError: "length must be at least 5"
|
177
144
|
def raise_min_validation_error!(min, options)
|
178
|
-
message = options
|
179
|
-
options.dig(:length, :message)
|
145
|
+
message = options[:min_message] || options[:message]
|
180
146
|
message %= { min: } unless message.nil?
|
181
147
|
|
182
148
|
raise ValidationError, message || I18n.t(
|
@@ -186,14 +152,20 @@ module CMDx
|
|
186
152
|
)
|
187
153
|
end
|
188
154
|
|
189
|
-
# Raises validation error for maximum length
|
155
|
+
# Raises a validation error for maximum length validation.
|
156
|
+
#
|
157
|
+
# @param max [Integer] the maximum acceptable length
|
158
|
+
# @param options [Hash] validation options
|
159
|
+
#
|
160
|
+
# @return [void]
|
161
|
+
#
|
162
|
+
# @raise [ValidationError] always raised with appropriate message
|
190
163
|
#
|
191
|
-
# @
|
192
|
-
#
|
193
|
-
#
|
164
|
+
# @example
|
165
|
+
# raise_max_validation_error!(10, {})
|
166
|
+
# # raises ValidationError: "length must be at most 10"
|
194
167
|
def raise_max_validation_error!(max, options)
|
195
|
-
message = options
|
196
|
-
options.dig(:length, :message)
|
168
|
+
message = options[:max_message] || options[:message]
|
197
169
|
message %= { max: } unless message.nil?
|
198
170
|
|
199
171
|
raise ValidationError, message || I18n.t(
|
@@ -203,14 +175,20 @@ module CMDx
|
|
203
175
|
)
|
204
176
|
end
|
205
177
|
|
206
|
-
# Raises validation error for exact length
|
178
|
+
# Raises a validation error for exact length validation.
|
207
179
|
#
|
208
|
-
# @param is [Integer]
|
209
|
-
# @param options [Hash]
|
210
|
-
#
|
180
|
+
# @param is [Integer] the exact required length
|
181
|
+
# @param options [Hash] validation options
|
182
|
+
#
|
183
|
+
# @return [void]
|
184
|
+
#
|
185
|
+
# @raise [ValidationError] always raised with appropriate message
|
186
|
+
#
|
187
|
+
# @example
|
188
|
+
# raise_is_validation_error!(5, {})
|
189
|
+
# # raises ValidationError: "length must be 5"
|
211
190
|
def raise_is_validation_error!(is, options)
|
212
|
-
message = options
|
213
|
-
options.dig(:length, :message)
|
191
|
+
message = options[:is_message] || options[:message]
|
214
192
|
message %= { is: } unless message.nil?
|
215
193
|
|
216
194
|
raise ValidationError, message || I18n.t(
|
@@ -220,14 +198,20 @@ module CMDx
|
|
220
198
|
)
|
221
199
|
end
|
222
200
|
|
223
|
-
# Raises validation error for
|
201
|
+
# Raises a validation error for exact length exclusion validation.
|
202
|
+
#
|
203
|
+
# @param is_not [Integer] the exact forbidden length
|
204
|
+
# @param options [Hash] validation options
|
205
|
+
#
|
206
|
+
# @return [void]
|
207
|
+
#
|
208
|
+
# @raise [ValidationError] always raised with appropriate message
|
224
209
|
#
|
225
|
-
# @
|
226
|
-
#
|
227
|
-
#
|
210
|
+
# @example
|
211
|
+
# raise_is_not_validation_error!(5, {})
|
212
|
+
# # raises ValidationError: "length must not be 5"
|
228
213
|
def raise_is_not_validation_error!(is_not, options)
|
229
|
-
message = options
|
230
|
-
options.dig(:length, :message)
|
214
|
+
message = options[:is_not_message] || options[:message]
|
231
215
|
message %= { is_not: } unless message.nil?
|
232
216
|
|
233
217
|
raise ValidationError, message || I18n.t(
|
@@ -2,108 +2,67 @@
|
|
2
2
|
|
3
3
|
module CMDx
|
4
4
|
module Validators
|
5
|
-
#
|
5
|
+
# Validator class for validating numeric values with various constraints.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# within_message: "must be between %{min} and %{max} items"
|
36
|
-
# }
|
37
|
-
# required :age, numeric: {
|
38
|
-
# min: 18,
|
39
|
-
# min_message: "must be at least %{min} years old"
|
40
|
-
# }
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# @example Numeric validation behavior
|
44
|
-
# # Integer validation
|
45
|
-
# Numeric.call(25, numeric: { min: 18 }) # passes
|
46
|
-
# Numeric.call(15, numeric: { min: 18 }) # raises ValidationError
|
47
|
-
#
|
48
|
-
# # Float validation
|
49
|
-
# Numeric.call(99.99, numeric: { max: 100.0 }) # passes
|
50
|
-
# Numeric.call(101.5, numeric: { max: 100.0 }) # raises ValidationError
|
51
|
-
#
|
52
|
-
# @see CMDx::Validators::Length For length/size validation
|
53
|
-
# @see CMDx::Parameter Parameter validation integration
|
54
|
-
# @see CMDx::ValidationError Raised when validation fails
|
55
|
-
module Numeric
|
56
|
-
|
57
|
-
extend self
|
58
|
-
|
59
|
-
# Validates that a parameter value meets the specified numeric constraints.
|
60
|
-
#
|
61
|
-
# Validates the numeric value using the specified constraint type.
|
62
|
-
# Only one constraint option can be used at a time, except for :min and :max
|
63
|
-
# which can be combined together.
|
64
|
-
#
|
65
|
-
# @param value [Numeric] The parameter value to validate (must be numeric)
|
66
|
-
# @param options [Hash] Validation configuration options
|
67
|
-
# @option options [Hash] :numeric Numeric validation configuration
|
68
|
-
# @option options [Range] :numeric.within Allowed value range
|
69
|
-
# @option options [Range] :numeric.not_within Forbidden value range
|
70
|
-
# @option options [Range] :numeric.in Alias for :within
|
71
|
-
# @option options [Range] :numeric.not_in Alias for :not_within
|
72
|
-
# @option options [Numeric] :numeric.min Minimum allowed value
|
73
|
-
# @option options [Numeric] :numeric.max Maximum allowed value
|
74
|
-
# @option options [Numeric] :numeric.is Exact required value
|
75
|
-
# @option options [Numeric] :numeric.is_not Forbidden exact value
|
76
|
-
# @option options [String] :numeric.*_message Custom error messages for each constraint
|
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
|
77
35
|
#
|
78
36
|
# @return [void]
|
79
|
-
#
|
80
|
-
# @raise [
|
37
|
+
#
|
38
|
+
# @raise [ValidationError] if the value doesn't meet the specified constraints
|
39
|
+
# @raise [ArgumentError] if no known numeric validator options are provided
|
81
40
|
#
|
82
41
|
# @example Range validation
|
83
|
-
# Numeric.call(
|
84
|
-
#
|
42
|
+
# Validators::Numeric.call(5, numeric: { within: 1..10 })
|
43
|
+
# #=> nil (no error raised)
|
85
44
|
#
|
86
|
-
# @example
|
87
|
-
# Numeric.call(
|
88
|
-
# #
|
45
|
+
# @example Range exclusion
|
46
|
+
# Validators::Numeric.call(5, numeric: { not_within: 1..10 })
|
47
|
+
# # raises ValidationError: "must not be within 1 and 10"
|
89
48
|
#
|
90
|
-
# @example
|
91
|
-
# Numeric.call(
|
92
|
-
#
|
49
|
+
# @example Min/max validation
|
50
|
+
# Validators::Numeric.call(15, numeric: { min: 10, max: 20 })
|
51
|
+
# #=> nil (no error raised)
|
93
52
|
#
|
94
|
-
# @example
|
95
|
-
# Numeric.call(
|
96
|
-
# #
|
53
|
+
# @example Minimum value validation
|
54
|
+
# Validators::Numeric.call(5, numeric: { min: 10 })
|
55
|
+
# # raises ValidationError: "must be at least 10"
|
97
56
|
#
|
98
57
|
# @example Exact value validation
|
99
|
-
# Numeric.call(42, numeric: { is: 42 })
|
100
|
-
#
|
58
|
+
# Validators::Numeric.call(42, numeric: { is: 42 })
|
59
|
+
# #=> nil (no error raised)
|
101
60
|
#
|
102
|
-
# @example
|
103
|
-
# Numeric.call(
|
104
|
-
# #
|
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"
|
105
64
|
def call(value, options = {})
|
106
|
-
case options
|
65
|
+
case options
|
107
66
|
in { within: within }
|
108
67
|
raise_within_validation_error!(within.begin, within.end, options) unless within.cover?(value)
|
109
68
|
in { not_within: not_within }
|
@@ -129,16 +88,21 @@ module CMDx
|
|
129
88
|
|
130
89
|
private
|
131
90
|
|
132
|
-
# Raises validation error for range
|
91
|
+
# Raises a validation error for within/range validation.
|
133
92
|
#
|
134
|
-
# @param min [Numeric]
|
135
|
-
# @param max [Numeric]
|
136
|
-
# @param options [Hash]
|
137
|
-
#
|
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
|
96
|
+
#
|
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"
|
138
104
|
def raise_within_validation_error!(min, max, options)
|
139
|
-
message = options
|
140
|
-
options.dig(:numeric, :in_message) ||
|
141
|
-
options.dig(:numeric, :message)
|
105
|
+
message = options[:within_message] || options[:in_message] || options[:message]
|
142
106
|
message %= { min:, max: } unless message.nil?
|
143
107
|
|
144
108
|
raise ValidationError, message || I18n.t(
|
@@ -149,16 +113,21 @@ module CMDx
|
|
149
113
|
)
|
150
114
|
end
|
151
115
|
|
152
|
-
# Raises validation error for
|
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
|
121
|
+
#
|
122
|
+
# @return [void]
|
123
|
+
#
|
124
|
+
# @raise [ValidationError] always raised with appropriate message
|
153
125
|
#
|
154
|
-
# @
|
155
|
-
#
|
156
|
-
#
|
157
|
-
# @raise [ValidationError] With formatted error message
|
126
|
+
# @example
|
127
|
+
# raise_not_within_validation_error!(1, 10, {})
|
128
|
+
# # raises ValidationError: "must not be within 1 and 10"
|
158
129
|
def raise_not_within_validation_error!(min, max, options)
|
159
|
-
message = options
|
160
|
-
options.dig(:numeric, :not_in_message) ||
|
161
|
-
options.dig(:numeric, :message)
|
130
|
+
message = options[:not_within_message] || options[:not_in_message] || options[:message]
|
162
131
|
message %= { min:, max: } unless message.nil?
|
163
132
|
|
164
133
|
raise ValidationError, message || I18n.t(
|
@@ -169,14 +138,20 @@ module CMDx
|
|
169
138
|
)
|
170
139
|
end
|
171
140
|
|
172
|
-
# Raises validation error for minimum value
|
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]
|
173
147
|
#
|
174
|
-
# @
|
175
|
-
#
|
176
|
-
# @
|
148
|
+
# @raise [ValidationError] always raised with appropriate message
|
149
|
+
#
|
150
|
+
# @example
|
151
|
+
# raise_min_validation_error!(10, {})
|
152
|
+
# # raises ValidationError: "must be at least 10"
|
177
153
|
def raise_min_validation_error!(min, options)
|
178
|
-
message = options
|
179
|
-
options.dig(:numeric, :message)
|
154
|
+
message = options[:min_message] || options[:message]
|
180
155
|
message %= { min: } unless message.nil?
|
181
156
|
|
182
157
|
raise ValidationError, message || I18n.t(
|
@@ -186,14 +161,20 @@ module CMDx
|
|
186
161
|
)
|
187
162
|
end
|
188
163
|
|
189
|
-
# Raises validation error for maximum value
|
164
|
+
# Raises a validation error for maximum value validation.
|
165
|
+
#
|
166
|
+
# @param max [Numeric] the maximum allowed value
|
167
|
+
# @param options [Hash] validation options
|
168
|
+
#
|
169
|
+
# @return [void]
|
190
170
|
#
|
191
|
-
# @
|
192
|
-
#
|
193
|
-
# @
|
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"
|
194
176
|
def raise_max_validation_error!(max, options)
|
195
|
-
message = options
|
196
|
-
options.dig(:numeric, :message)
|
177
|
+
message = options[:max_message] || options[:message]
|
197
178
|
message %= { max: } unless message.nil?
|
198
179
|
|
199
180
|
raise ValidationError, message || I18n.t(
|
@@ -203,14 +184,20 @@ module CMDx
|
|
203
184
|
)
|
204
185
|
end
|
205
186
|
|
206
|
-
# Raises validation error for exact value
|
187
|
+
# Raises a validation error for exact value validation.
|
188
|
+
#
|
189
|
+
# @param is [Numeric] the exact value required
|
190
|
+
# @param options [Hash] validation options
|
191
|
+
#
|
192
|
+
# @return [void]
|
193
|
+
#
|
194
|
+
# @raise [ValidationError] always raised with appropriate message
|
207
195
|
#
|
208
|
-
# @
|
209
|
-
#
|
210
|
-
#
|
196
|
+
# @example
|
197
|
+
# raise_is_validation_error!(42, {})
|
198
|
+
# # raises ValidationError: "must be 42"
|
211
199
|
def raise_is_validation_error!(is, options)
|
212
|
-
message = options
|
213
|
-
options.dig(:numeric, :message)
|
200
|
+
message = options[:is_message] || options[:message]
|
214
201
|
message %= { is: } unless message.nil?
|
215
202
|
|
216
203
|
raise ValidationError, message || I18n.t(
|
@@ -220,14 +207,20 @@ module CMDx
|
|
220
207
|
)
|
221
208
|
end
|
222
209
|
|
223
|
-
# Raises validation error for
|
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]
|
216
|
+
#
|
217
|
+
# @raise [ValidationError] always raised with appropriate message
|
224
218
|
#
|
225
|
-
# @
|
226
|
-
#
|
227
|
-
#
|
219
|
+
# @example
|
220
|
+
# raise_is_not_validation_error!(0, {})
|
221
|
+
# # raises ValidationError: "must not be 0"
|
228
222
|
def raise_is_not_validation_error!(is_not, options)
|
229
|
-
message = options
|
230
|
-
options.dig(:numeric, :message)
|
223
|
+
message = options[:is_not_message] || options[:message]
|
231
224
|
message %= { is_not: } unless message.nil?
|
232
225
|
|
233
226
|
raise ValidationError, message || I18n.t(
|