servactory 2.12.0.rc1 → 2.12.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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