servactory 2.12.0.rc1 → 2.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42d8f2391ba24198b46219e80408d7bf1d304faca0dfd5a99e89ac841d246fe4
4
- data.tar.gz: 93748b84e352a85641a18e2f50f7fbc1668c7d037a613b95f8ed47fe02fce079
3
+ metadata.gz: 8d25dddc86ece8552d4644707ec8ed928d743287e7b6d8cac5b6651c2f4376b4
4
+ data.tar.gz: 75954babfecf8c47e89961b6a3795b66264d34c09a9362e6ea30891d2453b156
5
5
  SHA512:
6
- metadata.gz: 45895d6e4182f0ccdb4fd0c37e030232d809d8fb14d6117f793c5abac5292ceb172d5cd1aeb1fc48cb3cbda70e0ce4ba08a8752744f4776f431136b2c7b1dc8e
7
- data.tar.gz: 97e972f65d377eec5eb1224d54c440e7cca90c66a32fd2fd398e87c482f355110fbc39453cfdca799b45e59af259bf5e393c039181e23f59ecb41bbfe5d478aa
6
+ metadata.gz: 3d5e14c6eed833a4e2181cbf68d87a69b869a7b98479402a26feb1b1ab2404cb5d74ec3e9cfa6a858a1b059980d3164bcf6c63c4faaddbf57925363a2cd3588b
7
+ data.tar.gz: cf7f8c2c6fd19d0f743e610a8de747f00fe0e885aa0c62ff392be27522a2d8d7dc88a8cbdd3916099ca4f21f8efcc5132d0ef3f995e37f8c5993bbca9dc207d6
@@ -12,8 +12,6 @@ en:
12
12
  for_fetch: "[%{service_class_name}] Undefined input attribute `%{input_name}`"
13
13
  for_assign: "[%{service_class_name}] Undefined input attribute `%{input_name}`"
14
14
  validations:
15
- inclusion:
16
- default_error: "[%{service_class_name}] Wrong value in `%{input_name}`, must be one of `%{input_inclusion}`"
17
15
  must:
18
16
  default_error: "[%{service_class_name}] Input `%{input_name}` must \"%{code}\""
19
17
  syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{input_name}` input: %{exception_message}"
@@ -26,6 +24,8 @@ en:
26
24
  default: "[%{service_class_name}] Input `%{input_name}` does not match `%{format_name}` format"
27
25
  wrong_pattern: "[%{service_class_name}] Input `%{input_name}` does not match `%{format_name}` format"
28
26
  unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for input `%{input_name}`"
27
+ inclusion:
28
+ default: "[%{service_class_name}] Wrong value in `%{input_name}`, must be one of `%{input_inclusion}`, got `%{value}`"
29
29
  min:
30
30
  default: "[%{service_class_name}] Input `%{input_name}` received value `%{value}`, which is less than `%{option_value}`"
31
31
  max:
@@ -52,8 +52,6 @@ en:
52
52
  for_fetch: "[%{service_class_name}] Undefined internal attribute `%{internal_name}`"
53
53
  for_assign: "[%{service_class_name}] Undefined internal attribute `%{internal_name}`"
54
54
  validations:
55
- inclusion:
56
- default_error: "[%{service_class_name}] Wrong value in `%{internal_name}`, must be one of `%{internal_inclusion}`"
57
55
  must:
58
56
  default_error: "[%{service_class_name}] Internal attribute `%{internal_name}` must \"%{code}\""
59
57
  syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{internal_name}` internal attribute: %{exception_message}"
@@ -66,6 +64,8 @@ en:
66
64
  default: "[%{service_class_name}] Internal attribute `%{internal_name}` does not match `%{format_name}` format"
67
65
  wrong_pattern: "[%{service_class_name}] Internal attribute `%{internal_name}` does not match `%{format_name}` format"
68
66
  unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for internal attribute `%{internal_name}`"
67
+ inclusion:
68
+ default: "[%{service_class_name}] Wrong value in `%{internal_name}`, must be one of `%{internal_inclusion}`, got `%{value}`"
69
69
  min:
70
70
  default: "[%{service_class_name}] Internal attribute `%{internal_name}` received value `%{value}`, which is less than `%{option_value}`"
71
71
  max:
@@ -84,8 +84,6 @@ en:
84
84
  for_fetch: "[%{service_class_name}] Undefined output attribute `%{output_name}`"
85
85
  for_assign: "[%{service_class_name}] Undefined output attribute `%{output_name}`"
86
86
  validations:
87
- inclusion:
88
- default_error: "[%{service_class_name}] Wrong value in `%{output_name}`, must be one of `%{output_inclusion}`"
89
87
  must:
90
88
  default_error: "[%{service_class_name}] Output attribute `%{output_name}` must \"%{code}\""
