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
@@ -6,15 +6,17 @@ 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
- def initialize(described_class, attribute_type, attribute_name, values, custom_message)
14
+ def initialize(described_class, attribute_type, attribute_name, values)
12
15
  @described_class = described_class
13
16
  @attribute_type = attribute_type
14
17
  @attribute_type_plural = attribute_type.to_s.pluralize.to_sym
15
18
  @attribute_name = attribute_name
16
19
  @values = values
17
- @custom_message = custom_message
18
20
 
19
21
  @attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
20
22
 
@@ -42,37 +44,19 @@ module Servactory
42
44
  :attribute_type_plural,
43
45
  :attribute_name,
44
46
  :values,
45
- :custom_message,
46
47
  :attribute_data
47
48
 
48
- def submatcher_passes?(_subject) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
49
- attribute_inclusion = attribute_data.fetch(:inclusion)
50
- attribute_inclusion_in = attribute_inclusion.fetch(:in)
51
- attribute_inclusion_message = attribute_inclusion.fetch(:message)
52
-
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
49
+ def submatcher_passes?(_subject)
50
+ attribute_inclusion = attribute_data.fetch(OPTION_NAME)
51
+ attribute_inclusion_in = attribute_inclusion.fetch(OPTION_BODY_KEY)
69
52
 
70
- matched
53
+ attribute_inclusion_in.difference(values).empty? &&
54
+ values.difference(attribute_inclusion_in).empty?
71
55
  end
72
56
 
73
57
  def build_missing_option
74
- attribute_inclusion = attribute_data.fetch(:inclusion)
75
- 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)
76
60
 
77
61
  <<~MESSAGE
78
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
@@ -84,31 +84,37 @@ module Servactory
84
84
  self
85
85
  end
86
86
 
87
- def consists_of(*types) # rubocop:disable Metrics/MethodLength
88
- message = block_given? ? yield : nil
89
-
87
+ def consists_of(*types)
90
88
  add_submatcher(
91
89
  HaveServiceAttributeMatchers::ConsistsOfMatcher,
92
90
  described_class,
93
91
  :input,
94
92
  input_name,
95
93
  @option_types,
96
- Array(types),
97
- message
94
+ Array(types)
98
95
  )
99
96
  self
100
97
  end
101
98
 
102
- def inclusion(values)
103
- message = block_given? ? yield : nil
99
+ def schema(data = {})
100
+ add_submatcher(
101
+ HaveServiceAttributeMatchers::SchemaMatcher,
102
+ described_class,
103
+ :input,
104
+ input_name,
105
+ @option_types,
106
+ data
107
+ )
108
+ self
109
+ end
104
110
 
111
+ def inclusion(values)
105
112
  add_submatcher(
106
113
  HaveServiceAttributeMatchers::InclusionMatcher,
107
114
  described_class,
108
115
  :input,
109
116
  input_name,
110
- Array(values),
111
- message
117
+ Array(values)
112
118
  )
113
119
  self
114
120
  end
@@ -135,6 +141,18 @@ module Servactory
135
141
  self
136
142
  end
137
143
 
144
+ def message(message)
145
+ add_submatcher(
146
+ HaveServiceAttributeMatchers::MessageMatcher,
147
+ described_class,
148
+ :input,
149
+ input_name,
150
+ @last_submatcher,
151
+ message
152
+ )
153
+ self
154
+ end
155
+
138
156
  # NOTE: Used for delayed chain implementation
139
157
  # def not_implemented_chain(*description)
140
158
  # Kernel.warn <<-MESSAGE.squish
@@ -165,11 +183,12 @@ module Servactory
165
183
 
166
184
  protected
167
185
 
168
- attr_reader :submatchers, :missing, :subject
186
+ attr_reader :last_submatcher, :submatchers, :missing, :subject
169
187
 
170
188
  def add_submatcher(matcher_class, *args)
171
189
  remove_submatcher(matcher_class)
172
- submatchers << matcher_class.new(*args)
190
+ @last_submatcher = matcher_class.new(*args)
191
+ submatchers << @last_submatcher
173
192
  end
174
193
 
175
194
  def remove_submatcher(matcher_class)
@@ -47,12 +47,13 @@ module Servactory
47
47
  :attribute_data,
48
48
  :i18n_root_key
49
49
 
50
- def submatcher_passes?(_subject) # rubocop:disable Metrics/CyclomaticComplexity
50
+ def submatcher_passes?(_subject) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
51
51
  success_passes? &&
52
52
  failure_type_passes? &&
53
53
  failure_required_passes? &&
54
54
  failure_optional_passes? &&
55
55
  failure_consists_of_passes? &&
56
+ failure_schema_passes? &&
56
57
  failure_format_passes? &&
57
58
  failure_inclusion_passes? &&
58
59
  failure_must_passes?
@@ -117,6 +118,11 @@ module Servactory
117
118
  true
118
119
  end
119
120
 
121
+ def failure_schema_passes?
122
+ # NOTE: Checking for negative cases is not implemented for `schema`
123
+ true
124
+ end
125
+
120
126
  def failure_format_passes?
121
127
  # NOTE: Checking for negative cases is not implemented for `format`
122
128
  true
@@ -2,8 +2,10 @@
2
2
 
3
3
  require_relative "have_service_attribute_matchers/types_matcher"
