servactory 3.1.0.rc2 → 3.1.0.rc4
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 +4 -4
- data/LICENSE +21 -0
- data/README.md +7 -2
- data/config/locales/ja.yml +134 -0
- data/lib/generators/README.md +2 -2
- data/lib/generators/servactory/extension/USAGE +1 -1
- data/lib/generators/servactory/install/USAGE +2 -2
- data/lib/generators/servactory/rspec/USAGE +4 -4
- data/lib/generators/servactory/rspec/rspec_generator.rb +2 -2
- data/lib/generators/servactory/service/USAGE +5 -5
- data/lib/servactory/actions/collection.rb +21 -5
- data/lib/servactory/actions/stages/collection.rb +19 -1
- data/lib/servactory/actions/tools/rules.rb +1 -1
- data/lib/servactory/actions/tools/runner.rb +1 -1
- data/lib/servactory/configuration/collection_mode/class_names_collection.rb +1 -1
- data/lib/servactory/configuration/config.rb +16 -0
- data/lib/servactory/configuration/configurable.rb +7 -4
- data/lib/servactory/configuration/factory.rb +30 -3
- data/lib/servactory/configuration/hash_mode/class_names_collection.rb +5 -1
- data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +156 -29
- data/lib/servactory/context/warehouse/inputs.rb +1 -1
- data/lib/servactory/context/workspace/inputs.rb +2 -2
- data/lib/servactory/context/workspace/internals.rb +2 -2
- data/lib/servactory/context/workspace/outputs.rb +2 -2
- data/lib/servactory/context/workspace.rb +7 -7
- data/lib/servactory/info/builder.rb +4 -3
- data/lib/servactory/inputs/dsl.rb +5 -1
- data/lib/servactory/inputs/input.rb +1 -2
- data/lib/servactory/inputs/tools/validation.rb +10 -18
- data/lib/servactory/internals/dsl.rb +7 -1
- data/lib/servactory/maintenance/attributes/base.rb +4 -1
- data/lib/servactory/maintenance/attributes/collection.rb +61 -6
- data/lib/servactory/maintenance/options/collection.rb +71 -30
- data/lib/servactory/maintenance/options/option.rb +3 -12
- data/lib/servactory/maintenance/options/registrar.rb +3 -13
- data/lib/servactory/maintenance/validations/checkers/type.rb +23 -5
- data/lib/servactory/maintenance/validations/performer.rb +12 -23
- data/lib/servactory/outputs/dsl.rb +7 -1
- data/lib/servactory/result.rb +21 -8
- data/lib/servactory/test_kit/rspec/helpers/exception_validator.rb +112 -0
- data/lib/servactory/test_kit/rspec/helpers/input_validator.rb +3 -1
- data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +125 -125
- data/lib/servactory/test_kit/rspec/helpers/service_inputs_guard.rb +98 -0
- data/lib/servactory/test_kit/rspec/helpers/service_inputs_matcher.rb +75 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +185 -50
- data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +45 -10
- data/lib/servactory/test_kit/rspec/helpers/wrap_block_adapter.rb +74 -0
- data/lib/servactory/test_kit/rspec/helpers.rb +4 -0
- data/lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb +72 -16
- data/lib/servactory/test_kit/rspec/matchers/base/message_expectation.rb +194 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher.rb +8 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/option_helper_rules.rb +148 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/proc_message.rb +117 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/required_message.rb +92 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +27 -4
- data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +10 -3
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/required_submatcher.rb +58 -23
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/valid_with_submatcher.rb +124 -59
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/message_submatcher.rb +133 -39
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/must_submatcher.rb +192 -17
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/target_submatcher.rb +4 -2
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/types_submatcher.rb +6 -0
- data/lib/servactory/test_kit/rspec/matchers.rb +15 -0
- data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +34 -11
- data/lib/servactory/tool_kit/dynamic_options/format.rb +3 -4
- data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +3 -3
- data/lib/servactory/tool_kit/dynamic_options/max.rb +3 -3
- data/lib/servactory/tool_kit/dynamic_options/min.rb +3 -3
- data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +35 -7
- data/lib/servactory/tool_kit/dynamic_options/must.rb +2 -3
- data/lib/servactory/tool_kit/dynamic_options/schema.rb +40 -25
- data/lib/servactory/tool_kit/dynamic_options/target.rb +4 -4
- data/lib/servactory/utils.rb +13 -3
- data/lib/servactory/version.rb +1 -1
- data/lib/servactory.rb +3 -4
- metadata +20 -4
|
@@ -9,7 +9,20 @@ module Servactory
|
|
|
9
9
|
#
|
|
10
10
|
# OptionHelpersCollection provides a unified interface for storing and
|
|
11
11
|
# querying Helper instances used by the configuration system.
|
|
12
|
-
# It
|
|
12
|
+
# It keeps helpers in an insertion-ordered Hash keyed by helper name,
|
|
13
|
+
# so every name resolves to exactly one helper.
|
|
14
|
+
#
|
|
15
|
+
# ## Built-in Helpers
|
|
16
|
+
#
|
|
17
|
+
# Helpers passed to the constructor are built-in: their names are
|
|
18
|
+
# reserved, and only `replace` may swap them (used to rebind built-in
|
|
19
|
+
# dynamic options to the configuration of a subclass).
|
|
20
|
+
#
|
|
21
|
+
# ## Inheritance
|
|
22
|
+
#
|
|
23
|
+
# A collection accepts one helper per name. A duplicate, made when
|
|
24
|
+
# a service class is inherited, may replace a helper registered before
|
|
25
|
+
# duplication in place without affecting the original.
|
|
13
26
|
#
|
|
14
27
|
# ## Usage
|
|
15
28
|
#
|
|
@@ -17,70 +30,184 @@ module Servactory
|
|
|
17
30
|
# to manage registered option helpers:
|
|
18
31
|
#
|
|
19
32
|
# ```ruby
|
|
20
|
-
# collection = OptionHelpersCollection.new
|
|
21
|
-
# collection
|
|
33
|
+
# collection = OptionHelpersCollection.new(builtin_helpers)
|
|
34
|
+
# collection.register(helper) # => :registered
|
|
35
|
+
# collection << other_helper # => collection, or raises ArgumentError
|
|
36
|
+
# collection.merge([third_helper]) # => collection, or raises ArgumentError
|
|
22
37
|
#
|
|
23
38
|
# collection.find_by(name: :must) # => helper instance
|
|
24
39
|
# collection.dynamic_options # => filtered OptionHelpersCollection
|
|
25
40
|
# ```
|
|
26
41
|
#
|
|
42
|
+
# ## Enumeration
|
|
43
|
+
#
|
|
44
|
+
# The collection includes Enumerable and yields helpers in registration
|
|
45
|
+
# order. Enumerable methods return plain values rather than collections:
|
|
46
|
+
# `filter` and `map` return Arrays, `find` returns a helper or nil, and
|
|
47
|
+
# `each_with_object` returns the memo object. Use `dynamic_options`
|
|
48
|
+
# to get a filtered OptionHelpersCollection.
|
|
49
|
+
#
|
|
27
50
|
# ## Performance
|
|
28
51
|
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
52
|
+
# Registration and lookup by name are O(1) Hash operations without
|
|
53
|
+
# cached indexes, so helpers registered after attributes are declared
|
|
54
|
+
# are visible to subsequent declarations.
|
|
31
55
|
#
|
|
32
56
|
class OptionHelpersCollection
|
|
33
|
-
|
|
57
|
+
include Enumerable
|
|
34
58
|
|
|
35
|
-
|
|
36
|
-
:<<,
|
|
37
|
-
:filter,
|
|
38
|
-
:each_with_object,
|
|
39
|
-
:map,
|
|
40
|
-
:merge
|
|
41
|
-
|
|
42
|
-
# Initializes the collection with an optional pre-built Set.
|
|
59
|
+
# Initializes the collection with built-in helpers.
|
|
43
60
|
#
|
|
44
|
-
# @param
|
|
61
|
+
# @param builtin_helpers [Enumerable<Maintenance::Options::Helper>] helpers whose names are reserved
|
|
45
62
|
# @return [OptionHelpersCollection]
|
|
46
|
-
def initialize(
|
|
47
|
-
@
|
|
63
|
+
def initialize(builtin_helpers = [])
|
|
64
|
+
@helpers = builtin_helpers.to_h { |helper| [helper.name, helper] }
|
|
65
|
+
@builtin_names = @helpers.keys.to_set.freeze
|
|
66
|
+
@own_names = Set.new
|
|
48
67
|
end
|
|
49
68
|
|
|
50
|
-
# Duplicates the collection
|
|
69
|
+
# Duplicates the collection so that registrations do not leak into the original.
|
|
70
|
+
#
|
|
71
|
+
# Helpers of the original may be replaced once in the duplicate.
|
|
51
72
|
#
|
|
52
73
|
# @param original [OptionHelpersCollection] the collection being duplicated
|
|
53
74
|
# @return [void]
|
|
54
75
|
def initialize_dup(original)
|
|
55
76
|
super
|
|
56
|
-
@
|
|
57
|
-
@
|
|
77
|
+
@helpers = original.instance_variable_get(:@helpers).dup
|
|
78
|
+
@own_names = Set.new
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Iterates over helpers in registration order.
|
|
82
|
+
#
|
|
83
|
+
# @yieldparam helper [Maintenance::Options::Helper] each registered helper
|
|
84
|
+
# @return [OptionHelpersCollection, Enumerator] self, or an enumerator without a block
|
|
85
|
+
def each(&block)
|
|
86
|
+
return enum_for(:each) unless block_given?
|
|
87
|
+
|
|
88
|
+
@helpers.each_value(&block)
|
|
89
|
+
self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Registers a helper under its name.
|
|
93
|
+
#
|
|
94
|
+
# A helper with a new name is appended; a helper replacing one registered
|
|
95
|
+
# before duplication keeps its position. The helpers are left unchanged
|
|
96
|
+
# unless the result is `:registered`.
|
|
97
|
+
#
|
|
98
|
+
# @param helper [Maintenance::Options::Helper] the helper to register
|
|
99
|
+
# @return [Symbol] `:registered`, `:skipped` when this very helper is
|
|
100
|
+
# already registered (the name then counts as registered by this collection
|
|
101
|
+
# itself), `:reserved` when the name belongs to a built-in helper,
|
|
102
|
+
# or `:duplicated` when this collection already registered the name itself
|
|
103
|
+
# since it was created or duplicated
|
|
104
|
+
def register(helper)
|
|
105
|
+
name = helper.name
|
|
106
|
+
|
|
107
|
+
if @helpers[name].equal?(helper)
|
|
108
|
+
@own_names << name
|
|
109
|
+
return :skipped
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
return :reserved if @builtin_names.include?(name)
|
|
113
|
+
return :duplicated if @own_names.include?(name)
|
|
114
|
+
|
|
115
|
+
@own_names << name
|
|
116
|
+
@helpers[name] = helper
|
|
117
|
+
:registered
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Registers a helper under its name, raising when the name is rejected.
|
|
121
|
+
#
|
|
122
|
+
# Follows `register`: a helper registered before duplication may be
|
|
123
|
+
# replaced once. Re-adding this very helper leaves the helpers unchanged
|
|
124
|
+
# but still counts as this collection registering the name itself, so
|
|
125
|
+
# after re-adding an inherited helper a different helper with that name
|
|
126
|
+
# raises instead of replacing it.
|
|
127
|
+
#
|
|
128
|
+
# @param helper [Maintenance::Options::Helper] the helper to register
|
|
129
|
+
# @return [OptionHelpersCollection] self
|
|
130
|
+
# @raise [ArgumentError] when the name belongs to a built-in helper
|
|
131
|
+
# or this collection already registered the name itself
|
|
132
|
+
def <<(helper)
|
|
133
|
+
case register(helper)
|
|
134
|
+
when :reserved
|
|
135
|
+
raise_error_about_reserved_name_with(helper.name)
|
|
136
|
+
when :duplicated
|
|
137
|
+
raise_error_about_duplicated_name_with(helper.name)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
self
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Registers every helper of the given enumerables in order with `<<`.
|
|
144
|
+
#
|
|
145
|
+
# Helpers preceding a rejected one stay registered.
|
|
146
|
+
#
|
|
147
|
+
# A built-in helper coming from another service's collection counts as
|
|
148
|
+
# a reserved name unless it is the very helper of this collection. Each
|
|
149
|
+
# service rebinds the built-in dynamic options (`consists_of`, `schema`)
|
|
150
|
+
# to its own configuration, so merging another service's whole
|
|
151
|
+
# collection raises.
|
|
152
|
+
#
|
|
153
|
+
# @param enums [Array<Enumerable<Maintenance::Options::Helper>>] the helpers to register
|
|
154
|
+
# @return [OptionHelpersCollection] self
|
|
155
|
+
# @raise [ArgumentError] when `<<` rejects a helper
|
|
156
|
+
def merge(*enums)
|
|
157
|
+
enums.each { |helpers| helpers.each { |helper| self << helper } }
|
|
158
|
+
self
|
|
58
159
|
end
|
|
59
160
|
|
|
60
161
|
# Returns a new collection containing only dynamic option helpers.
|
|
61
162
|
#
|
|
62
163
|
# @return [OptionHelpersCollection] filtered collection of dynamic helpers
|
|
63
164
|
def dynamic_options
|
|
64
|
-
OptionHelpersCollection.new
|
|
165
|
+
each_with_object(OptionHelpersCollection.new) do |helper, collection|
|
|
166
|
+
collection.register(helper) if helper.dynamic_option?
|
|
167
|
+
end
|
|
65
168
|
end
|
|
66
169
|
|
|
67
|
-
# Finds a helper by its name
|
|
170
|
+
# Finds a helper by its name with a direct Hash lookup.
|
|
68
171
|
#
|
|
69
172
|
# @param name [Symbol] the helper name to find
|
|
70
173
|
# @return [Maintenance::Options::Helper, nil] the found helper or nil
|
|
71
174
|
def find_by(name:)
|
|
72
|
-
|
|
175
|
+
@helpers[name]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Replaces a built-in helper by name with a new one, keeping its position.
|
|
179
|
+
#
|
|
180
|
+
# Names that do not belong to a built-in helper are ignored.
|
|
181
|
+
#
|
|
182
|
+
# @param name [Symbol] the built-in helper name to replace
|
|
183
|
+
# @param with [Maintenance::Options::Helper] the replacement helper
|
|
184
|
+
# @return [void]
|
|
185
|
+
def replace(name:, with:)
|
|
186
|
+
return unless @builtin_names.include?(name)
|
|
187
|
+
|
|
188
|
+
@helpers[name] = with
|
|
73
189
|
end
|
|
74
190
|
|
|
75
191
|
private
|
|
76
192
|
|
|
77
|
-
#
|
|
193
|
+
# Raises an error about a name that belongs to a built-in helper.
|
|
78
194
|
#
|
|
79
|
-
# @
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
195
|
+
# @param name [Symbol] the rejected helper name
|
|
196
|
+
# @raise [ArgumentError] always
|
|
197
|
+
def raise_error_about_reserved_name_with(name)
|
|
198
|
+
raise ArgumentError,
|
|
199
|
+
"The `#{name}` option helper name is reserved by a built-in option helper. " \
|
|
200
|
+
"See configuration example here: https://servactory.com/guide/configuration"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Raises an error about a name this collection already registered itself.
|
|
204
|
+
#
|
|
205
|
+
# @param name [Symbol] the rejected helper name
|
|
206
|
+
# @raise [ArgumentError] always
|
|
207
|
+
def raise_error_about_duplicated_name_with(name)
|
|
208
|
+
raise ArgumentError,
|
|
209
|
+
"The `#{name}` option helper is already registered in this collection. " \
|
|
210
|
+
"See configuration example here: https://servactory.com/guide/configuration"
|
|
84
211
|
end
|
|
85
212
|
end
|
|
86
213
|
end
|
|
@@ -45,7 +45,7 @@ module Servactory
|
|
|
45
45
|
# @param _args [Array] Method arguments (unused)
|
|
46
46
|
# @return [Object] Input value or predicate result
|
|
47
47
|
def method_missing(name, *_args)
|
|
48
|
-
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
48
|
+
predicate = @context.class.config.predicate_methods_enabled && name.end_with?("?")
|
|
49
49
|
|
|
50
50
|
input_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
51
51
|
|
|
@@ -38,7 +38,7 @@ module Servactory
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
40
|
def fetch_with(name:, &_block)
|
|
41
|
-
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
41
|
+
predicate = @context.class.config.predicate_methods_enabled && name.end_with?("?")
|
|
42
42
|
|
|
43
43
|
input_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
44
44
|
|
|
@@ -67,7 +67,7 @@ module Servactory
|
|
|
67
67
|
def resolve_input(name)
|
|
68
68
|
return true if @collection_of_inputs.find_by(name:)
|
|
69
69
|
|
|
70
|
-
@context.config.predicate_methods_enabled &&
|
|
70
|
+
@context.class.config.predicate_methods_enabled &&
|
|
71
71
|
name.to_s.end_with?("?") &&
|
|
72
72
|
@collection_of_inputs.find_by(name: name.to_s.chomp("?").to_sym)
|
|
73
73
|
end
|
|
@@ -60,7 +60,7 @@ module Servactory
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def fetch_with(name:, &_block)
|
|
63
|
-
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
63
|
+
predicate = @context.class.config.predicate_methods_enabled && name.end_with?("?")
|
|
64
64
|
|
|
65
65
|
internal_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
66
66
|
internal = @collection_of_internals.find_by(name: internal_name)
|
|
@@ -89,7 +89,7 @@ module Servactory
|
|
|
89
89
|
def resolve_internal(name)
|
|
90
90
|
return true if @collection_of_internals.find_by(name:)
|
|
91
91
|
|
|
92
|
-
@context.config.predicate_methods_enabled &&
|
|
92
|
+
@context.class.config.predicate_methods_enabled &&
|
|
93
93
|
name.to_s.end_with?("?") &&
|
|
94
94
|
@collection_of_internals.find_by(name: name.to_s.chomp("?").to_sym)
|
|
95
95
|
end
|
|
@@ -52,7 +52,7 @@ module Servactory
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def fetch_with(name:, &_block)
|
|
55
|
-
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
55
|
+
predicate = @context.class.config.predicate_methods_enabled && name.end_with?("?")
|
|
56
56
|
|
|
57
57
|
output_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
58
58
|
output = @collection_of_outputs.find_by(name: output_name)
|
|
@@ -67,7 +67,7 @@ module Servactory
|
|
|
67
67
|
def resolve_output(name)
|
|
68
68
|
return true if @collection_of_outputs.find_by(name:)
|
|
69
69
|
|
|
70
|
-
@context.config.predicate_methods_enabled &&
|
|
70
|
+
@context.class.config.predicate_methods_enabled &&
|
|
71
71
|
name.to_s.end_with?("?") &&
|
|
72
72
|
@collection_of_outputs.find_by(name: name.to_s.chomp("?").to_sym)
|
|
73
73
|
end
|
|
@@ -8,7 +8,7 @@ module Servactory
|
|
|
8
8
|
|
|
9
9
|
def initialize(context)
|
|
10
10
|
@class_name = context.class.name
|
|
11
|
-
@i18n_root_key = context.config.i18n_root_key
|
|
11
|
+
@i18n_root_key = context.class.config.i18n_root_key
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def translate(key, **options)
|
|
@@ -42,11 +42,11 @@ module Servactory
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def success!
|
|
45
|
-
raise config.success_class.new(context: self)
|
|
45
|
+
raise self.class.config.success_class.new(context: self)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def fail_input!(input_name, message:, meta: nil)
|
|
49
|
-
raise config.input_exception_class.new(
|
|
49
|
+
raise self.class.config.input_exception_class.new(
|
|
50
50
|
context: self,
|
|
51
51
|
input_name:,
|
|
52
52
|
message:,
|
|
@@ -55,7 +55,7 @@ module Servactory
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def fail_internal!(internal_name, message:, meta: nil)
|
|
58
|
-
raise config.internal_exception_class.new(
|
|
58
|
+
raise self.class.config.internal_exception_class.new(
|
|
59
59
|
context: self,
|
|
60
60
|
internal_name:,
|
|
61
61
|
message:,
|
|
@@ -64,7 +64,7 @@ module Servactory
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def fail_output!(output_name, message:, meta: nil)
|
|
67
|
-
raise config.output_exception_class.new(
|
|
67
|
+
raise self.class.config.output_exception_class.new(
|
|
68
68
|
context: self,
|
|
69
69
|
output_name:,
|
|
70
70
|
message:,
|
|
@@ -73,7 +73,7 @@ module Servactory
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def fail!(type = :base, message:, meta: nil)
|
|
76
|
-
raise config.failure_class.new(type:, message:, meta:)
|
|
76
|
+
raise self.class.config.failure_class.new(type:, message:, meta:)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def fail_result!(service_result)
|
|
@@ -118,7 +118,7 @@ module Servactory
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def call
|
|
121
|
-
raise config.failure_class.new(
|
|
121
|
+
raise self.class.config.failure_class.new(
|
|
122
122
|
type: :base,
|
|
123
123
|
message: servactory_service_info.translate(
|
|
124
124
|
"methods.call.not_used"
|
|
@@ -26,7 +26,7 @@ module Servactory
|
|
|
26
26
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
|
-
# rubocop:disable Metrics/MethodLength
|
|
29
|
+
# rubocop:disable-next Metrics/MethodLength
|
|
30
30
|
def build_all_attributes(inputs:, internals:, outputs:, stages:, config:)
|
|
31
31
|
build_input_attributes_with(
|
|
32
32
|
collection: inputs,
|
|
@@ -45,7 +45,6 @@ module Servactory
|
|
|
45
45
|
|
|
46
46
|
build_action_stages_with(collection: stages)
|
|
47
47
|
end
|
|
48
|
-
# rubocop:enable Metrics/MethodLength
|
|
49
48
|
|
|
50
49
|
def build_input_attributes_with(collection:, dynamic_options:)
|
|
51
50
|
@inputs = build_attributes_with(
|
|
@@ -142,11 +141,13 @@ module Servactory
|
|
|
142
141
|
end
|
|
143
142
|
|
|
144
143
|
def enrich_options_with_common_fields(attribute:, options:)
|
|
144
|
+
types = attribute.collection_of_options.find_by(name: :types)
|
|
145
145
|
must = attribute.collection_of_options.find_by(name: :must)
|
|
146
146
|
|
|
147
147
|
options.merge(
|
|
148
148
|
actor: attribute.actor,
|
|
149
|
-
types:
|
|
149
|
+
type: { is: types.value, message: types.body[:message] },
|
|
150
|
+
types: types.value,
|
|
150
151
|
must: must&.value
|
|
151
152
|
)
|
|
152
153
|
end
|
|
@@ -18,12 +18,16 @@ module Servactory
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def input(name, *helpers, **options)
|
|
21
|
-
|
|
21
|
+
input = Input.new(
|
|
22
22
|
name,
|
|
23
23
|
*helpers,
|
|
24
24
|
option_helpers: config.input_option_helpers,
|
|
25
25
|
**options
|
|
26
26
|
)
|
|
27
|
+
|
|
28
|
+
raise ArgumentError, "[#{self.name}] Input `#{name}` must have the `type` option" if input.types.empty?
|
|
29
|
+
|
|
30
|
+
collection_of_inputs << input
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def collection_of_inputs
|
|
@@ -52,7 +52,7 @@ module Servactory
|
|
|
52
52
|
|
|
53
53
|
attr_reader :internal_name
|
|
54
54
|
|
|
55
|
-
# rubocop:disable Style/KeywordParametersOrder
|
|
55
|
+
# rubocop:disable-next Style/KeywordParametersOrder
|
|
56
56
|
def initialize(
|
|
57
57
|
name,
|
|
58
58
|
*helpers,
|
|
@@ -64,7 +64,6 @@ module Servactory
|
|
|
64
64
|
|
|
65
65
|
super(name, *helpers, option_helpers:, **options)
|
|
66
66
|
end
|
|
67
|
-
# rubocop:enable Style/KeywordParametersOrder
|
|
68
67
|
|
|
69
68
|
def conflict_code
|
|
70
69
|
@collection_of_options.defined_conflict_code
|
|
@@ -29,30 +29,22 @@ module Servactory
|
|
|
29
29
|
def process_input(context, warehouse, input)
|
|
30
30
|
value = warehouse.fetch_input(input.name)
|
|
31
31
|
|
|
32
|
-
input.
|
|
33
|
-
error = process_option(context, input, value, check_key, check_options)
|
|
32
|
+
input.collection_of_options.validations_for_checks.each do |check_key, check_options, validation_class|
|
|
33
|
+
error = process_option(context, input, value, check_key, check_options, validation_class)
|
|
34
34
|
return error if error.present?
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
nil
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def process_option(context, input, value, check_key, check_options)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
value:,
|
|
49
|
-
check_key:,
|
|
50
|
-
check_options:
|
|
51
|
-
)
|
|
52
|
-
return error_message if error_message.present?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
nil
|
|
40
|
+
def process_option(context, input, value, check_key, check_options, validation_class)
|
|
41
|
+
validation_class.check(
|
|
42
|
+
context:,
|
|
43
|
+
attribute: input,
|
|
44
|
+
value:,
|
|
45
|
+
check_key:,
|
|
46
|
+
check_options:
|
|
47
|
+
)
|
|
56
48
|
end
|
|
57
49
|
end
|
|
58
50
|
end
|
|
@@ -17,12 +17,18 @@ module Servactory
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def internal(name, *helpers, **options)
|
|
20
|
-
|
|
20
|
+
internal = Internal.new(
|
|
21
21
|
name,
|
|
22
22
|
*helpers,
|
|
23
23
|
option_helpers: config.internal_option_helpers,
|
|
24
24
|
**options
|
|
25
25
|
)
|
|
26
|
+
|
|
27
|
+
if internal.types.empty?
|
|
28
|
+
raise ArgumentError, "[#{self.name}] Internal attribute `#{name}` must have the `type` option"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
collection_of_internals << internal
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
def collection_of_internals
|
|
@@ -39,8 +39,11 @@ module Servactory
|
|
|
39
39
|
@collection_of_options = options_registrar.collection
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# @deprecated Use collection_of_options.validations_for_checks instead.
|
|
42
43
|
def options_for_checks
|
|
43
|
-
|
|
44
|
+
warn "[DEPRECATION] Servactory::Maintenance::Attributes::Base#options_for_checks is deprecated. " \
|
|
45
|
+
"Use collection_of_options.validations_for_checks instead."
|
|
46
|
+
@collection_of_options.send(:check_options_by_key)
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
def system_name
|
|
@@ -13,6 +13,10 @@ module Servactory
|
|
|
13
13
|
# how attributes are indexed and filtered (e.g., inputs use `internal_name`
|
|
14
14
|
# for the `as:` parameter).
|
|
15
15
|
#
|
|
16
|
+
# An attribute is declared once per name: adding an attribute whose
|
|
17
|
+
# `name` is already declared (e.g., redeclaring a parent's input in
|
|
18
|
+
# a child service) replaces the earlier declaration in place.
|
|
19
|
+
#
|
|
16
20
|
# ## Usage
|
|
17
21
|
#
|
|
18
22
|
# Subclasses are used internally by Input, Internal, and Output DSL modules
|
|
@@ -32,16 +36,15 @@ module Servactory
|
|
|
32
36
|
#
|
|
33
37
|
# The collection uses memoization for frequently accessed data:
|
|
34
38
|
# - `attributes_index` - cached hash for O(1) lookups by name
|
|
39
|
+
# - `declarations_index` - cached hash for O(1) redeclaration checks
|
|
35
40
|
#
|
|
36
41
|
class Collection
|
|
37
42
|
extend Forwardable
|
|
38
43
|
|
|
39
44
|
def_delegators :@collection,
|
|
40
|
-
:<<,
|
|
41
45
|
:filter,
|
|
42
46
|
:each, :each_with_object,
|
|
43
|
-
:map, :to_h
|
|
44
|
-
:merge
|
|
47
|
+
:map, :to_h
|
|
45
48
|
|
|
46
49
|
# Initializes the collection with an optional pre-built Set.
|
|
47
50
|
#
|
|
@@ -51,6 +54,50 @@ module Servactory
|
|
|
51
54
|
@collection = collection
|
|
52
55
|
end
|
|
53
56
|
|
|
57
|
+
# Duplicates the collection, resetting memoized caches.
|
|
58
|
+
#
|
|
59
|
+
# @param original [Collection] the collection being duplicated
|
|
60
|
+
# @return [void]
|
|
61
|
+
def initialize_dup(original)
|
|
62
|
+
super
|
|
63
|
+
@collection = original.instance_variable_get(:@collection).dup
|
|
64
|
+
@attributes_index = nil
|
|
65
|
+
@declarations_index = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Adds an attribute to the collection, invalidating the index cache.
|
|
69
|
+
#
|
|
70
|
+
# An attribute with an already declared `name` replaces
|
|
71
|
+
# the earlier declaration in place, keeping its position.
|
|
72
|
+
#
|
|
73
|
+
# @param attribute [Object] the attribute to add
|
|
74
|
+
# @return [Collection] self
|
|
75
|
+
def <<(attribute)
|
|
76
|
+
redeclared = declarations_index[attribute.name]
|
|
77
|
+
|
|
78
|
+
if redeclared.nil?
|
|
79
|
+
@collection << attribute
|
|
80
|
+
else
|
|
81
|
+
@collection = Set.new(@collection) { |declared| declared.equal?(redeclared) ? attribute : declared }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
declarations_index[attribute.name] = attribute
|
|
85
|
+
@attributes_index = nil
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Merges attributes from another collection, invalidating the index cache.
|
|
90
|
+
#
|
|
91
|
+
# Each attribute is added as with `<<`, so redeclarations replace
|
|
92
|
+
# the earlier declarations in place.
|
|
93
|
+
#
|
|
94
|
+
# @param other [Collection, Enumerable] attributes to merge
|
|
95
|
+
# @return [Collection] self
|
|
96
|
+
def merge(other)
|
|
97
|
+
other.each { |attribute| self << attribute }
|
|
98
|
+
self
|
|
99
|
+
end
|
|
100
|
+
|
|
54
101
|
# Returns names of all attributes in the collection.
|
|
55
102
|
#
|
|
56
103
|
# @return [Array<Symbol>] list of attribute names
|
|
@@ -88,9 +135,17 @@ module Servactory
|
|
|
88
135
|
#
|
|
89
136
|
# @return [Hash{Symbol => Object}] attribute names mapped to attribute instances
|
|
90
137
|
def attributes_index
|
|
91
|
-
@attributes_index ||=
|
|
92
|
-
|
|
93
|
-
|
|
138
|
+
@attributes_index ||= to_h { |attribute| [lookup_name(attribute), attribute] }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Builds and caches a hash index of declared attributes by `name`.
|
|
142
|
+
#
|
|
143
|
+
# Unlike `attributes_index`, it is keyed by the declared name
|
|
144
|
+
# (the external name for inputs), which identifies a redeclaration.
|
|
145
|
+
#
|
|
146
|
+
# @return [Hash{Symbol => Object}] declared names mapped to attribute instances
|
|
147
|
+
def declarations_index
|
|
148
|
+
@declarations_index ||= to_h { |attribute| [attribute.name, attribute] }
|
|
94
149
|
end
|
|
95
150
|
|
|
96
151
|
# Returns the name used for indexing and filtering a given attribute.
|