91
89
  syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{output_name}` output attribute: %{exception_message}"
@@ -98,6 +96,8 @@ en:
98
96
  default: "[%{service_class_name}] Output attribute `%{output_name}` does not match `%{format_name}` format"
99
97
  wrong_pattern: "[%{service_class_name}] Output attribute `%{output_name}` does not match `%{format_name}` format"
100
98
  unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for output attribute `%{output_name}`"
99
+ inclusion:
100
+ default: "[%{service_class_name}] Wrong value in `%{output_name}`, must be one of `%{output_inclusion}`, got `%{value}`"
101
101
  min:
102
102
  default: "[%{service_class_name}] Output attribute `%{output_name}` received value `%{value}`, which is less than `%{option_value}`"
103
103
  max:
@@ -12,8 +12,6 @@ ru:
12
12
  for_fetch: "[%{service_class_name}] Неизвестный входящий атрибут `%{input_name}`"
13
13
  for_assign: "[%{service_class_name}] Неизвестный входящий атрибут `%{input_name}`"
14
14
  validations:
15
- inclusion:
16
- default_error: "[%{service_class_name}] Неправильное значение в `%{input_name}`, должно быть одним из `%{input_inclusion}`"
17
15
  must:
18
16
  default_error: "[%{service_class_name}] Инпут `%{input_name}` должен \"%{code}\""
19
17
  syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` инпута `%{input_name}`: %{exception_message}"
@@ -26,6 +24,8 @@ ru:
26
24
  default: "[%{service_class_name}] Инпут `%{input_name}` не соответствует формату `%{format_name}`"
27
25
  wrong_pattern: "[%{service_class_name}] Инпут `%{input_name}` не соответствует формату `%{format_name}`"
28
26
  unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у инпута `%{input_name}`"
27
+ inclusion:
28
+ default: "[%{service_class_name}] Неправильное значение в `%{input_name}`, должно быть одним из `%{input_inclusion}`, получено `%{value}`"
29
29
  min:
30
30
  default: "[%{service_class_name}] Инпут `%{input_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
31
31
  max:
@@ -52,8 +52,6 @@ ru:
52
52
  for_fetch: "[%{service_class_name}] Неизвестный внутренний атрибут `%{internal_name}`"
53
53
  for_assign: "[%{service_class_name}] Неизвестный внутренний атрибут `%{internal_name}`"
54
54
  validations:
55
- inclusion:
56
- default_error: "[%{service_class_name}] Неправильное значение в `%{internal_name}`, должно быть одним из `%{internal_inclusion}`"
57
55
  must:
58
56
  default_error: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` должен \"%{code}\""
59
57
  syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` внутреннего атрибута `%{internal_name}`: %{exception_message}"
@@ -66,6 +64,8 @@ ru:
66
64
  default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` не соответствует формату `%{format_name}`"
67
65
  wrong_pattern: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` не соответствует формату `%{format_name}`"
68
66
  unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у внутреннего атрибута `%{internal_name}`"
67
+ inclusion:
68
+ default: "[%{service_class_name}] Неправильное значение в `%{internal_name}`, должно быть одним из `%{internal_inclusion}`, получено `%{value}`"
69
69
  min:
70
70
  default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
71
71
  max:
@@ -84,8 +84,6 @@ ru:
84
84
  for_fetch: "[%{service_class_name}] Неизвестный выходящий атрибут `%{output_name}`"
85
85
  for_assign: "[%{service_class_name}] Неизвестный выходящий атрибут `%{output_name}`"
86
86
  validations:
87
- inclusion:
88
- default_error: "[%{service_class_name}] Неправильное значение в `%{output_name}`, должно быть одним из `%{output_inclusion}`"
89
87
  must:
90
88
  default_error: "[%{service_class_name}] Выходящий атрибут `%{output_name}` должен \"%{code}\""
91
89
  syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` выходящего атрибута `%{output_name}`: %{exception_message}"
@@ -98,6 +96,8 @@ ru:
98
96
  default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` не соответствует формату `%{format_name}`"
99
97
  wrong_pattern: "[%{service_class_name}] Выходящий атрибут `%{output_name}` не соответствует формату `%{format_name}`"
100
98
  unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у выходящего атрибута `%{output_name}`"
99
+ inclusion:
100
+ default: "[%{service_class_name}] Неправильное значение в `%{output_name}`, должно быть одним из `%{output_inclusion}`, получено `%{value}`"
101
101
  min:
102
102
  default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
103
103
  max:
@@ -5,12 +5,16 @@ module Servactory
5
5
  module OptionHelpers
6
6
  class OptionHelpersCollection
7
7
  extend Forwardable
