servactory 2.12.0.rc1 → 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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/en.yml +18 -12
  3. data/config/locales/ru.yml +18 -12
  4. data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +5 -1
  5. data/lib/servactory/configuration/setup.rb +9 -3
  6. data/lib/servactory/context/workspace/inputs.rb +0 -35
  7. data/lib/servactory/info/builder.rb +103 -0
  8. data/lib/servactory/info/dsl.rb +8 -51
  9. data/lib/servactory/info/result.rb +4 -4
  10. data/lib/servactory/inputs/dsl.rb +0 -1
  11. data/lib/servactory/inputs/input.rb +8 -9
  12. data/lib/servactory/internals/dsl.rb +0 -1
  13. data/lib/servactory/internals/internal.rb +5 -8
  14. data/lib/servactory/maintenance/attributes/option.rb +7 -13
  15. data/lib/servactory/maintenance/attributes/option_helper.rb +8 -2
  16. data/lib/servactory/maintenance/attributes/options/registrar.rb +2 -57
  17. data/lib/servactory/maintenance/attributes/translator/must.rb +1 -1
  18. data/lib/servactory/maintenance/attributes/translator/type.rb +3 -38
  19. data/lib/servactory/maintenance/attributes/validations/must.rb +8 -6
  20. data/lib/servactory/maintenance/validations/types.rb +3 -26
  21. data/lib/servactory/outputs/dsl.rb +0 -1
  22. data/lib/servactory/outputs/output.rb +5 -8
  23. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +8 -13
  24. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +7 -4
  25. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/message_matcher.rb +91 -0
  26. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +6 -0
  27. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/schema_matcher.rb +83 -0
  28. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +29 -7
  29. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +12 -5
  30. data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +31 -7
  31. data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +63 -0
  32. data/lib/servactory/tool_kit/dynamic_options/must.rb +34 -6
  33. data/lib/servactory/tool_kit/dynamic_options/schema.rb +193 -0
  34. data/lib/servactory/version.rb +1 -1
  35. metadata +7 -5
  36. data/lib/servactory/maintenance/attributes/translator/inclusion.rb +0 -26
  37. data/lib/servactory/maintenance/attributes/validations/inclusion.rb +0 -63
  38. data/lib/servactory/maintenance/validations/object_schema.rb +0 -116
@@ -75,7 +75,6 @@ module Servactory
75
75
  )
76
76
  end
77
77
 
78
- # rubocop:disable Metrics/MethodLength
79
78
  def prepare_advanced_for(
80
79
  body:,
81
80
  body_key:,
@@ -83,22 +82,17 @@ module Servactory
83
82
  body_fallback:
84
83
  )
85
84
  if body.is_a?(Hash)
86
- if @name == :schema && body.fetch(body_key, nil).nil?
87
- DEFAULT_BODY.call(key: body_key, body:)
88
- else
89
- message = body.fetch(:message, nil)
90
-
91
- DEFAULT_BODY.call(
92
- key: body_key,
93
- body: body.fetch(body_key, message.present? ? body_value : body_fallback),
94
- message:
95
- )
96
- end
85
+ message = body.fetch(:message, nil)
86
+
87
+ DEFAULT_BODY.call(
88
+ key: body_key,
89
+ body: body.fetch(body_key, message.present? ? body_value : body_fallback),
90
+ message:
91
+ )
97
92
  else
98
93
  DEFAULT_BODY.call(key: body_key, body:)
99
94
  end
100
95
  end
101
- # rubocop:enable Metrics/MethodLength
102
96
 
103
97
  def prepare_methods_for(attribute)
104
98
  attribute.instance_eval(define_methods_template) if define_methods_template.present?
@@ -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
@@ -10,8 +10,6 @@ module Servactory
10
10
  required
11
11
  default
12
12
  collection
13
- hash
14
- inclusion
15
13
  must
16
14
  prepare
17
15
  ].freeze
@@ -20,8 +18,6 @@ module Servactory
20
18
  required: false,
21
19
  types: false,
22
20
  default: false,
23
- hash: false,
24
- inclusion: false,
25
21
  must: false,
26
22
  prepare: false
27
23
  }.freeze
@@ -32,26 +28,21 @@ module Servactory
32
28
  new(...).register
33
29
  end
34
30
 
35
- def initialize(attribute:, hash_mode_class_names:, options:, features:)
31
+ def initialize(attribute:, options:, features:)
36
32
  @attribute = attribute
37
- @hash_mode_class_names = hash_mode_class_names
38
33
  @options = options
39
34
  @features = DEFAULT_FEATURES.merge(features)
40
35
  end
41
36
 
