servactory 2.12.0.rc2 → 2.12.0.rc3

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 +193 -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,193 @@
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:)
33
+ return true if option.value == false
34
+ return [false, :wrong_type] if @default_hash_mode_class_names.intersection(attribute.types).empty?
35
+
36
+ schema = option.value.fetch(:is, option.value)
37
+
38
+ is_success, reason, meta = validate_for!(object: value, schema:)
39
+
40
+ prepare_object_with!(object: value, schema:) if is_success
41
+
42
+ [is_success, reason, meta]
43
+ end
44
+
45
+ def validate_for!(object:, schema:, root_schema_key: nil) # rubocop:disable Metrics/MethodLength
46
+ unless object.respond_to?(:fetch)
47
+ return [
48
+ false,
49
+ :wrong_element_value,
50
+ {
51
+ key_name: root_schema_key,
52
+ expected_type: Hash.name,
53
+ given_type: object.class.name
54
+ }
55
+ ]
56
+ end
57
+
58
+ errors = schema.map do |schema_key, schema_value|
59
+ attribute_type = schema_value.fetch(:type, String)
60
+
61
+ if attribute_type == Hash
62
+ validate_for!(
63
+ object: object.fetch(schema_key, {}),
64
+ schema: schema_value.except(*RESERVED_ATTRIBUTES),
65
+ root_schema_key: schema_key
66
+ )
67
+ else
68
+ is_success, given_type = validate_with(
69
+ object:,
70
+ schema_key:,
71
+ schema_value:,
72
+ attribute_type:,
73
+ attribute_required: schema_value.fetch(:required, true)
74
+ )
75
+
76
+ next if is_success
77
+
78
+ [false, :wrong_element_type, { key_name: schema_key, expected_type: attribute_type, given_type: }]
79
+ end
80
+ end
81
+
82
+ errors.compact.first || true
83
+ end
84
+
85
+ def validate_with(object:, schema_key:, schema_value:, attribute_type:, attribute_required:) # rubocop:disable Metrics/MethodLength
86
+ unless should_be_checked_for?(
87
+ object:,
88
+ schema_key:,
89
+ schema_value:,
90
+ required: attribute_required
91
+ ) # do
92
+ return true
93
+ end
94
+
95
+ value = object.fetch(schema_key, nil)
96
+ prepared_value = prepare_value_from(schema_value:, value:, required: attribute_required)
97
+
98
+ [
99
+ Array(attribute_type).uniq.any? { |type| prepared_value.is_a?(type) },
100
+ prepared_value.class.name
101
+ ]
102
+ end
103
+
104
+ def should_be_checked_for?(object:, schema_key:, schema_value:, required:)
105
+ required || (
106
+ !required && !fetch_default_from(schema_value).nil?
107
+ ) || (
108
+ !required && !object.fetch(schema_key, nil).nil?
109
+ )
110
+ end
111
+
112
+ def prepare_value_from(schema_value:, value:, required:)
113
+ if !required && !fetch_default_from(schema_value).nil? && value.blank?
114
+ fetch_default_from(schema_value)
115
+ else
116
+ value
117
+ end
118
+ end
119
+
120
+ def fetch_default_from(value)
121
+ value.fetch(:default, nil)
122
+ end
123
+
124
+ ########################################################################
125
+
126
+ def prepare_object_with!(object:, schema:) # rubocop:disable Metrics/MethodLength
127
+ schema.map do |schema_key, schema_value|
128
+ attribute_type = schema_value.fetch(:type, String)
129
+
130
+ if attribute_type == Hash
131
+ prepare_object_with!(
132
+ object: object.fetch(schema_key, {}),
133
+ schema: schema_value.except(*RESERVED_ATTRIBUTES)
134
+ )
135
+ else
136
+ next object unless object[schema_key].nil?
137
+
138
+ next object if (default = schema_value.fetch(:default, nil)).nil?
139
+
140
+ object[schema_key] = default
141
+ end
142
+ end
143
+ end
144
+
145
+ ########################################################################
146
+ ########################################################################
147
+ ########################################################################
148
+
149
+ def message_for_input_with(service:, input:, reason:, meta:, **)
150
+ i18n_key = "inputs.validations.must.dynamic_options.schema"
151
+ i18n_key += reason.present? ? ".#{reason}" : ".default"
152
+
153
+ service.translate(
154
+ i18n_key,
155
+ service_class_name: service.class_name,
156
+ input_name: input.name,
157
+ key_name: meta.fetch(:key_name),
158
+ expected_type: meta.fetch(:expected_type),
159
+ given_type: meta.fetch(:given_type)
160
+ )
161
+ end
162
+
163
+ def message_for_internal_with(service:, internal:, reason:, meta:, **)
164
+ i18n_key = "internals.validations.must.dynamic_options.schema"
165
+ i18n_key += reason.present? ? ".#{reason}" : ".default"
166
+
167
+ service.translate(
168
+ i18n_key,
169
+ service_class_name: service.class_name,
170
+ internal_name: internal.name,
171
+ key_name: meta.fetch(:key_name),
172
+ expected_type: meta.fetch(:expected_type),
173
+ given_type: meta.fetch(:given_type)
174
+ )
175
+ end
176
+
177
+ def message_for_output_with(service:, output:, reason:, meta:, **)
178
+ i18n_key = "outputs.validations.must.dynamic_options.schema"
179
+ i18n_key += reason.present? ? ".#{reason}" : ".default"
180
+
181
+ service.translate(
182
+ i18n_key,
183
+ service_class_name: service.class_name,
184
+ output_name: output.name,
185
+ key_name: meta.fetch(:key_name),
186
+ expected_type: meta.fetch(:expected_type),
187
+ given_type: meta.fetch(:given_type)
188
+ )
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -5,7 +5,7 @@ module Servactory
5
5
  MAJOR = 2
6
6
  MINOR = 12
7
7
  PATCH = 0
8
- PRE = "rc2"
8
+ PRE = "rc3"
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.rc3
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