8
- def_delegators :@collection, :<<, :find, :merge
8
+ def_delegators :@collection, :<<, :filter, :map, :find, :merge
9
9
 
10
10
  def initialize(collection = Set.new)
11
11
  @collection = collection
12
12
  end
13
13
 
14
+ def dynamic_options
15
+ OptionHelpersCollection.new(filter(&:dynamic_option?))
16
+ end
17
+
14
18
  def find_by(name:)
15
19
  find { |helper| helper.name == name }
16
20
  end
@@ -71,19 +71,22 @@ module Servactory
71
71
  def default_input_option_helpers
72
72
  Set[
73
73
  Servactory::Maintenance::Attributes::OptionHelper.new(name: :optional, equivalent: { required: false }),
74
- Servactory::ToolKit::DynamicOptions::ConsistsOf.use(collection_mode_class_names:)
74
+ Servactory::ToolKit::DynamicOptions::ConsistsOf.use(collection_mode_class_names:),
75
+ Servactory::ToolKit::DynamicOptions::Inclusion.use
75
76
  ]
76
77
  end
77
78
 
78
79
  def default_internal_option_helpers
79
80
  Set[
80
- Servactory::ToolKit::DynamicOptions::ConsistsOf.use(collection_mode_class_names:)
81
+ Servactory::ToolKit::DynamicOptions::ConsistsOf.use(collection_mode_class_names:),
82
+ Servactory::ToolKit::DynamicOptions::Inclusion.use
81
83
  ]
82
84
  end
83
85
 
84
86
  def default_output_option_helpers
85
87
  Set[
86
- Servactory::ToolKit::DynamicOptions::ConsistsOf.use(collection_mode_class_names:)
88
+ Servactory::ToolKit::DynamicOptions::ConsistsOf.use(collection_mode_class_names:),
89
+ Servactory::ToolKit::DynamicOptions::Inclusion.use
87
90
  ]
88
91
  end
89
92
  end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Info