4
4
  require_relative "have_service_attribute_matchers/consists_of_matcher"
5
+ require_relative "have_service_attribute_matchers/schema_matcher"
5
6
  require_relative "have_service_attribute_matchers/inclusion_matcher"
6
7
  require_relative "have_service_attribute_matchers/must_matcher"
8
+ require_relative "have_service_attribute_matchers/message_matcher"
7
9
 
8
10
  module Servactory
9
11
  module TestKit
@@ -50,31 +52,37 @@ module Servactory
50
52
  self
51
53
  end
52
54
 
53
- def consists_of(*types) # rubocop:disable Metrics/MethodLength
54
- message = block_given? ? yield : nil
55
-
55
+ def consists_of(*types)
56
56
  add_submatcher(
57
57
  HaveServiceAttributeMatchers::ConsistsOfMatcher,
58
58
  described_class,
59
59
  :internal,
60
60
  internal_name,
61
61
  @option_types,
62
- Array(types),
63
- message
62
+ Array(types)
64
63
  )
65
64
  self
66
65
  end
67
66
 
68
- def inclusion(values)
69
- message = block_given? ? yield : nil
67
+ def schema(data = {})
68
+ add_submatcher(
69
+ HaveServiceAttributeMatchers::SchemaMatcher,
70
+ described_class,
71
+ :internal,
72
+ internal_name,
73
+ @option_types,
74
+ data
75
+ )
76
+ self
77
+ end
70
78
 
79
+ def inclusion(values)
71
80
  add_submatcher(
72
81
  HaveServiceAttributeMatchers::InclusionMatcher,
73
82
  described_class,
74
83
  :internal,
75
84
  internal_name,
76
- Array(values),
77
- message
85
+ Array(values)
78
86
  )
79
87
  self
80
88
  end
@@ -90,6 +98,18 @@ module Servactory
90
98
  self
91
99
  end
92
100
 
101
+ def message(message)
102
+ add_submatcher(
103
+ HaveServiceAttributeMatchers::MessageMatcher,
104
+ described_class,
105
+ :internal,
106
+ internal_name,
107
+ @last_submatcher,
108
+ message
109
+ )
110
+ self
111
+ end
112
+
93
113
  def description
94
114
  "#{internal_name} with #{submatchers.map(&:description).join(', ')}"
95
115
  end
@@ -110,11 +130,12 @@ module Servactory
110
130
 
111
131
  protected
112
132
 
113
- attr_reader :submatchers, :missing, :subject
133
+ attr_reader :last_submatcher, :submatchers, :missing, :subject
114
134
 
115
135
  def add_submatcher(matcher_class, *args)
116
136
  remove_submatcher(matcher_class)
117
- submatchers << matcher_class.new(*args)
137
+ @last_submatcher = matcher_class.new(*args)
138
+ submatchers << @last_submatcher
118
139
  end
119
140
 
120
141
  def remove_submatcher(matcher_class)
@@ -3,7 +3,7 @@
3
3
  module Servactory
4
4
  module ToolKit
5
5
  module DynamicOptions
6
- class Must
6
+ class Must # rubocop:disable Metrics/ClassLength
7
7
  class WorkOption
8
8
  attr_reader :name,
9
9
  :value,
@@ -80,24 +80,48 @@ module Servactory
80
80
  is_option_message_present = option.message.present?
81
81
  is_option_message_proc = option.message.is_a?(Proc) if is_option_message_present
82
82
 
83
- lambda do |input: nil, internal: nil, output: nil, **attributes|
83
+ lambda do |input: nil, internal: nil, output: nil, **attributes| # rubocop:disable Metrics/BlockLength
84
84
  default_attributes = { **attributes, option_name: option.name, option_value: option.value }
85
85
 
86
86
  if Servactory::Utils.really_input?(input)
87
87
  if is_option_message_present
88
- is_option_message_proc ? option.message.call(**default_attributes, input:) : option.message
88
+ if is_option_message_proc
89
+ option.message.call(
90
+ input:,
91
+ **default_attributes.delete(:meta) || {},
92
+ **default_attributes
93
+ )
94
+ else
95
+ option.message
96
+ end
89
97
  else
90
98
  message_for_input_with(**default_attributes, input:)
91
99
  end
92
100
  elsif Servactory::Utils.really_internal?(internal)
93
101
  if is_option_message_present
94
- is_option_message_proc ? option.message.call(**default_attributes, internal:) : option.message
102
+ if is_option_message_proc
103
+ option.message.call(
104
+ internal:,
105
+ **default_attributes.delete(:meta) || {},
106
+ **default_attributes
107
+ )
108
+ else
109
+ option.message
110
+ end
95
111
  else
96
112
  message_for_internal_with(**default_attributes, internal:)
97
113
  end
98
114
  elsif Servactory::Utils.really_output?(output)
99
115
  if is_option_message_present
100
- is_option_message_proc ? option.message.call(**default_attributes, output:) : option.message
116
+ if is_option_message_proc
117
+ option.message.call(
118
+ output:,
119
+ **default_attributes.delete(:meta) || {},
120
+ **default_attributes
121
+ )
122
+ else
123
+ option.message
124
+ end
101
125
  else
102
126
  message_for_output_with(**default_attributes, output:)
103
127
  end