42
37
  ########################################################################
43
38
 
44
- def register # rubocop:disable Metrics/CyclomaticComplexity
39
+ def register
45
40
  # Validation Class: Servactory::Inputs::Validations::Required
46
41
  register_required_option if @features.fetch(:required)
47
42
 
48
43
  # Validation Class: Servactory::Maintenance::Attributes::Validations::Type
49
44
  register_types_option if @features.fetch(:types)
50
45
  register_default_option if @features.fetch(:default)
51
- register_hash_option if @features.fetch(:hash)
52
-
53
- # Validation Class: Servactory::Maintenance::Attributes::Validations::Inclusion
54
- register_inclusion_option if @features.fetch(:inclusion)
55
46
 
56
47
  # Validation Class: Servactory::Maintenance::Attributes::Validations::Must
57
48
  register_must_option if @features.fetch(:must)
@@ -121,47 +112,6 @@ module Servactory
121
112
  )
122
113
  end
123
114
 
124
- def register_hash_option # rubocop:disable Metrics/MethodLength
125
- collection << Servactory::Maintenance::Attributes::Option.new(
126
- name: :schema,
127
- attribute: @attribute,
128
- validation_class: Servactory::Maintenance::Attributes::Validations::Type,
129
- define_methods: [
130
- Servactory::Maintenance::Attributes::DefineMethod.new(
131
- name: :hash_mode?,
132
- content: ->(**) { @hash_mode_class_names.include?(@options.fetch(:type)) }
133
- )
134
- ],
135
- define_conflicts: [
136
- Servactory::Maintenance::Attributes::DefineConflict.new(
137
- content: -> { :object_vs_inclusion if @attribute.hash_mode? && @attribute.inclusion_present? }
138
- )
139
- ],
140
- need_for_checks: false,
141
- body_key: :is,
142
- body_fallback: {},
143
- **@options
144
- )
145
- end
146
-
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
115
  def register_must_option # rubocop:disable Metrics/MethodLength
166
116
  collection << Servactory::Maintenance::Attributes::Option.new(
167
117
  name: :must,
@@ -192,11 +142,6 @@ module Servactory
192
142
  content: ->(option:) { option[:in].present? }
193
143
  )
194
144
  ],
195
- define_conflicts: [
196
- Servactory::Maintenance::Attributes::DefineConflict.new(
197
- content: -> { :prepare_vs_inclusion if @attribute.prepare_present? && @attribute.inclusion_present? }
198
- )
199
- ],
200
145
  need_for_checks: false,
201
146
  body_key: :in,
202
147
  body_fallback: false,
@@ -8,7 +8,7 @@ module Servactory
8
8
  module_function
9
9
 
10
10
  def default_message
11
- lambda do |service:, value:, code:, input: nil, internal: nil, output: nil, reason: nil|
11
+ lambda do |service:, value:, code:, input: nil, internal: nil, output: nil, reason: nil, **|
12
12
  attribute = Servactory::Utils.define_attribute_with(input:, internal:, output:)
13
13
 
