servactory 2.16.1 → 3.0.0.rc2
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/README.md +38 -9
- data/config/locales/de.yml +134 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/es.yml +134 -0
- data/config/locales/fr.yml +134 -0
- data/config/locales/it.yml +134 -0
- data/config/locales/ru.yml +15 -0
- data/lib/generators/README.md +45 -0
- data/lib/generators/servactory/base.rb +82 -0
- data/lib/generators/servactory/extension/USAGE +54 -0
- data/lib/generators/servactory/extension/extension_generator.rb +41 -0
- data/lib/generators/servactory/extension/templates/extension.rb.tt +62 -0
- data/lib/generators/servactory/install/USAGE +27 -0
- data/lib/generators/servactory/install/install_generator.rb +94 -0
- data/lib/generators/servactory/{templates/services/application_service/base.rb → install/templates/application_service/base.rb.tt} +29 -19
- data/lib/generators/servactory/{templates/services/application_service/exceptions.rb → install/templates/application_service/exceptions.rb.tt} +1 -1
- data/lib/generators/servactory/{templates/services/application_service/result.rb → install/templates/application_service/result.rb.tt} +1 -1
- data/lib/generators/servactory/rspec/USAGE +46 -0
- data/lib/generators/servactory/rspec/rspec_generator.rb +95 -0
- data/lib/generators/servactory/rspec/templates/service_spec.rb.tt +58 -0
- data/lib/generators/servactory/service/USAGE +51 -0
- data/lib/generators/servactory/service/service_generator.rb +56 -0
- data/lib/generators/servactory/service/templates/service.rb.tt +22 -0
- data/lib/servactory/actions/collection.rb +56 -1
- data/lib/servactory/actions/dsl.rb +11 -11
- data/lib/servactory/actions/tools/rules.rb +1 -1
- data/lib/servactory/actions/tools/runner.rb +111 -28
- data/lib/servactory/base.rb +1 -7
- data/lib/servactory/configuration/actions/aliases/collection.rb +5 -0
- data/lib/servactory/configuration/actions/rescue_handlers/collection.rb +5 -0
- data/lib/servactory/configuration/actions/shortcuts/collection.rb +5 -0
- data/lib/servactory/configuration/collection_mode/class_names_collection.rb +5 -0
- data/lib/servactory/configuration/config.rb +36 -0
- data/lib/servactory/configuration/configurable.rb +95 -0
- data/lib/servactory/configuration/dsl.rb +3 -27
- data/lib/servactory/configuration/factory.rb +20 -20
- data/lib/servactory/configuration/hash_mode/class_names_collection.rb +5 -0
- data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +5 -0
- data/lib/servactory/context/warehouse/inputs.rb +2 -2
- data/lib/servactory/context/workspace/inputs.rb +2 -2
- data/lib/servactory/context/workspace/internals.rb +2 -2
- data/lib/servactory/context/workspace/outputs.rb +2 -2
- data/lib/servactory/context/workspace.rb +11 -7
- data/lib/servactory/dsl.rb +10 -8
- data/lib/servactory/maintenance/attributes/tools/validation.rb +1 -1
- data/lib/servactory/result.rb +2 -2
- data/lib/servactory/test_kit/fake_type.rb +23 -0
- data/lib/servactory/test_kit/result.rb +45 -0
- data/lib/servactory/test_kit/rspec/helpers/argument_matchers.rb +97 -0
- data/lib/servactory/test_kit/rspec/helpers/concerns/error_messages.rb +179 -0
- data/lib/servactory/test_kit/rspec/helpers/concerns/service_class_validation.rb +74 -0
- data/lib/servactory/test_kit/rspec/helpers/fluent.rb +110 -0
- data/lib/servactory/test_kit/rspec/helpers/input_validator.rb +149 -0
- data/lib/servactory/test_kit/rspec/helpers/legacy.rb +228 -0
- data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +256 -0
- data/lib/servactory/test_kit/rspec/helpers/output_validator.rb +121 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +422 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +129 -0
- data/lib/servactory/test_kit/rspec/helpers.rb +51 -84
- data/lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb +324 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher.rb +133 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher_context.rb +101 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher_registry.rb +205 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/attribute_data_access.rb +100 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/error_message_builder.rb +106 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/value_comparison.rb +97 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +89 -219
- data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +74 -166
- data/lib/servactory/test_kit/rspec/matchers/have_service_output_matcher.rb +238 -0
- data/lib/servactory/test_kit/rspec/matchers/result/be_failure_service_matcher.rb +257 -0
- data/lib/servactory/test_kit/rspec/matchers/result/be_success_service_matcher.rb +185 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/default_submatcher.rb +81 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/optional_submatcher.rb +62 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/required_submatcher.rb +93 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/valid_with_submatcher.rb +271 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/consists_of_submatcher.rb +85 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/inclusion_submatcher.rb +120 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/message_submatcher.rb +115 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/must_submatcher.rb +82 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/schema_submatcher.rb +102 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/target_submatcher.rb +125 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/types_submatcher.rb +77 -0
- data/lib/servactory/test_kit/rspec/matchers.rb +126 -285
- data/lib/servactory/test_kit/utils/faker.rb +62 -2
- data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +166 -0
- data/lib/servactory/tool_kit/dynamic_options/format.rb +195 -8
- data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +219 -17
- data/lib/servactory/tool_kit/dynamic_options/max.rb +143 -0
- data/lib/servactory/tool_kit/dynamic_options/min.rb +143 -0
- data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +157 -8
- data/lib/servactory/tool_kit/dynamic_options/must.rb +194 -0
- data/lib/servactory/tool_kit/dynamic_options/schema.rb +226 -2
- data/lib/servactory/tool_kit/dynamic_options/target.rb +248 -0
- data/lib/servactory/version.rb +4 -4
- data/lib/servactory.rb +6 -0
- metadata +57 -48
- data/lib/generators/servactory/install_generator.rb +0 -21
- data/lib/generators/servactory/rspec_generator.rb +0 -88
- data/lib/generators/servactory/service_generator.rb +0 -49
- data/lib/servactory/configuration/setup.rb +0 -97
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +0 -68
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +0 -73
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/message_matcher.rb +0 -91
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +0 -72
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/schema_matcher.rb +0 -92
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/types_matcher.rb +0 -72
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/default_matcher.rb +0 -69
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/optional_matcher.rb +0 -63
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/required_matcher.rb +0 -81
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +0 -199
|
@@ -3,60 +3,262 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module ToolKit
|
|
5
5
|
module DynamicOptions
|
|
6
|
+
# Validates that attribute value is included in a specified set.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Inclusion provides set membership validation for attributes.
|
|
11
|
+
# It ensures that the value is present within a predefined list
|
|
12
|
+
# of acceptable values. This is similar to ActiveModel's inclusion
|
|
13
|
+
# validation and is useful for enum-like constraints.
|
|
14
|
+
#
|
|
15
|
+
# ## Usage
|
|
16
|
+
#
|
|
17
|
+
# This option is **included by default** for inputs, internals, and outputs.
|
|
18
|
+
# No registration required.
|
|
19
|
+
#
|
|
20
|
+
# Use in your service definition:
|
|
21
|
+
#
|
|
22
|
+
# ```ruby
|
|
23
|
+
# class CreateUserService < ApplicationService::Base
|
|
24
|
+
# input :role, type: String, inclusion: { in: %w[admin user guest] }
|
|
25
|
+
# input :status, type: Symbol, inclusion: { in: [:active, :inactive] }
|
|
26
|
+
# input :level, type: Integer, inclusion: { in: [1, 2, 3, 4, 5] }
|
|
27
|
+
# end
|
|
28
|
+
# ```
|
|
29
|
+
#
|
|
30
|
+
# ## Simple Mode
|
|
31
|
+
#
|
|
32
|
+
# Specify inclusion values directly:
|
|
33
|
+
#
|
|
34
|
+
# ```ruby
|
|
35
|
+
# class CreateUserService < ApplicationService::Base
|
|
36
|
+
# input :role, type: String, inclusion: %w[admin user guest]
|
|
37
|
+
# input :status, type: Symbol, inclusion: [:active, :inactive]
|
|
38
|
+
# input :level, type: Integer, inclusion: 1..10
|
|
39
|
+
# end
|
|
40
|
+
# ```
|
|
41
|
+
#
|
|
42
|
+
# ## Advanced Mode
|
|
43
|
+
#
|
|
44
|
+
# Specify inclusion with custom error message using a hash.
|
|
45
|
+
# Note: Advanced mode uses `:in` key (not `:is`).
|
|
46
|
+
#
|
|
47
|
+
# With static message:
|
|
48
|
+
#
|
|
49
|
+
# ```ruby
|
|
50
|
+
# input :role, type: String, inclusion: {
|
|
51
|
+
# in: %w[admin user guest],
|
|
52
|
+
# message: "Input `role` must be one of: admin, user, guest"
|
|
53
|
+
# }
|
|
54
|
+
# ```
|
|
55
|
+
#
|
|
56
|
+
# With dynamic lambda message:
|
|
57
|
+
#
|
|
58
|
+
# ```ruby
|
|
59
|
+
# input :role, type: String, inclusion: {
|
|
60
|
+
# in: %w[admin user guest],
|
|
61
|
+
# message: lambda do |input:, value:, option_value:, **|
|
|
62
|
+
# "Input `#{input.name}` has invalid value `#{value}`, allowed: #{option_value.join(', ')}"
|
|
63
|
+
# end
|
|
64
|
+
# }
|
|
65
|
+
# ```
|
|
66
|
+
#
|
|
67
|
+
# Lambda receives the following parameters:
|
|
68
|
+
# - For inputs: `input:, value:, option_value:, reason:, **`
|
|
69
|
+
# - For internals: `internal:, value:, option_value:, reason:, **`
|
|
70
|
+
# - For outputs: `output:, value:, option_value:, reason:, **`
|
|
71
|
+
#
|
|
72
|
+
# ## Validation Rules
|
|
73
|
+
#
|
|
74
|
+
# - Value must be present in the inclusion list
|
|
75
|
+
# - Supports arrays as inclusion sets
|
|
76
|
+
# - Optional inputs with nil value validate against default
|
|
77
|
+
# - Returns `:invalid_option` error if inclusion set is nil
|
|
78
|
+
#
|
|
79
|
+
# ## Important Notes
|
|
80
|
+
#
|
|
81
|
+
# - Use `inclusion: { in: [...] }` syntax for specifying allowed values
|
|
82
|
+
# - Single values are automatically wrapped in an array
|
|
83
|
+
# - For optional inputs with nil value, validates default if present
|
|
84
|
+
# - Range objects ARE supported natively: `(1..10)`, `(1..)`, `(..100)`
|
|
85
|
+
# - Mixed syntax supported: `[1..5, 10, 15..20]` checks value against all elements
|
|
6
86
|
class Inclusion < Must
|
|
87
|
+
# Creates an Inclusion validator instance.
|
|
88
|
+
#
|
|
89
|
+
# @param option_name [Symbol] The option name (default: :inclusion)
|
|
90
|
+
# @return [Servactory::Maintenance::Attributes::OptionHelper]
|
|
7
91
|
def self.use(option_name = :inclusion)
|
|
8
92
|
instance = new(option_name, :in)
|
|
9
93
|
instance.must(:be_inclusion)
|
|
10
94
|
end
|
|
11
95
|
|
|
12
|
-
|
|
96
|
+
# Validates inclusion condition for input attribute.
|
|
97
|
+
#
|
|
98
|
+
# @param input [Object] Input attribute object
|
|
99
|
+
# @param value [Object] Value to validate
|
|
100
|
+
# @param option [WorkOption] Inclusion configuration
|
|
101
|
+
# @return [Boolean, Array] true if valid, or [false, reason]
|
|
102
|
+
def condition_for_input_with(input:, value:, option:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
103
|
+
return [false, :invalid_option] if option.value.nil?
|
|
104
|
+
|
|
105
|
+
inclusion_values = normalize_inclusion_values(option.value)
|
|
106
|
+
|
|
107
|
+
# Required inputs or optional with non-nil value.
|
|
13
108
|
if input.required? || (input.optional? && !value.nil?) # rubocop:disable Style/IfUnlessModifier
|
|
14
|
-
return
|
|
109
|
+
return value_in_inclusion?(inclusion_values, value)
|
|
15
110
|
end
|
|
16
111
|
|
|
17
|
-
|
|
18
|
-
|
|
112
|
+
# Optional with nil value but has default.
|
|
113
|
+
if input.optional? && value.nil? && !input.default.nil?
|
|
114
|
+
return value_in_inclusion?(inclusion_values, input.default)
|
|
19
115
|
end
|
|
20
116
|
|
|
21
117
|
true
|
|
22
118
|
end
|
|
23
119
|
|
|
24
|
-
|
|
25
|
-
|
|
120
|
+
# Validates inclusion condition for internal attribute.
|
|
121
|
+
#
|
|
122
|
+
# @param value [Object] Value to validate
|
|
123
|
+
# @param option [WorkOption] Inclusion configuration
|
|
124
|
+
# @return [Boolean, Array] true if valid, or [false, reason]
|
|
125
|
+
def condition_for_internal_with(value:, option:, **)
|
|
126
|
+
return [false, :invalid_option] if option.value.nil?
|
|
127
|
+
|
|
128
|
+
inclusion_values = normalize_inclusion_values(option.value)
|
|
129
|
+
value_in_inclusion?(inclusion_values, value)
|
|
26
130
|
end
|
|
27
131
|
|
|
28
|
-
|
|
29
|
-
|
|
132
|
+
# Validates inclusion condition for output attribute.
|
|
133
|
+
#
|
|
134
|
+
# @param value [Object] Value to validate
|
|
135
|
+
# @param option [WorkOption] Inclusion configuration
|
|
136
|
+
# @return [Boolean, Array] true if valid, or [false, reason]
|
|
137
|
+
def condition_for_output_with(value:, option:, **)
|
|
138
|
+
return [false, :invalid_option] if option.value.nil?
|
|
139
|
+
|
|
140
|
+
inclusion_values = normalize_inclusion_values(option.value)
|
|
141
|
+
value_in_inclusion?(inclusion_values, value)
|
|
30
142
|
end
|
|
31
143
|
|
|
32
144
|
########################################################################
|
|
33
145
|
|
|
34
|
-
|
|
146
|
+
# Generates error message for input validation failure.
|
|
147
|
+
#
|
|
148
|
+
# @param service [Object] Service context
|
|
149
|
+
# @param input [Object] Input attribute
|
|
150
|
+
# @param value [Object] Failed value
|
|
151
|
+
# @param option_name [Symbol] Option name
|
|
152
|
+
# @param option_value [Object] Allowed values
|
|
153
|
+
# @param reason [Symbol] Failure reason
|
|
154
|
+
# @return [String] Localized error message
|
|
155
|
+
def message_for_input_with(service:, input:, value:, option_name:, option_value:, reason:, **)
|
|
156
|
+
i18n_key = "inputs.validations.must.dynamic_options.inclusion"
|
|
157
|
+
i18n_key += reason.present? ? ".#{reason}" : ".default"
|
|
158
|
+
|
|
35
159
|
service.translate(
|
|
36
|
-
|
|
160
|
+
i18n_key,
|
|
37
161
|
input_name: input.name,
|
|
38
162
|
value: value.inspect,
|
|
39
|
-
input_inclusion: option_value.inspect
|
|
163
|
+
input_inclusion: option_value.inspect,
|
|
164
|
+
option_name:
|
|
40
165
|
)
|
|
41
166
|
end
|
|
42
167
|
|
|
43
|
-
|
|
168
|
+
# Generates error message for internal validation failure.
|
|
169
|
+
#
|
|
170
|
+
# @param service [Object] Service context
|
|
171
|
+
# @param internal [Object] Internal attribute
|
|
172
|
+
# @param value [Object] Failed value
|
|
173
|
+
# @param option_name [Symbol] Option name
|
|
174
|
+
# @param option_value [Object] Allowed values
|
|
175
|
+
# @param reason [Symbol] Failure reason
|
|
176
|
+
# @return [String] Localized error message
|
|
177
|
+
def message_for_internal_with(service:, internal:, value:, option_name:, option_value:, reason:, **)
|
|
178
|
+
i18n_key = "internals.validations.must.dynamic_options.inclusion"
|
|
179
|
+
i18n_key += reason.present? ? ".#{reason}" : ".default"
|
|
180
|
+
|
|
44
181
|
service.translate(
|
|
45
|
-
|
|
182
|
+
i18n_key,
|
|
46
183
|
internal_name: internal.name,
|
|
47
184
|
value: value.inspect,
|
|
48
|
-
internal_inclusion: option_value.inspect
|
|
185
|
+
internal_inclusion: option_value.inspect,
|
|
186
|
+
option_name:
|
|
49
187
|
)
|
|
50
188
|
end
|
|
51
189
|
|
|
52
|
-
|
|
190
|
+
# Generates error message for output validation failure.
|
|
191
|
+
#
|
|
192
|
+
# @param service [Object] Service context
|
|
193
|
+
# @param output [Object] Output attribute
|
|
194
|
+
# @param value [Object] Failed value
|
|
195
|
+
# @param option_name [Symbol] Option name
|
|
196
|
+
# @param option_value [Object] Allowed values
|
|
197
|
+
# @param reason [Symbol] Failure reason
|
|
198
|
+
# @return [String] Localized error message
|
|
199
|
+
def message_for_output_with(service:, output:, value:, option_name:, option_value:, reason:, **)
|
|
200
|
+
i18n_key = "outputs.validations.must.dynamic_options.inclusion"
|
|
201
|
+
i18n_key += reason.present? ? ".#{reason}" : ".default"
|
|
202
|
+
|
|
53
203
|
service.translate(
|
|
54
|
-
|
|
204
|
+
i18n_key,
|
|
55
205
|
output_name: output.name,
|
|
56
206
|
value: value.inspect,
|
|
57
|
-
output_inclusion: option_value.inspect
|
|
207
|
+
output_inclusion: option_value.inspect,
|
|
208
|
+
option_name:
|
|
58
209
|
)
|
|
59
210
|
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
# Normalizes inclusion values, preserving Range objects.
|
|
215
|
+
#
|
|
216
|
+
# @param option_value [Range, Array, Object] Inclusion value(s)
|
|
217
|
+
# @return [Range, Array] Range preserved as-is, others normalized to array
|
|
218
|
+
def normalize_inclusion_values(option_value)
|
|
219
|
+
case option_value
|
|
220
|
+
when Range, Array then option_value
|
|
221
|
+
else [option_value]
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Checks if value is included in the normalized inclusion values.
|
|
226
|
+
#
|
|
227
|
+
# @param inclusion_values [Range, Array] Normalized inclusion set
|
|
228
|
+
# @param value [Object] Value to check
|
|
229
|
+
# @return [Boolean] true if value is in inclusion set
|
|
230
|
+
def value_in_inclusion?(inclusion_values, value)
|
|
231
|
+
case inclusion_values
|
|
232
|
+
when Range
|
|
233
|
+
range_covers?(inclusion_values, value)
|
|
234
|
+
when Array
|
|
235
|
+
inclusion_values.any? { |element| element_matches?(element, value) }
|
|
236
|
+
else
|
|
237
|
+
inclusion_values == value
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Checks if value matches a single element (Range or scalar).
|
|
242
|
+
#
|
|
243
|
+
# @param element [Range, Object] Element to match against
|
|
244
|
+
# @param value [Object] Value to check
|
|
245
|
+
# @return [Boolean] true if matches
|
|
246
|
+
def element_matches?(element, value)
|
|
247
|
+
element.is_a?(Range) ? range_covers?(element, value) : element == value
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Safely checks if Range covers the value.
|
|
251
|
+
#
|
|
252
|
+
# @param range [Range] Range to check against
|
|
253
|
+
# @param value [Object] Value to check
|
|
254
|
+
# @return [Boolean] true if range covers value, false on type errors
|
|
255
|
+
def range_covers?(range, value)
|
|
256
|
+
return false if value.nil?
|
|
257
|
+
|
|
258
|
+
range.cover?(value)
|
|
259
|
+
rescue ArgumentError, TypeError
|
|
260
|
+
false
|
|
261
|
+
end
|
|
60
262
|
end
|
|
61
263
|
end
|
|
62
264
|
end
|
|
@@ -3,28 +3,147 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module ToolKit
|
|
5
5
|
module DynamicOptions
|
|
6
|
+
# Validates that attribute value does not exceed a maximum limit.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Max provides upper bound validation for numeric values and
|
|
11
|
+
# size-based validation for collections and strings. It ensures
|
|
12
|
+
# that values stay within acceptable limits.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# This option is **NOT included by default**. Register it for each
|
|
17
|
+
# attribute type where you want to use it:
|
|
18
|
+
#
|
|
19
|
+
# ```ruby
|
|
20
|
+
# configuration do
|
|
21
|
+
# input_option_helpers([
|
|
22
|
+
# Servactory::ToolKit::DynamicOptions::Max.use
|
|
23
|
+
# ])
|
|
24
|
+
#
|
|
25
|
+
# internal_option_helpers([
|
|
26
|
+
# Servactory::ToolKit::DynamicOptions::Max.use
|
|
27
|
+
# ])
|
|
28
|
+
#
|
|
29
|
+
# output_option_helpers([
|
|
30
|
+
# Servactory::ToolKit::DynamicOptions::Max.use
|
|
31
|
+
# ])
|
|
32
|
+
# end
|
|
33
|
+
# ```
|
|
34
|
+
#
|
|
35
|
+
# Use in your service definition:
|
|
36
|
+
#
|
|
37
|
+
# ```ruby
|
|
38
|
+
# class ProcessDataService < ApplicationService::Base
|
|
39
|
+
# input :count, type: Integer, max: 100
|
|
40
|
+
# input :name, type: String, max: 255
|
|
41
|
+
# input :items, type: Array, max: 50
|
|
42
|
+
# end
|
|
43
|
+
# ```
|
|
44
|
+
#
|
|
45
|
+
# ## Simple Mode
|
|
46
|
+
#
|
|
47
|
+
# Specify maximum value directly:
|
|
48
|
+
#
|
|
49
|
+
# ```ruby
|
|
50
|
+
# class ProcessDataService < ApplicationService::Base
|
|
51
|
+
# input :count, type: Integer, max: 100
|
|
52
|
+
# input :name, type: String, max: 255
|
|
53
|
+
# input :items, type: Array, max: 50
|
|
54
|
+
# end
|
|
55
|
+
# ```
|
|
56
|
+
#
|
|
57
|
+
# ## Advanced Mode
|
|
58
|
+
#
|
|
59
|
+
# Specify maximum with custom error message using a hash:
|
|
60
|
+
#
|
|
61
|
+
# With static message:
|
|
62
|
+
#
|
|
63
|
+
# ```ruby
|
|
64
|
+
# input :count, type: Integer, max: {
|
|
65
|
+
# is: 100,
|
|
66
|
+
# message: "Input `count` must not exceed 100"
|
|
67
|
+
# }
|
|
68
|
+
# ```
|
|
69
|
+
#
|
|
70
|
+
# With dynamic lambda message:
|
|
71
|
+
#
|
|
72
|
+
# ```ruby
|
|
73
|
+
# input :count, type: Integer, max: {
|
|
74
|
+
# is: 100,
|
|
75
|
+
# message: lambda do |input:, value:, option_value:, **|
|
|
76
|
+
# "Input `#{input.name}` must be <= #{option_value}, got #{value}"
|
|
77
|
+
# end
|
|
78
|
+
# }
|
|
79
|
+
# ```
|
|
80
|
+
#
|
|
81
|
+
# Lambda receives the following parameters:
|
|
82
|
+
# - For inputs: `input:, option_value:, value:, **`
|
|
83
|
+
# - For internals: `internal:, option_value:, value:, **`
|
|
84
|
+
# - For outputs: `output:, option_value:, value:, **`
|
|
85
|
+
#
|
|
86
|
+
# ## Validation Rules
|
|
87
|
+
#
|
|
88
|
+
# - For Integer: value must be <= max
|
|
89
|
+
# - For String/Array/Hash: size must be <= max
|
|
90
|
+
# - Objects must respond to `:size` method for size-based validation
|
|
91
|
+
#
|
|
92
|
+
# ## Important Notes
|
|
93
|
+
#
|
|
94
|
+
# - Returns false if value doesn't support size comparison
|
|
95
|
+
# - Combines well with `:min` for range validation
|
|
6
96
|
class Max < Must
|
|
97
|
+
# Creates a Max validator instance.
|
|
98
|
+
#
|
|
99
|
+
# @param option_name [Symbol] The option name (default: :max)
|
|
100
|
+
# @return [Servactory::Maintenance::Attributes::OptionHelper]
|
|
7
101
|
def self.use(option_name = :max)
|
|
8
102
|
new(option_name).must(:be_less_than_or_equal_to)
|
|
9
103
|
end
|
|
10
104
|
|
|
105
|
+
# Validates max condition for input attribute.
|
|
106
|
+
#
|
|
107
|
+
# @param input [Object] Input attribute object
|
|
108
|
+
# @param value [Object] Value to validate
|
|
109
|
+
# @param option [WorkOption] Max configuration
|
|
110
|
+
# @return [Boolean] true if valid
|
|
11
111
|
def condition_for_input_with(...)
|
|
12
112
|
common_condition_with(...)
|
|
13
113
|
end
|
|
14
114
|
|
|
115
|
+
# Validates max condition for internal attribute.
|
|
116
|
+
#
|
|
117
|
+
# @param internal [Object] Internal attribute object
|
|
118
|
+
# @param value [Object] Value to validate
|
|
119
|
+
# @param option [WorkOption] Max configuration
|
|
120
|
+
# @return [Boolean] true if valid
|
|
15
121
|
def condition_for_internal_with(...)
|
|
16
122
|
common_condition_with(...)
|
|
17
123
|
end
|
|
18
124
|
|
|
125
|
+
# Validates max condition for output attribute.
|
|
126
|
+
#
|
|
127
|
+
# @param output [Object] Output attribute object
|
|
128
|
+
# @param value [Object] Value to validate
|
|
129
|
+
# @param option [WorkOption] Max configuration
|
|
130
|
+
# @return [Boolean] true if valid
|
|
19
131
|
def condition_for_output_with(...)
|
|
20
132
|
common_condition_with(...)
|
|
21
133
|
end
|
|
22
134
|
|
|
135
|
+
# Common validation logic for all attribute types.
|
|
136
|
+
#
|
|
137
|
+
# @param value [Object] Value to validate
|
|
138
|
+
# @param option [WorkOption] Max configuration
|
|
139
|
+
# @return [Boolean] true if value <= max
|
|
23
140
|
def common_condition_with(value:, option:, **) # rubocop:disable Naming/PredicateMethod
|
|
24
141
|
case value
|
|
25
142
|
when Integer
|
|
143
|
+
# Direct numeric comparison.
|
|
26
144
|
value <= option.value
|
|
27
145
|
else
|
|
146
|
+
# Size-based comparison for collections and strings.
|
|
28
147
|
return false unless value.respond_to?(:size)
|
|
29
148
|
|
|
30
149
|
value.size <= option.value
|
|
@@ -33,6 +152,14 @@ module Servactory
|
|
|
33
152
|
|
|
34
153
|
########################################################################
|
|
35
154
|
|
|
155
|
+
# Generates error message for input validation failure.
|
|
156
|
+
#
|
|
157
|
+
# @param service [Object] Service context
|
|
158
|
+
# @param input [Object] Input attribute
|
|
159
|
+
# @param value [Object] Failed value
|
|
160
|
+
# @param option_name [Symbol] Option name
|
|
161
|
+
# @param option_value [Object] Maximum limit
|
|
162
|
+
# @return [String] Localized error message
|
|
36
163
|
def message_for_input_with(service:, input:, value:, option_name:, option_value:, **)
|
|
37
164
|
service.translate(
|
|
38
165
|
"inputs.validations.must.dynamic_options.max.default",
|
|
@@ -43,6 +170,14 @@ module Servactory
|
|
|
43
170
|
)
|
|
44
171
|
end
|
|
45
172
|
|
|
173
|
+
# Generates error message for internal validation failure.
|
|
174
|
+
#
|
|
175
|
+
# @param service [Object] Service context
|
|
176
|
+
# @param internal [Object] Internal attribute
|
|
177
|
+
# @param value [Object] Failed value
|
|
178
|
+
# @param option_name [Symbol] Option name
|
|
179
|
+
# @param option_value [Object] Maximum limit
|
|
180
|
+
# @return [String] Localized error message
|
|
46
181
|
def message_for_internal_with(service:, internal:, value:, option_name:, option_value:, **)
|
|
47
182
|
service.translate(
|
|
48
183
|
"internals.validations.must.dynamic_options.max.default",
|
|
@@ -53,6 +188,14 @@ module Servactory
|
|
|
53
188
|
)
|
|
54
189
|
end
|
|
55
190
|
|
|
191
|
+
# Generates error message for output validation failure.
|
|
192
|
+
#
|
|
193
|
+
# @param service [Object] Service context
|
|
194
|
+
# @param output [Object] Output attribute
|
|
195
|
+
# @param value [Object] Failed value
|
|
196
|
+
# @param option_name [Symbol] Option name
|
|
197
|
+
# @param option_value [Object] Maximum limit
|
|
198
|
+
# @return [String] Localized error message
|
|
56
199
|
def message_for_output_with(service:, output:, value:, option_name:, option_value:, **)
|
|
57
200
|
service.translate(
|
|
58
201
|
"outputs.validations.must.dynamic_options.max.default",
|
|
@@ -3,28 +3,147 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module ToolKit
|
|
5
5
|
module DynamicOptions
|
|
6
|
+
# Validates that attribute value meets a minimum threshold.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Min provides lower bound validation for numeric values and
|
|
11
|
+
# size-based validation for collections and strings. It ensures
|
|
12
|
+
# that values meet minimum requirements.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# This option is **NOT included by default**. Register it for each
|
|
17
|
+
# attribute type where you want to use it:
|
|
18
|
+
#
|
|
19
|
+
# ```ruby
|
|
20
|
+
# configuration do
|
|
21
|
+
# input_option_helpers([
|
|
22
|
+
# Servactory::ToolKit::DynamicOptions::Min.use
|
|
23
|
+
# ])
|
|
24
|
+
#
|
|
25
|
+
# internal_option_helpers([
|
|
26
|
+
# Servactory::ToolKit::DynamicOptions::Min.use
|
|
27
|
+
# ])
|
|
28
|
+
#
|
|
29
|
+
# output_option_helpers([
|
|
30
|
+
# Servactory::ToolKit::DynamicOptions::Min.use
|
|
31
|
+
# ])
|
|
32
|
+
# end
|
|
33
|
+
# ```
|
|
34
|
+
#
|
|
35
|
+
# Use in your service definition:
|
|
36
|
+
#
|
|
37
|
+
# ```ruby
|
|
38
|
+
# class ProcessDataService < ApplicationService::Base
|
|
39
|
+
# input :age, type: Integer, min: 18
|
|
40
|
+
# input :password, type: String, min: 8
|
|
41
|
+
# input :tags, type: Array, min: 1
|
|
42
|
+
# end
|
|
43
|
+
# ```
|
|
44
|
+
#
|
|
45
|
+
# ## Simple Mode
|
|
46
|
+
#
|
|
47
|
+
# Specify minimum value directly:
|
|
48
|
+
#
|
|
49
|
+
# ```ruby
|
|
50
|
+
# class ProcessDataService < ApplicationService::Base
|
|
51
|
+
# input :age, type: Integer, min: 18
|
|
52
|
+
# input :password, type: String, min: 8
|
|
53
|
+
# input :tags, type: Array, min: 1
|
|
54
|
+
# end
|
|
55
|
+
# ```
|
|
56
|
+
#
|
|
57
|
+
# ## Advanced Mode
|
|
58
|
+
#
|
|
59
|
+
# Specify minimum with custom error message using a hash:
|
|
60
|
+
#
|
|
61
|
+
# With static message:
|
|
62
|
+
#
|
|
63
|
+
# ```ruby
|
|
64
|
+
# input :age, type: Integer, min: {
|
|
65
|
+
# is: 18,
|
|
66
|
+
# message: "Input `age` must be at least 18"
|
|
67
|
+
# }
|
|
68
|
+
# ```
|
|
69
|
+
#
|
|
70
|
+
# With dynamic lambda message:
|
|
71
|
+
#
|
|
72
|
+
# ```ruby
|
|
73
|
+
# input :age, type: Integer, min: {
|
|
74
|
+
# is: 18,
|
|
75
|
+
# message: lambda do |input:, value:, option_value:, **|
|
|
76
|
+
# "Input `#{input.name}` must be >= #{option_value}, got #{value}"
|
|
77
|
+
# end
|
|
78
|
+
# }
|
|
79
|
+
# ```
|
|
80
|
+
#
|
|
81
|
+
# Lambda receives the following parameters:
|
|
82
|
+
# - For inputs: `input:, option_value:, value:, **`
|
|
83
|
+
# - For internals: `internal:, option_value:, value:, **`
|
|
84
|
+
# - For outputs: `output:, option_value:, value:, **`
|
|
85
|
+
#
|
|
86
|
+
# ## Validation Rules
|
|
87
|
+
#
|
|
88
|
+
# - For Integer: value must be >= min
|
|
89
|
+
# - For String/Array/Hash: size must be >= min
|
|
90
|
+
# - Objects must respond to `:size` method for size-based validation
|
|
91
|
+
#
|
|
92
|
+
# ## Important Notes
|
|
93
|
+
#
|
|
94
|
+
# - Returns false if value doesn't support size comparison
|
|
95
|
+
# - Combines well with `:max` for range validation
|
|
6
96
|
class Min < Must
|
|
97
|
+
# Creates a Min validator instance.
|
|
98
|
+
#
|
|
99
|
+
# @param option_name [Symbol] The option name (default: :min)
|
|
100
|
+
# @return [Servactory::Maintenance::Attributes::OptionHelper]
|
|
7
101
|
def self.use(option_name = :min)
|
|
8
102
|
new(option_name).must(:be_greater_than_or_equal_to)
|
|
9
103
|
end
|
|
10
104
|
|
|
105
|
+
# Validates min condition for input attribute.
|
|
106
|
+
#
|
|
107
|
+
# @param input [Object] Input attribute object
|
|
108
|
+
# @param value [Object] Value to validate
|
|
109
|
+
# @param option [WorkOption] Min configuration
|
|
110
|
+
# @return [Boolean] true if valid
|
|
11
111
|
def condition_for_input_with(...)
|
|
12
112
|
common_condition_with(...)
|
|
13
113
|
end
|
|
14
114
|
|
|
115
|
+
# Validates min condition for internal attribute.
|
|
116
|
+
#
|
|
117
|
+
# @param internal [Object] Internal attribute object
|
|
118
|
+
# @param value [Object] Value to validate
|
|
119
|
+
# @param option [WorkOption] Min configuration
|
|
120
|
+
# @return [Boolean] true if valid
|
|
15
121
|
def condition_for_internal_with(...)
|
|
16
122
|
common_condition_with(...)
|
|
17
123
|
end
|
|
18
124
|
|
|
125
|
+
# Validates min condition for output attribute.
|
|
126
|
+
#
|
|
127
|
+
# @param output [Object] Output attribute object
|
|
128
|
+
# @param value [Object] Value to validate
|
|
129
|
+
# @param option [WorkOption] Min configuration
|
|
130
|
+
# @return [Boolean] true if valid
|
|
19
131
|
def condition_for_output_with(...)
|
|
20
132
|
common_condition_with(...)
|
|
21
133
|
end
|
|
22
134
|
|
|
135
|
+
# Common validation logic for all attribute types.
|
|
136
|
+
#
|
|
137
|
+
# @param value [Object] Value to validate
|
|
138
|
+
# @param option [WorkOption] Min configuration
|
|
139
|
+
# @return [Boolean] true if value >= min
|
|
23
140
|
def common_condition_with(value:, option:, **) # rubocop:disable Naming/PredicateMethod
|
|
24
141
|
case value
|
|
25
142
|
when Integer
|
|
143
|
+
# Direct numeric comparison.
|
|
26
144
|
value >= option.value
|
|
27
145
|
else
|
|
146
|
+
# Size-based comparison for collections and strings.
|
|
28
147
|
return false unless value.respond_to?(:size)
|
|
29
148
|
|
|
30
149
|
value.size >= option.value
|
|
@@ -33,6 +152,14 @@ module Servactory
|
|
|
33
152
|
|
|
34
153
|
########################################################################
|
|
35
154
|
|
|
155
|
+
# Generates error message for input validation failure.
|
|
156
|
+
#
|
|
157
|
+
# @param service [Object] Service context
|
|
158
|
+
# @param input [Object] Input attribute
|
|
159
|
+
# @param value [Object] Failed value
|
|
160
|
+
# @param option_name [Symbol] Option name
|
|
161
|
+
# @param option_value [Object] Minimum limit
|
|
162
|
+
# @return [String] Localized error message
|
|
36
163
|
def message_for_input_with(service:, input:, value:, option_name:, option_value:, **)
|
|
37
164
|
service.translate(
|
|
38
165
|
"inputs.validations.must.dynamic_options.min.default",
|
|
@@ -43,6 +170,14 @@ module Servactory
|
|
|
43
170
|
)
|
|
44
171
|
end
|
|
45
172
|
|
|
173
|
+
# Generates error message for internal validation failure.
|
|
174
|
+
#
|
|
175
|
+
# @param service [Object] Service context
|
|
176
|
+
# @param internal [Object] Internal attribute
|
|
177
|
+
# @param value [Object] Failed value
|
|
178
|
+
# @param option_name [Symbol] Option name
|
|
179
|
+
# @param option_value [Object] Minimum limit
|
|
180
|
+
# @return [String] Localized error message
|
|
46
181
|
def message_for_internal_with(service:, internal:, value:, option_name:, option_value:, **)
|
|
47
182
|
service.translate(
|
|
48
183
|
"internals.validations.must.dynamic_options.min.default",
|
|
@@ -53,6 +188,14 @@ module Servactory
|
|
|
53
188
|
)
|
|
54
189
|
end
|
|
55
190
|
|
|
191
|
+
# Generates error message for output validation failure.
|
|
192
|
+
#
|
|
193
|
+
# @param service [Object] Service context
|
|
194
|
+
# @param output [Object] Output attribute
|
|
195
|
+
# @param value [Object] Failed value
|
|
196
|
+
# @param option_name [Symbol] Option name
|
|
197
|
+
# @param option_value [Object] Minimum limit
|
|
198
|
+
# @return [String] Localized error message
|
|
56
199
|
def message_for_output_with(service:, output:, value:, option_name:, option_value:, **)
|
|
57
200
|
service.translate(
|
|
58
201
|
"outputs.validations.must.dynamic_options.min.default",
|