servactory 2.12.0.rc2 → 2.12.0.rc4

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/en.yml +12 -6
  3. data/config/locales/ru.yml +12 -6
  4. data/lib/servactory/configuration/setup.rb +3 -0
  5. data/lib/servactory/context/workspace/inputs.rb +0 -35
  6. data/lib/servactory/inputs/dsl.rb +0 -1
  7. data/lib/servactory/inputs/input.rb +0 -4
  8. data/lib/servactory/internals/dsl.rb +0 -1
  9. data/lib/servactory/internals/internal.rb +0 -4
  10. data/lib/servactory/maintenance/attributes/option.rb +7 -13
  11. data/lib/servactory/maintenance/attributes/options/registrar.rb +1 -23
  12. data/lib/servactory/maintenance/attributes/translator/must.rb +1 -1
  13. data/lib/servactory/maintenance/attributes/translator/type.rb +3 -38
  14. data/lib/servactory/maintenance/attributes/validations/must.rb +8 -6
  15. data/lib/servactory/maintenance/validations/types.rb +3 -26
  16. data/lib/servactory/outputs/dsl.rb +0 -1
  17. data/lib/servactory/outputs/output.rb +0 -4
  18. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +9 -26
  19. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +11 -27
  20. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/message_matcher.rb +91 -0
  21. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +6 -0
  22. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/schema_matcher.rb +83 -0
  23. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +30 -11
  24. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +7 -1
  25. data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +32 -11
  26. data/lib/servactory/tool_kit/dynamic_options/must.rb +29 -5
  27. data/lib/servactory/tool_kit/dynamic_options/schema.rb +197 -0
  28. data/lib/servactory/version.rb +1 -1
  29. metadata +5 -3
  30. data/lib/servactory/maintenance/validations/object_schema.rb +0 -116
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module ToolKit
5
+ module DynamicOptions
6
+ class Schema < Must # rubocop:disable Metrics/ClassLength
7
+ RESERVED_ATTRIBUTES = %i[type required default].freeze
8
+ private_constant :RESERVED_ATTRIBUTES
9
+
10
+ def self.use(option_name = :schema, default_hash_mode_class_names:)
11
+ instance = new(option_name, :is, false)
12
+ instance.assign(default_hash_mode_class_names)
13
+ instance.must(:schema)
14
+ end
15
+
16
+ def assign(default_hash_mode_class_names)
17
+ @default_hash_mode_class_names = default_hash_mode_class_names
18
+ end
19
+
20
+ def condition_for_input_with(input:, value:, option:)
21
+ common_condition_with(attribute: input, value:, option:)
22
+ end
23
+
24
+ def condition_for_internal_with(internal:, value:, option:)
25
+ common_condition_with(attribute: internal, value:, option:)
26
+ end
27
+
28
+ def condition_for_output_with(output:, value:, option:)
29
+ common_condition_with(attribute: output, value:, option:)
30
+ end
31
+
32
+ def common_condition_with(attribute:, value:, option:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
33
+ return true if option.value == false
34
+ return [false, :wrong_type] if @default_hash_mode_class_names.intersection(attribute.types).empty?
35
+
36
+ if value.blank? && ((attribute.input? && attribute.optional?) || attribute.internal? || attribute.output?)
37
+ return true
38
+ end
39
+
40
+ schema = option.value.fetch(:is, option.value)
41
+
42
+ is_success, reason, meta = validate_for!(object: value, schema:)
43
+
44
+ prepare_object_with!(object: value, schema:) if is_success
45
+
46
+ [is_success, reason, meta]
47
+ end
48
+
49
+ def validate_for!(object:, schema:, root_schema_key: nil) # rubocop:disable Metrics/MethodLength
50
+ unless object.respond_to?(:fetch)
51
+ return [
52
+ false,
53
+ :wrong_element_value,
54
+ {
55
+ key_name: root_schema_key,
56
+ expected_type: Hash.name,
57
+ given_type: object.class.name
58
+ }
59
+ ]
60
+ end
61
+
62
+ errors = schema.map do |schema_key, schema_value|
63
+ attribute_type = schema_value.fetch(:type, String)
64
+
65
+ if attribute_type == Hash
66
+ validate_for!(
67
+ object: object.fetch(schema_key, {}),
68
+ schema: schema_value.except(*RESERVED_ATTRIBUTES),
69
+ root_schema_key: schema_key
70
+ )
71
+ else
72
+ is_success, given_type = validate_with(
73
+ object:,
74
+ schema_key:,
75
+ schema_value:,
76
+ attribute_type:,
77
+ attribute_required: schema_value.fetch(:required, true)
78
+ )
79
+
80
+ next if is_success
81
+
82
+ [false, :wrong_element_type, { key_name: schema_key, expected_type: attribute_type, given_type: }]
83
+ end
84
+ end
85
+
86
+ errors.compact.first || true
87
+ end
88
+
89
+ def validate_with(object:, schema_key:, schema_value:, attribute_type:, attribute_required:) # rubocop:disable Metrics/MethodLength
90
+ unless should_be_checked_for?(
91
+ object:,
92
+ schema_key:,
93
+ schema_value:,
94
+ required: attribute_required
95
+ ) # do
96
+ return true
97
+ end
98
+
99
+ value = object.fetch(schema_key, nil)
100
+ prepared_value = prepare_value_from(schema_value:, value:, required: attribute_required)
101
+
102
+ [
103
+ Array(attribute_type).uniq.any? { |type| prepared_value.is_a?(type) },
104
+ prepared_value.class.name
105
+ ]
106
+ end
107
+
108
+ def should_be_checked_for?(object:, schema_key:, schema_value:, required:)
109
+ required || (
110
+ !required && !fetch_default_from(schema_value).nil?
111
+ ) || (
112
+ !required && !object.fetch(schema_key, nil).nil?
113
+ )
114
+ end
115
+
116
+ def prepare_value_from(schema_value:, value:, required:)
117
+ if !required && !fetch_default_from(schema_value).nil? && value.blank?
118
+ fetch_default_from(schema_value)
119
+ else
120
+ value
121
+ end
122
+ end
123
+
124
+ def fetch_default_from(value)
125
+ value.fetch(:default, nil)
126
+ end
127
+
128
+ ########################################################################
129
+
130
+ def prepare_object_with!(object:, schema:) # rubocop:disable Metrics/MethodLength
131
+ schema.map do |schema_key, schema_value|
132
+ attribute_type = schema_value.fetch(:type, String)
133
+
134
+ if attribute_type == Hash
135
+ prepare_object_with!(
136
+ object: object.fetch(schema_key, {}),
137
+ schema: schema_value.except(*RESERVED_ATTRIBUTES)
138
+ )
139
+ else
140
+ next object unless object[schema_key].nil?
141
+
142
+ next object if (default = schema_value.fetch(:default, nil)).nil?
143
+
144
+ object[schema_key] = default
145
+ end
146
+ end
147
+ end
148
+
149
+ ########################################################################
150
+ ########################################################################
151
+ ########################################################################
152
+
153
+ def message_for_input_with(service:, input:, reason:, meta:, **)
154
+ i18n_key = "inputs.validations.must.dynamic_options.schema"
155
+ i18n_key += reason.present? ? ".#{reason}" : ".default"
156
+
157
+ service.translate(
158
+ i18n_key,
159
+ service_class_name: service.class_name,
160
+ input_name: input.name,
161
+ key_name: meta.fetch(:key_name),
162
+ expected_type: meta.fetch(:expected_type),
163
+ given_type: meta.fetch(:given_type)
164
+ )
165
+ end
166
+
167
+ def message_for_internal_with(service:, internal:, reason:, meta:, **)
168
+ i18n_key = "internals.validations.must.dynamic_options.schema"
169
+ i18n_key += reason.present? ? ".#{reason}" : ".default"
170
+
171
+ service.translate(
172
+ i18n_key,
173
+ service_class_name: service.class_name,
174
+ internal_name: internal.name,
175
+ key_name: meta.fetch(:key_name),
176
+ expected_type: meta.fetch(:expected_type),
177
+ given_type: meta.fetch(:given_type)
178
+ )
179
+ end
180
+
181
+ def message_for_output_with(service:, output:, reason:, meta:, **)
182
+ i18n_key = "outputs.validations.must.dynamic_options.schema"
183
+ i18n_key += reason.present? ? ".#{reason}" : ".default"
184
+
185
+ service.translate(
186
+ i18n_key,
187
+ service_class_name: service.class_name,
188
+ output_name: output.name,
189
+ key_name: meta.fetch(:key_name),
190
+ expected_type: meta.fetch(:expected_type),
191
+ given_type: meta.fetch(:given_type)
192
+ )
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -5,7 +5,7 @@ module Servactory
5
5
  MAJOR = 2
6
6
  MINOR = 12
7
7
  PATCH = 0
8
- PRE = "rc2"
8
+ PRE = "rc4"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servactory
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.12.0.rc2
4
+ version: 2.12.0.rc4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-18 00:00:00.000000000 Z
10
+ date: 2025-03-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -298,7 +298,6 @@ files:
298
298
  - lib/servactory/maintenance/attributes/validations/errors.rb
299
299
  - lib/servactory/maintenance/attributes/validations/must.rb
300
300
  - lib/servactory/maintenance/attributes/validations/type.rb
301
- - lib/servactory/maintenance/validations/object_schema.rb
302
301
  - lib/servactory/maintenance/validations/types.rb
303
302
  - lib/servactory/outputs/collection.rb
304
303
  - lib/servactory/outputs/dsl.rb
@@ -310,7 +309,9 @@ files:
310
309
  - lib/servactory/test_kit/rspec/matchers.rb
311
310
  - lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb
312
311
  - lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb
312
+ - lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/message_matcher.rb
313
313
  - lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb
314
+ - lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/schema_matcher.rb
314
315
  - lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/types_matcher.rb
315
316
  - lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb
316
317
  - lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/default_matcher.rb
@@ -326,6 +327,7 @@ files:
326
327
  - lib/servactory/tool_kit/dynamic_options/min.rb
327
328
  - lib/servactory/tool_kit/dynamic_options/multiple_of.rb
328
329
  - lib/servactory/tool_kit/dynamic_options/must.rb
330
+ - lib/servactory/tool_kit/dynamic_options/schema.rb
329
331
  - lib/servactory/utils.rb
330
332
  - lib/servactory/version.rb
331
333
  homepage: https://github.com/servactory/servactory
@@ -1,116 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- module Maintenance
5
- module Validations
6
- class ObjectSchema
7
- RESERVED_ATTRIBUTES = %i[type required default].freeze
8
- private_constant :RESERVED_ATTRIBUTES
9
-
10
- attr_reader :errors
11
-
12
- def self.validate(...)
13
- new(...).validate
14
- end
15
-
16
- def initialize(object:, schema:)
17
- @object = object
18
- @schema = schema.fetch(:is)
19
-
20
- @errors = []
21
- end
22
-
23
- def validate
24
- validate_for!(object: @object, schema: @schema)
25
-
26
- self
27
- end
28
-
29
- def valid?
30
- @errors.empty?
31
- end
32
-
33
- private
34
-
35
- def validate_for!(object:, schema:, root_schema_key: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
36
- unless object.respond_to?(:fetch)
37
- add_error(key_name: root_schema_key, expected_type: Hash.name, given_type: object.class.name)
38
- return
39
- end
40
-
41
- schema.each do |schema_key, schema_value|
42
- attribute_type = schema_value.fetch(:type, String)
43
-
44
- if attribute_type == Hash
45
- validate_for!(
46
- object: object.fetch(schema_key, {}),
47
- schema: schema_value.except(*RESERVED_ATTRIBUTES),
48
- root_schema_key: schema_key
49
- )
50
- else
51
- is_success = validate_with(
52
- object:,
53
- schema_key:,
54
- schema_value:,
55
- attribute_type:,
56
- attribute_required: schema_value.fetch(:required, true)
57
- )
58
-
59
- next if is_success
60
-
61
- add_error(
62
- key_name: schema_key,
63
- expected_type: attribute_type,
64
- given_type: object.fetch(schema_key, nil).class.name
65
- )
66
- end
67
- end
68
- end
69
-
70
- def validate_with(object:, schema_key:, schema_value:, attribute_type:, attribute_required:) # rubocop:disable Metrics/MethodLength
71
- unless should_be_checked_for?(
72
- object:,
73
- schema_key:,
74
- schema_value:,
75
- required: attribute_required
76
- ) # do
77
- return true
78
- end
79
-
80
- value = object.fetch(schema_key, nil)
81
- prepared_value = prepare_value_from(schema_value:, value:, required: attribute_required)
82
-
83
- Array(attribute_type).uniq.any? { |type| prepared_value.is_a?(type) }
84
- end
85
-
86
- def should_be_checked_for?(object:, schema_key:, schema_value:, required:)
87
- required || (
88
- !required && !fetch_default_from(schema_value).nil?
89
- ) || (
90
- !required && !object.fetch(schema_key, nil).nil?
91
- )
92
- end
93
-
94
- def prepare_value_from(schema_value:, value:, required:)
95
- if !required && !fetch_default_from(schema_value).nil? && value.blank?
96
- fetch_default_from(schema_value)
97
- else
98
- value
99
- end
100
- end
101
-
102
- def fetch_default_from(value)
103
- value.fetch(:default, nil)
104
- end
105
-
106
- def add_error(key_name:, expected_type:, given_type:)
107
- @errors << {
108
- key_name:,
109
- expected_type:,
110
- given_type:
111
- }
112
- end
113
- end
114
- end
115
- end
116
- end