14
14
  service.translate(
@@ -5,53 +5,18 @@ module Servactory
5
5
  module Attributes
6
6
  module Translator
7
7
  module Type
8
- extend self
8
+ module_function
9
9
 
10
10
  def default_message
11
- lambda do |service:, attribute:, key_name:, expected_type:, given_type:, **|
12
- if attribute.hash_mode? && key_name.present?
13
- for_hash_mode_with(service:, attribute:, key_name:,
14
- expected_type:, given_type:)
15
- else
16
- for_others_with(service:, attribute:,
17
- expected_type:, given_type:)
18
- end
19
- end
20
- end
21
-
22
- private
23
-
24
- def for_hash_mode_with(service:, attribute:, key_name:, expected_type:, given_type:) # rubocop:disable Metrics/MethodLength
25
- hash_message = attribute.schema.fetch(:message)
26
-
27
- if hash_message.is_a?(Proc)
28
- hash_message.call(
29
- **Servactory::Utils.fetch_hash_with_desired_attribute(attribute),
30
- key_name:,
31
- expected_type:,
32
- given_type:
33
- )
34
- elsif hash_message.is_a?(String) && hash_message.present?
35
- hash_message
36
- else
11
+ lambda do |service:, attribute:, expected_type:, given_type:, **|
37
12
  service.translate(
38
- "#{attribute.i18n_name}.validations.type.default_error.for_hash.wrong_element_type",
13
+ "#{attribute.i18n_name}.validations.type.default_error.default",
39
14
  "#{attribute.system_name}_name": attribute.name,
40
- key_name:,
41
15
  expected_type:,
42
16
  given_type:
43
17
  )
44
18
  end
45
19
  end
46
-
47
- def for_others_with(service:, attribute:, expected_type:, given_type:)
48
- service.translate(
49
- "#{attribute.i18n_name}.validations.type.default_error.default",
50
- "#{attribute.system_name}_name": attribute.name,
51
- expected_type:,
52
- given_type:
53
- )
54
- end
55
20
  end
56
21
  end
57
22
  end
@@ -28,11 +28,11 @@ module Servactory
28
28
 
29
29
  def check
30
30
  @check_options.each do |code, options|
31
- message, reason = call_or_fetch_message_from(code, options)
31
+ message, reason, meta = call_or_fetch_message_from(code, options)
32
32
 
33
33
  next if message.blank?
34
34
 
35
- add_error_with(message, code, reason)
35
+ add_error_with(message, code, reason, meta)
36
36
  end
37
37
 
38
38
  errors
@@ -43,14 +43,15 @@ module Servactory
43
43
  def call_or_fetch_message_from(code, options) # rubocop:disable Metrics/MethodLength
44
44
  check, message = options.values_at(:is, :message)
45
45
 
46
- check_result, check_result_code =
46
+ check_result, check_result_code, meta =
47
47
  check.call(value: @value, **Servactory::Utils.fetch_hash_with_desired_attribute(@attribute))
48
48
 
49
49
  return if check_result
50
50
 
51
51
  [
52
52
  message.presence || Servactory::Maintenance::Attributes::Translator::Must.default_message,
53
- check_result_code
53
+ check_result_code,
54
+ meta
54
55
  ]
55
56
  rescue StandardError => e
56
57
  add_syntax_error_with(
@@ -62,14 +63,15 @@ module Servactory
62
63
 
63
64
  ########################################################################
64
65
 
65
- def add_error_with(message, code, reason)
66
+ def add_error_with(message, code, reason, meta = {})
66
67
  add_error(
67
68
  message:,
68
69
  service: @context.send(:servactory_service_info),
69
70
  **Servactory::Utils.fetch_hash_with_desired_attribute(@attribute),
70
71
  value: @value,
71
72
  code:,
72
- reason:
73
+ reason:,
74
+ meta:
73
75
  )
74
76
  end
75
77
 
@@ -16,32 +16,9 @@ module Servactory
16
16
  @error_callback = error_callback
17
17
  end
18
18
 
19
- def validate! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
20
- object_schema_validator = nil
21
-
22
- if @attribute.hash_mode?
23
- object_schema_validator = Servactory::Maintenance::Validations::ObjectSchema.validate(
24
- object: @value,
25
- schema: @attribute.schema
26
- )
27
-
28
- return if object_schema_validator.valid?
29
- else
30
- return if prepared_types.any? do |type| # rubocop:disable Style/IfInsideElse
31
- @value.is_a?(type)
32
- end
33
- end
34
-
35
- if (first_error = object_schema_validator&.errors&.first).present?
36
- return @error_callback.call(
37
- message: Servactory::Maintenance::Attributes::Translator::Type.default_message,
38
- service: @context.send(:servactory_service_info),
39
- attribute: @attribute,
40
- value: @value,
41
- key_name: first_error.fetch(:key_name),
42
- expected_type: first_error.fetch(:expected_type),
43
- given_type: first_error.fetch(:given_type)
44
- )
19
+ def validate! # rubocop:disable Metrics/MethodLength
20
+ return if prepared_types.any? do |type|
21
+ @value.is_a?(type)
45
22
  end
46
23
 
47
24
  @error_callback.call(
@@ -20,7 +20,6 @@ module Servactory
20
20
  collection_of_outputs << Output.new(
21
21
  name,
22
22
  *helpers,
23
- hash_mode_class_names: config.hash_mode_class_names,
24
23
  option_helpers: config.output_option_helpers,
25
24
  **options
26
25
  )
@@ -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,17 +23,16 @@ 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,
30
31
  *helpers,
31
- hash_mode_class_names:,
32
32
  option_helpers:,
33
33
  **options
34
34
  )
35
35
  @name = name
36
- @hash_mode_class_names = hash_mode_class_names
37
36
  @option_helpers = option_helpers
38
37
 
39
38
  register_options(helpers:, options:)
@@ -59,16 +58,14 @@ module Servactory
59
58
 
60
59
  options_registrar = Servactory::Maintenance::Attributes::Options::Registrar.register(
61
60
  attribute: self,
62
- hash_mode_class_names: @hash_mode_class_names,
63
61
  options:,
64
62
  features: {
65
63
  types: true,
66
- hash: true,
67
- inclusion: true,
68
64
  must: true
69
65
  }
70
66
  )
71
67
 
68
+ @options = options
72
69
  @collection_of_options = options_registrar.collection
73
70
  end
74
71
 
@@ -6,17 +6,18 @@ module Servactory
6
6
  module Matchers
7
7
  module HaveServiceAttributeMatchers
8
8
  class ConsistsOfMatcher
9
+ OPTION_NAME = :consists_of
10
+ OPTION_BODY_KEY = :type
11
+
9
12
  attr_reader :missing_option
10
13
 
11
- def initialize(described_class, attribute_type, attribute_name, option_types, consists_of_types,
12
- custom_message)
14
+ def initialize(described_class, attribute_type, attribute_name, option_types, consists_of_types)
13
15
  @described_class = described_class
14
16
  @attribute_type = attribute_type
15
17
  @attribute_type_plural = attribute_type.to_s.pluralize.to_sym
16
18
  @attribute_name = attribute_name
17
19
  @option_types = option_types
18
20
  @consists_of_types = consists_of_types
19
- @custom_message = custom_message
20
21
 
21
22
  @attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
22
23
 
@@ -46,20 +47,14 @@ module Servactory
46
47
  :attribute_name,
47
48
  :option_types,
48
49
  :consists_of_types,
49
- :custom_message,
50
50
  :attribute_data
51
51
 
52
52
  def submatcher_passes?(_subject)
53
- attribute_must = attribute_data.fetch(:must)
54
-
55
- attribute_must_keys = attribute_must.keys
56
-
57
- expected_keys = %i[consists_of]
58
-
59
- attribute_must_keys = attribute_must_keys.select { |key| expected_keys.include?(key) }
53
+ attribute_consists_of = attribute_data.fetch(OPTION_NAME)
54
+ attribute_consists_of_types = Array(attribute_consists_of.fetch(OPTION_BODY_KEY))
60
55
 
61
- attribute_must_keys.difference(expected_keys).empty? &&
62
- expected_keys.difference(attribute_must_keys).empty?
56
+ attribute_consists_of_types.difference(consists_of_types).empty? &&
57
+ consists_of_types.difference(attribute_consists_of_types).empty?
63
58
  end
64
59
 
65
60
  def build_missing_option
@@ -6,6 +6,9 @@ module Servactory
6
6
  module Matchers
7
7
  module HaveServiceAttributeMatchers
8
8
  class InclusionMatcher
9
+ OPTION_NAME = :inclusion
10
+ OPTION_BODY_KEY = :in
11
+
9
12
  attr_reader :missing_option
10
13
 
11
14
  def initialize(described_class, attribute_type, attribute_name, values)
@@ -44,16 +47,16 @@ module Servactory
44
47
  :attribute_data
45
48
 
46
49
  def submatcher_passes?(_subject)
47
- attribute_inclusion = attribute_data.fetch(:inclusion)
48
- attribute_inclusion_in = attribute_inclusion.fetch(:in)
50
+ attribute_inclusion = attribute_data.fetch(OPTION_NAME)
51
+ attribute_inclusion_in = attribute_inclusion.fetch(OPTION_BODY_KEY)
49
52
 
50
53
  attribute_inclusion_in.difference(values).empty? &&
51
54
  values.difference(attribute_inclusion_in).empty?
52
55
  end
53
56
 
54
57
  def build_missing_option
55
- attribute_inclusion = attribute_data.fetch(:inclusion)
56
- attribute_inclusion_in = attribute_inclusion.fetch(:in)
58
+ attribute_inclusion = attribute_data.fetch(OPTION_NAME)
59
+ attribute_inclusion_in = attribute_inclusion.fetch(OPTION_BODY_KEY)
57
60
 
58
61
  <<~MESSAGE
59
62
  should include the expected values
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module TestKit
5
+ module Rspec
6
+ module Matchers
7
+ module HaveServiceAttributeMatchers
8
+ class MessageMatcher
9
+ attr_reader :missing_option
10
+
11
+ def initialize(described_class, attribute_type, attribute_name, submatcher, custom_message)
12
+ @described_class = described_class
13
+ @attribute_type = attribute_type
14
+ @attribute_type_plural = attribute_type.to_s.pluralize.to_sym
15
+ @attribute_name = attribute_name
16
+ @custom_message = custom_message
17
+
18
+ attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
19
+
20
+ attribute_schema = attribute_data.fetch(submatcher.class::OPTION_NAME)
21
+ @attribute_schema_is = attribute_schema.fetch(submatcher.class::OPTION_BODY_KEY)
22
+ @attribute_schema_message = attribute_schema.fetch(:message)
23
+
24
+ @missing_option = ""
25
+ end
26
+
27
+ def description
28
+ result = "message: "
29
+ result + attribute_schema_message
30
+ end
31
+
32
+ def matches?(subject)
33
+ if submatcher_passes?(subject)
34
+ true
35
+ else
36
+ @missing_option = build_missing_option
37
+
38
+ false
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :described_class,
45
+ :attribute_type,
46
+ :attribute_type_plural,
47
+ :attribute_name,
48
+ :option_types,
49
+ :custom_message,
50
+ :attribute_schema_is,
51
+ :attribute_schema_message
52
+
53
+ def submatcher_passes?(_subject)
54
+ schema_message_equal?
55
+ end
56
+
57
+ def schema_message_equal? # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
58
+ @schema_message_equal ||=
59
+ if custom_message.present? && !attribute_schema_message.nil?
60
+ if custom_message.is_a?(RSpec::Matchers::BuiltIn::BaseMatcher)
61
+ RSpec::Expectations::ValueExpectationTarget
62
+ .new(attribute_schema_message)
63
+ .to(custom_message)
64
+
65
+ true
66
+ elsif attribute_schema_message.is_a?(Proc)
67
+ attribute_schema_message.call.casecmp(custom_message).zero?
68
+ else
69
+ attribute_schema_message.casecmp(custom_message).zero?
70
+ end
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ def build_missing_option
77
+ unless schema_message_equal? # rubocop:disable Style/GuardClause
78
+ <<~MESSAGE
79
+ should return expected message in case of problem:
80
+
81
+ expected #{attribute_schema_message.inspect}
82
+ got #{custom_message.inspect}
83
+ MESSAGE
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -51,6 +51,12 @@ module Servactory
51
51
  # NOTE: Even though the dynamic option `consists_of` is a `must`, here we are testing explicit `must`.
52
52
  attribute_must_keys.delete(:consists_of)
53
53
 
54
+ # NOTE: Even though the dynamic option `schema` is a `must`, here we are testing explicit `must`.
55
+ attribute_must_keys.delete(:schema)
56
+
57
+ # NOTE: Even though the dynamic option `inclusion` is a `must`, here we are testing explicit `must`.
58
+ attribute_must_keys.delete(:inclusion)
59
+
54
60
  attribute_must_keys.difference(must_names).empty? &&
55
61
  must_names.difference(attribute_must_keys).empty?
56
62
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module TestKit
5
+ module Rspec
6
+ module Matchers
7
+ module HaveServiceAttributeMatchers
8
+ class SchemaMatcher
9
+ OPTION_NAME = :schema
10
+ OPTION_BODY_KEY = :is
11
+
12
+ attr_reader :missing_option
13
+
14
+ def initialize(described_class, attribute_type, attribute_name, option_types, schema_data) # rubocop:disable Metrics/MethodLength
15
+ @described_class = described_class
16
+ @attribute_type = attribute_type
17
+ @attribute_type_plural = attribute_type.to_s.pluralize.to_sym
18
+ @attribute_name = attribute_name
19
+ @option_types = option_types
20
+ @schema_data = schema_data
21
+
22
+ attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
23
+
24
+ attribute_schema = attribute_data.fetch(OPTION_NAME)
25
+ @attribute_schema_is = attribute_schema.fetch(OPTION_BODY_KEY)
26
+ @attribute_schema_message = attribute_schema.fetch(:message)
27
+
28
+ @missing_option = ""
29
+ end
30
+
31
+ def description
32
+ result = "schema: "
33
+ result + schema_data
34
+ end
35
+
36
+ def matches?(subject)
37
+ if submatcher_passes?(subject)
38
+ true
39
+ else
40
+ @missing_option = build_missing_option
41
+
42
+ false
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :described_class,
49
+ :attribute_type,
50
+ :attribute_type_plural,
51
+ :attribute_name,
52
+ :option_types,
53
+ :schema_data,
54
+ :attribute_schema_is,
55
+ :attribute_schema_message
56
+
57
+ def submatcher_passes?(_subject)
58
+ schema_data_equal?
59
+ end
60
+
61
+ def schema_data_equal?
62
+ @schema_data_equal ||=
63
+ (
64
+ schema_data.present? && schema_data == attribute_schema_is
65
+ ) || schema_data.blank?
66
+ end
67
+
68
+ def build_missing_option
69
+ return if schema_data_equal?
70
+
71
+ <<~MESSAGE
72
+ should be schema with corresponding template
73
+
74
+ expected #{attribute_schema_is.inspect}
75
+ got #{schema_data.inspect}
76
+ MESSAGE
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end