5
+ class Builder
6
+ attr_reader :inputs,
7
+ :internals,
8
+ :outputs
9
+
10
+ def self.build(...)
11
+ new.build(...)
12
+ end
13
+
14
+ def build(collection_of_inputs:, collection_of_internals:, collection_of_outputs:, config:)
15
+ dynamic_options = config.input_option_helpers.dynamic_options
16
+
17
+ build_inputs_with(collection_of_inputs:, dynamic_options:)
18
+ build_internals_with(collection_of_internals:, dynamic_options:)
19
+ build_outputs_with(collection_of_outputs:, dynamic_options:)
20
+
21
+ self
22
+ end
23
+
24
+ private
25
+
26
+ def build_inputs_with(collection_of_inputs:, dynamic_options:)
27
+ @inputs = collection_of_inputs.to_h do |input|
28
+ options = build_options_for(input, dynamic_options)
29
+ options = enrich_options_for(input, options)
30
+
31
+ options[:required] = input.required
32
+ options[:default] = input.default
33
+
34
+ [
35
+ input.name,
36
+ options
37
+ ]
38
+ end
39
+ end
40
+
41
+ def build_internals_with(collection_of_internals:, dynamic_options:)
42
+ @internals = collection_of_internals.to_h do |internal|
43
+ options = build_options_for(internal, dynamic_options)
44
+ options = enrich_options_for(internal, options)
45
+
46
+ [
47
+ internal.name,
48
+ options
49
+ ]
50
+ end
51
+ end
52
+
53
+ def build_outputs_with(collection_of_outputs:, dynamic_options:)
54
+ @outputs = collection_of_outputs.to_h do |output|
55
+ options = build_options_for(output, dynamic_options)
56
+ options = enrich_options_for(output, options)
57
+
58
+ [
59
+ output.name,
60
+ options
61
+ ]
62
+ end
63
+ end
64
+
65
+ ##########################################################################
66
+
67
+ def build_options_for(attribute, dynamic_options)
68
+ attribute.options.to_h do |key, value|
69
+ dynamic_option = dynamic_options.find_by(name: key)
70
+
71
+ next [nil, nil] if dynamic_option.nil?
72
+
73
+ body_key = dynamic_option.meta.fetch(:body_key)
74
+
75
+ option = build_option_from(body_key:, value:)
76
+
77
+ [
78
+ dynamic_option.name,
79
+ option
80
+ ]
81
+ end.compact
82
+ end
83
+
84
+ def build_option_from(body_key:, value:)
85
+ {
86
+ body_key => value.is_a?(Hash) ? value.fetch(body_key, value) : value,
87
+ message: value.is_a?(Hash) ? value.fetch(:message, nil) : nil
88
+ }
89
+ end
90
+
91
+ def enrich_options_for(attribute, options)
92
+ actor = attribute.class::Actor.new(attribute)
93
+ must = attribute.collection_of_options.find_by(name: :must)
94
+
95
+ options.merge(
96
+ actor:,
97
+ types: attribute.types,
98
+ must: must.body
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
@@ -8,58 +8,15 @@ module Servactory
8
8
  end
9
9
 
10
10
  module ClassMethods
11
- def info # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
12
- Servactory::Info::Result.new(
13
- inputs: collection_of_inputs.to_h do |input|
14
- actor = input.class::Actor.new(input)
15
- inclusion = input.collection_of_options.find_by(name: :inclusion)
16
- must = input.collection_of_options.find_by(name: :must)
17
-
18
- [
19
- input.name,
20
- {
21
- actor:,
22
- types: input.types,
23
- required: input.required,
24
- default: input.default,
25
- inclusion: inclusion.body,
26
- must: must.body
27
- }
28
- ]
29
- end,
30
-
31
- internals: collection_of_internals.to_h do |internal|
32
- actor = internal.class::Actor.new(internal)
33
- inclusion = internal.collection_of_options.find_by(name: :inclusion)
34
- must = internal.collection_of_options.find_by(name: :must)
35
-
36
- [
37
- internal.name,
38
- {
39
- actor:,
40
- types: internal.types,
41
- inclusion: inclusion.body,
42
- must: must.body
43
- }
44
- ]
45
- end,
46
-
47
- outputs: collection_of_outputs.to_h do |output|
48
- actor = output.class::Actor.new(output)
49
- inclusion = output.collection_of_options.find_by(name: :inclusion)
50
- must = output.collection_of_options.find_by(name: :must)
51
-
52
- [
53
- output.name,
54
- {
55
- actor:,
56
- types: output.types,
57
- inclusion: inclusion.body,
58
- must: must.body
59
- }
60
- ]
61
- end
11
+ def info
12
+ builder = Builder.build(
13
+ collection_of_inputs:,
14
+ collection_of_internals:,
15
+ collection_of_outputs:,
16
+ config:
62
17
  )
18
+
19
+ Result.new(builder)
63
20
  end
64
21
  end
65
22
  end
@@ -7,10 +7,10 @@ module Servactory
7
7
  :internals,
8
8
  :outputs
9
9
 
10
- def initialize(inputs:, internals:, outputs:)
11
- @inputs = inputs
12
- @internals = internals
13
- @outputs = outputs
10
+ def initialize(builder)
11
+ @inputs = builder.inputs
12
+ @internals = builder.internals
13
+ @outputs = builder.outputs
14
14
  end
15
15
  end
16
16
  end
@@ -7,13 +7,15 @@ module Servactory
7
7
  attr_reader :name,
8
8
  :internal_name,
9
9
  :types,
10
- :inclusion
10
+ :default,
11
+ :options
11
12
 
12
- def initialize(input) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
13
+ def initialize(input) # rubocop:disable Metrics/MethodLength
13
14
  @name = input.name
14
15
  @internal_name = input.internal_name
15
16
  @types = input.types
16
- @inclusion = input.inclusion.slice(:in) if input.inclusion_present?
17
+ @default = input.default
18
+ @options = input.options
17
19
 
18
20
  define_singleton_method(:system_name) { input.system_name }
19
21
  define_singleton_method(:i18n_name) { input.i18n_name }
@@ -28,7 +30,8 @@ module Servactory
28
30
 
29
31
  attr_reader :name,
30
32
  :internal_name,
31
- :collection_of_options
33
+ :collection_of_options,
34
+ :options
32
35
 
33
36
  # rubocop:disable Style/KeywordParametersOrder
34
37
  def initialize(
@@ -75,12 +78,12 @@ module Servactory
75
78
  types: true,
76
79
  default: true,
77
80
  hash: true,
78
- inclusion: true,
79
81
  must: true,
80
82
  prepare: true
81
83
  }
82
84
  )
83
85
 
86
+ @options = options
84
87
  @collection_of_options = options_registrar.collection
85
88
  end
86
89
 
@@ -6,12 +6,12 @@ module Servactory
6
6
  class Actor
7
7
  attr_reader :name,
8
8
  :types,
9
- :inclusion
9
+ :options
10
10
 
11
11
  def initialize(internal)
12
12
  @name = internal.name
13
13
  @types = internal.types
14
- @inclusion = internal.inclusion.slice(:in) if internal.inclusion_present?
14
+ @options = internal.options
15
15
 
16
16
  define_singleton_method(:system_name) { internal.system_name }
17
17
  define_singleton_method(:i18n_name) { internal.i18n_name }
@@ -23,7 +23,8 @@ module Servactory
23
23
  end
24
24
 
25
25
  attr_reader :name,
26
- :collection_of_options
26
+ :collection_of_options,
27
+ :options
27
28
 
28
29
  def initialize(
29
30
  name,
@@ -64,11 +65,11 @@ module Servactory
64
65
  features: {
65
66
  types: true,
66
67
  hash: true,
67
- inclusion: true,
68
68
  must: true
69
69
  }
70
70
  )
71
71
 
72
+ @options = options
72
73
  @collection_of_options = options_registrar.collection
73
74
  end
74
75
 
@@ -5,11 +5,17 @@ module Servactory
5
5
  module Attributes
6
6
  class OptionHelper
7
7
  attr_reader :name,
8
- :equivalent
8
+ :equivalent,
9
+ :meta
9
10
 
10
- def initialize(name:, equivalent:)
11
+ def initialize(name:, equivalent:, meta: {})
11
12
  @name = name
12
13
  @equivalent = equivalent
14
+ @meta = meta
15
+ end
16
+
17
+ def dynamic_option?
18
+ meta[:type] == :dynamic_option
13
19
  end
14
20
  end
15
21
  end
@@ -11,7 +11,6 @@ module Servactory
11
11
  default
12
12
  collection
13
13
  hash
14
- inclusion
15
14
  must
16
15
  prepare
17
16
  ].freeze
@@ -21,7 +20,6 @@ module Servactory
21
20
  types: false,
22
21
  default: false,
23
22
  hash: false,
24
- inclusion: false,
25
23
  must: false,
26
24
  prepare: false
27
25
  }.freeze
@@ -41,7 +39,7 @@ module Servactory
41
39
 
42
40
  ########################################################################
43
41
 
44
- def register # rubocop:disable Metrics/CyclomaticComplexity
42
+ def register
45
43
  # Validation Class: Servactory::Inputs::Validations::Required
46
44
  register_required_option if @features.fetch(:required)
47
45
 
@@ -50,9 +48,6 @@ module Servactory
50
48
  register_default_option if @features.fetch(:default)
51
49
  register_hash_option if @features.fetch(:hash)
52
50
 
53
- # Validation Class: Servactory::Maintenance::Attributes::Validations::Inclusion
54
- register_inclusion_option if @features.fetch(:inclusion)
55
-
56
51
  # Validation Class: Servactory::Maintenance::Attributes::Validations::Must
57
52
  register_must_option if @features.fetch(:must)
58
53
 
@@ -132,11 +127,6 @@ module Servactory
132
127
  content: ->(**) { @hash_mode_class_names.include?(@options.fetch(:type)) }
133
128
  )
134
129
  ],
135
- define_conflicts: [
136
- Servactory::Maintenance::Attributes::DefineConflict.new(
137
- content: -> { :object_vs_inclusion if @attribute.hash_mode? && @attribute.inclusion_present? }
138
- )
139
- ],
140
130
  need_for_checks: false,
141
131
  body_key: :is,
142
132
  body_fallback: {},
@@ -144,24 +134,6 @@ module Servactory
144
134
  )
145
135
  end
146
136
 
147
- def register_inclusion_option # rubocop:disable Metrics/MethodLength
148
- collection << Servactory::Maintenance::Attributes::Option.new(
149
- name: :inclusion,
150
- attribute: @attribute,
151
- validation_class: Servactory::Maintenance::Attributes::Validations::Inclusion,
152
- define_methods: [
153
- Servactory::Maintenance::Attributes::DefineMethod.new(
154
- name: :inclusion_present?,
155
- content: ->(option:) { option[:in].is_a?(Array) && option[:in].present? }
156
- )
157
- ],
158
- need_for_checks: true,
159
- body_key: :in,
160
- body_fallback: nil,
161
- **@options
162
- )
163
- end
164
-
165
137
  def register_must_option # rubocop:disable Metrics/MethodLength
166
138
  collection << Servactory::Maintenance::Attributes::Option.new(
167
139
  name: :must,
@@ -192,11 +164,6 @@ module Servactory
192
164
  content: ->(option:) { option[:in].present? }
193
165
  )
194
166
  ],
195
- define_conflicts: [
196
- Servactory::Maintenance::Attributes::DefineConflict.new(
197
- content: -> { :prepare_vs_inclusion if @attribute.prepare_present? && @attribute.inclusion_present? }
198
- )
199
- ],
200
167
  need_for_checks: false,
201
168
  body_key: :in,
202
169
  body_fallback: false,
@@ -6,12 +6,12 @@ module Servactory
6
6
  class Actor
7
7
  attr_reader :name,
8
8
  :types,
9
- :inclusion
9
+ :options
10
10
 
11
11
  def initialize(output)
12
12
  @name = output.name
13
13
  @types = output.types
14
- @inclusion = output.inclusion.slice(:in) if output.inclusion_present?
14
+ @options = output.options
15
15
 
16
16
  define_singleton_method(:system_name) { output.system_name }
17
17
  define_singleton_method(:i18n_name) { output.i18n_name }
@@ -23,7 +23,8 @@ module Servactory
23
23
  end
24
24
 
25
25
  attr_reader :name,
26
- :collection_of_options
26
+ :collection_of_options,
27
+ :options
27
28
 
28
29
  def initialize(
29
30
  name,
@@ -64,11 +65,11 @@ module Servactory
64
65
  features: {
65
66
  types: true,
66
67
  hash: true,
67
- inclusion: true,
68
68
  must: true
69
69
  }
70
70
  )
71
71
 
72
+ @options = options
72
73
  @collection_of_options = options_registrar.collection
73
74
  end
74
75
 
@@ -49,17 +49,29 @@ module Servactory
49
49
  :custom_message,
50
50
  :attribute_data
51
51
 
52
- def submatcher_passes?(_subject)
53
- attribute_must = attribute_data.fetch(:must)
52
+ def submatcher_passes?(_subject) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
53
+ attribute_consists_of = attribute_data.fetch(:consists_of)
54
+ attribute_consists_of_types = Array(attribute_consists_of.fetch(:type))
55
+ attribute_consists_of_message = attribute_consists_of.fetch(:message)
54
56
 
55
- attribute_must_keys = attribute_must.keys
57
+ matched = attribute_consists_of_types.difference(consists_of_types).empty? &&
58
+ consists_of_types.difference(attribute_consists_of_types).empty?
56
59
 
57
- expected_keys = %i[consists_of]
58
-
59
- attribute_must_keys = attribute_must_keys.select { |key| expected_keys.include?(key) }
60
+ if custom_message.present? && !attribute_consists_of_message.nil?
61
+ if custom_message.is_a?(RSpec::Matchers::BuiltIn::BaseMatcher)
62
+ RSpec::Expectations::ValueExpectationTarget
63
+ .new(attribute_consists_of_message)
64
+ .to(custom_message)
65
+ else
66
+ matched &&= if attribute_consists_of_message.is_a?(Proc)
67
+ attribute_consists_of_message.call.casecmp(custom_message).zero?
68
+ else
69
+ attribute_consists_of_message.casecmp(custom_message).zero?
70
+ end
71
+ end
72
+ end
60
73
 
61
- attribute_must_keys.difference(expected_keys).empty? &&
62
- expected_keys.difference(attribute_must_keys).empty?
74
+ matched
63
75
  end
64
76
 
65
77
  def build_missing_option
@@ -8,12 +8,13 @@ module Servactory
8
8
  class InclusionMatcher
9
9
  attr_reader :missing_option
10
10
 
11
- def initialize(described_class, attribute_type, attribute_name, values)
11
+ def initialize(described_class, attribute_type, attribute_name, values, custom_message)
12
12
  @described_class = described_class
13
13
  @attribute_type = attribute_type
14
14
  @attribute_type_plural = attribute_type.to_s.pluralize.to_sym
15
15
  @attribute_name = attribute_name
16
16
  @values = values
17
+ @custom_message = custom_message
17
18
 
18
19
  @attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
19
20
 
@@ -41,14 +42,32 @@ module Servactory
41
42
  :attribute_type_plural,
42
43
  :attribute_name,
43
44
  :values,
45
+ :custom_message,
44
46
  :attribute_data
45
47
 
46
- def submatcher_passes?(_subject)
48
+ def submatcher_passes?(_subject) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
47
49
  attribute_inclusion = attribute_data.fetch(:inclusion)
48
50
  attribute_inclusion_in = attribute_inclusion.fetch(:in)
51
+ attribute_inclusion_message = attribute_inclusion.fetch(:message)
49
52
 
50
- attribute_inclusion_in.difference(values).empty? &&
51
- values.difference(attribute_inclusion_in).empty?
53
+ matched = attribute_inclusion_in.difference(values).empty? &&
54
+ values.difference(attribute_inclusion_in).empty?
55
+
56
+ if custom_message.present? && !attribute_inclusion_message.nil?
57
+ if custom_message.is_a?(RSpec::Matchers::BuiltIn::BaseMatcher)
58
+ RSpec::Expectations::ValueExpectationTarget
59
+ .new(attribute_inclusion_message)
60
+ .to(custom_message)
61
+ else
62
+ matched &&= if attribute_inclusion_message.is_a?(Proc)
63
+ attribute_inclusion_message.call.casecmp(custom_message).zero?
64
+ else
65
+ attribute_inclusion_message.casecmp(custom_message).zero?
66
+ end
67
+ end
68
+ end
69
+
70
+ matched
52
71
  end
53
72
 
54
73
  def build_missing_option
@@ -100,12 +100,15 @@ module Servactory
100
100
  end
101
101
 
102
102
  def inclusion(values)
103
+ message = block_given? ? yield : nil
104
+
103
105
  add_submatcher(
104
106
  HaveServiceAttributeMatchers::InclusionMatcher,
105
107
  described_class,
106
108
  :input,
107
109
  input_name,
108
- Array(values)
110
+ Array(values),
111
+ message
109
112
  )
110
113
  self
111
114
  end
@@ -5,6 +5,7 @@ module Servactory
5
5
  module Rspec
6
6
  module Matchers
7
7
  module HaveServiceInputMatchers
8
+ # DEPRECATED: This chain is planned to be decommissioned.
8
9
  class ValidWithMatcher # rubocop:disable Metrics/ClassLength
9
10
  attr_reader :missing_option
10
11
 
@@ -122,7 +123,7 @@ module Servactory
122
123
  end
123
124
 
124
125
  def failure_inclusion_passes? # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
125
- input_inclusion_in = attribute_data.fetch(:inclusion).fetch(:in)
126
+ input_inclusion_in = attribute_data.fetch(:inclusion, {}).fetch(:in, nil)
126
127
 
127
128
  return true if input_inclusion_in.blank?
128
129
 
@@ -135,11 +136,11 @@ module Servactory
135
136
 
136
137
  if input_required_message.nil?
137
138
  input_required_message = I18n.t(
138
- "#{i18n_root_key}.#{attribute_type_plural}.validations.inclusion.default_error",
139
+ "#{i18n_root_key}.#{attribute_type_plural}.validations.must.dynamic_options.inclusion.default",
139
140
  service_class_name: described_class.name,
140
141
  "#{attribute_type}_name": attribute_name,
141
- "#{attribute_type}_inclusion": input_inclusion_in,
142
- value: wrong_value
142
+ "#{attribute_type}_inclusion": input_inclusion_in.inspect,
143
+ value: wrong_value.inspect
143
144
  )
144
145
  elsif input_required_message.is_a?(Proc)
145
146
  service_class = Struct.new(:class_name, keyword_init: true)
@@ -66,12 +66,15 @@ module Servactory
66
66
  end
67
67
 
68
68
  def inclusion(values)
69
+ message = block_given? ? yield : nil
70
+
69
71
  add_submatcher(
70
72
  HaveServiceAttributeMatchers::InclusionMatcher,
71
73
  described_class,
72
74
  :internal,
73
75
  internal_name,
74
- Array(values)
76
+ Array(values),
77
+ message
75
78
  )
76
79
  self
77
80
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module ToolKit
5
+ module DynamicOptions
6
+ class Inclusion < Must
7
+ def self.use(option_name = :inclusion)
8
+ instance = new(option_name, :in)
9
+ instance.must(:be_inclusion)
10
+ end
11
+
12
+ def condition_for_input_with(input:, value:, option:)
13
+ if input.required? || (
14
+ input.optional? && !input.default.nil?
15
+ ) || (
16
+ input.optional? && !value.nil?
17
+ ) # do
18
+ return option.value.include?(value)
19
+ end
20
+
21
+ true
22
+ end
23
+
24
+ def condition_for_internal_with(value:, option:, **)
25
+ option.value.include?(value)
26
+ end
27
+
28
+ def condition_for_output_with(value:, option:, **)
29
+ option.value.include?(value)
30
+ end
31
+
32
+ ########################################################################
33
+
34
+ def message_for_input_with(service:, input:, value:, option_value:, **)
35
+ service.translate(
36
+ "inputs.validations.must.dynamic_options.inclusion.default",
37
+ input_name: input.name,
38
+ value: value.inspect,
39
+ input_inclusion: option_value.inspect
40
+ )
41
+ end
42
+
43
+ def message_for_internal_with(service:, internal:, value:, option_value:, **)
44
+ service.translate(
45
+ "internals.validations.must.dynamic_options.inclusion.default",
46
+ internal_name: internal.name,
47
+ value: value.inspect,
48
+ internal_inclusion: option_value.inspect
49
+ )
50
+ end
51
+
52
+ def message_for_output_with(service:, output:, value:, option_value:, **)
53
+ service.translate(
54
+ "outputs.validations.must.dynamic_options.inclusion.default",
55
+ output_name: output.name,
56
+ value: value.inspect,
57
+ output_inclusion: option_value.inspect
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -34,7 +34,11 @@ module Servactory
34
34
  def must(name)
35
35
  Servactory::Maintenance::Attributes::OptionHelper.new(
36
36
  name: @option_name,
37
- equivalent: equivalent_with(name)
37
+ equivalent: equivalent_with(name),
38
+ meta: {
39
+ type: :dynamic_option,
40
+ body_key: @body_key
41
+ }
38
42
  )
39
43
  end
40
44
 
@@ -5,7 +5,7 @@ module Servactory
5
5
  MAJOR = 2
6
6
  MINOR = 12
7
7
  PATCH = 0
8
- PRE = "rc1"
8
+ PRE = "rc2"
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.rc1
4
+ version: 2.12.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-08 00:00:00.000000000 Z
10
+ date: 2025-02-18 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -266,6 +266,7 @@ files:
266
266
  - lib/servactory/exceptions/internal.rb
267
267
  - lib/servactory/exceptions/output.rb
268
268
  - lib/servactory/exceptions/success.rb
269
+ - lib/servactory/info/builder.rb
269
270
  - lib/servactory/info/dsl.rb
270
271
  - lib/servactory/info/result.rb
271
272
  - lib/servactory/inputs/collection.rb
@@ -291,12 +292,10 @@ files:
291
292
  - lib/servactory/maintenance/attributes/options_collection.rb
292
293
  - lib/servactory/maintenance/attributes/tools/check_errors.rb
293
294
  - lib/servactory/maintenance/attributes/tools/validation.rb
294
- - lib/servactory/maintenance/attributes/translator/inclusion.rb
295
295
  - lib/servactory/maintenance/attributes/translator/must.rb
296
296
  - lib/servactory/maintenance/attributes/translator/type.rb
297
297
  - lib/servactory/maintenance/attributes/validations/base.rb
298
298
  - lib/servactory/maintenance/attributes/validations/errors.rb
299
- - lib/servactory/maintenance/attributes/validations/inclusion.rb
300
299
  - lib/servactory/maintenance/attributes/validations/must.rb
301
300
  - lib/servactory/maintenance/attributes/validations/type.rb
302
301
  - lib/servactory/maintenance/validations/object_schema.rb
@@ -322,6 +321,7 @@ files:
322
321
  - lib/servactory/test_kit/utils/faker.rb
323
322
  - lib/servactory/tool_kit/dynamic_options/consists_of.rb
324
323
  - lib/servactory/tool_kit/dynamic_options/format.rb
324
+ - lib/servactory/tool_kit/dynamic_options/inclusion.rb
325
325
  - lib/servactory/tool_kit/dynamic_options/max.rb
326
326
  - lib/servactory/tool_kit/dynamic_options/min.rb
327
327
  - lib/servactory/tool_kit/dynamic_options/multiple_of.rb
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- module Maintenance
5
- module Attributes
6
- module Translator
7
- module Inclusion
8
- module_function
9
-
10
- def default_message
11
- lambda do |service:, value:, input: nil, internal: nil, output: nil|
12
- attribute = Servactory::Utils.define_attribute_with(input:, internal:, output:)
13
-
14
- service.translate(
15
- "#{attribute.i18n_name}.validations.inclusion.default_error",
16
- "#{attribute.system_name}_name": attribute.name,
17
- "#{attribute.system_name}_inclusion": attribute.inclusion[:in],
18
- value:
19
- )
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- module Maintenance
5
- module Attributes
6
- module Validations
7
- class Inclusion < Base
8
- def self.check(context:, attribute:, value:, check_key:, **)
9
- return unless should_be_checked_for?(attribute, value, check_key)
10
-
11
- new(context:, attribute:, value:).check
12
- end
13
-
14
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
15
- def self.should_be_checked_for?(attribute, value, check_key)
16
- check_key == :inclusion && (
17
- (
18
- attribute.input? && (
19
- attribute.required? || (
20
- attribute.optional? && !attribute.default.nil?
21
- ) || (
22
- attribute.optional? && !value.nil?
23
- )
24
- )
25
- ) || attribute.internal? || attribute.output?
26
- )
27
- end
28
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
29
-
30
- ##########################################################################
31
-
32
- def initialize(context:, attribute:, value:)
33
- super()
34
-
35
- @context = context
36
- @attribute = attribute
37
- @value = value
38
- end
39
-
40
- def check
41
- inclusion_in, message = @attribute.inclusion.values_at(:in, :message)
42
-
43
- return if inclusion_in.nil?
44
- return if inclusion_in.include?(@value)
45
-
46
- add_error_with(message)
47
- end
48
-
49
- private
50
-
51
- def add_error_with(message)
52
- add_error(
53
- message: message.presence || Servactory::Maintenance::Attributes::Translator::Inclusion.default_message,
54
- service: @context.send(:servactory_service_info),
55
- **Servactory::Utils.fetch_hash_with_desired_attribute(@attribute),
56
- value: @value
57
- )
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end