servactory 3.0.0.rc3 → 3.0.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/lib/servactory/context/warehouse/base.rb +20 -4
- data/lib/servactory/context/warehouse/crate.rb +39 -0
- data/lib/servactory/context/warehouse/inputs.rb +41 -10
- data/lib/servactory/context/warehouse/internals.rb +7 -2
- data/lib/servactory/context/warehouse/outputs.rb +7 -0
- data/lib/servactory/context/warehouse/setup.rb +53 -30
- data/lib/servactory/context/workspace/inputs.rb +6 -8
- data/lib/servactory/context/workspace/internals.rb +5 -7
- data/lib/servactory/context/workspace/outputs.rb +5 -7
- data/lib/servactory/inputs/input.rb +28 -18
- data/lib/servactory/inputs/tools/validation.rb +9 -9
- data/lib/servactory/inputs/validations/required.rb +51 -27
- data/lib/servactory/internals/internal.rb +20 -16
- data/lib/servactory/maintenance/attributes/tools/validation.rb +9 -10
- data/lib/servactory/maintenance/attributes/validations/concerns/error_builder.rb +52 -0
- data/lib/servactory/maintenance/attributes/validations/must.rb +164 -57
- data/lib/servactory/maintenance/attributes/validations/type.rb +77 -27
- data/lib/servactory/maintenance/validations/types.rb +18 -33
- data/lib/servactory/outputs/output.rb +18 -13
- data/lib/servactory/result.rb +266 -43
- data/lib/servactory/version.rb +1 -1
- metadata +3 -6
- data/lib/servactory/inputs/validations/base.rb +0 -21
- data/lib/servactory/inputs/validations/errors.rb +0 -17
- data/lib/servactory/maintenance/attributes/tools/check_errors.rb +0 -23
- data/lib/servactory/maintenance/attributes/validations/base.rb +0 -23
- data/lib/servactory/maintenance/attributes/validations/errors.rb +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c94166cc0d29f50acac299374f644be29db0b4ee533ca49c7f9e0ec698c32a6
|
|
4
|
+
data.tar.gz: 022a3b05c74fbef3005fd6ec7cc09b9d3b8459d5544578bc09731e9d8378f2e5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f411d897f5885d6b783c9b5531c6ae1b71d0b681b8249d635bf8e6152613bb43630f6705674f33cb54351daa9f207352fb2ab23b488629a05a5bfc4a44329e01
|
|
7
|
+
data.tar.gz: db0bd759b7e7da5c50a3283b144a8ba8ba1a58ce6fa3fd7c91709e46adcaa661fce2103d03fe2e70a59ba37906f984c8c4944bf54d54aa00a8832b76552e7b83
|
|
@@ -3,19 +3,35 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Context
|
|
5
5
|
module Warehouse
|
|
6
|
+
# Base class for warehouse storage views.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Provides common fetch and assign operations for all warehouse
|
|
11
|
+
# view classes (Inputs, Internals, Outputs).
|
|
6
12
|
class Base
|
|
13
|
+
# Creates base view with storage hash reference.
|
|
14
|
+
#
|
|
15
|
+
# @param arguments [Hash] Reference to Crate data
|
|
16
|
+
# @return [Base] New base view
|
|
7
17
|
def initialize(arguments = {})
|
|
8
18
|
@arguments = arguments
|
|
9
19
|
end
|
|
10
20
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
|
|
21
|
+
# Retrieves value by name with default fallback.
|
|
22
|
+
#
|
|
23
|
+
# @param name [Symbol] Key name
|
|
24
|
+
# @param default_value [Object] Value if not found
|
|
25
|
+
# @return [Object] Stored or default value
|
|
15
26
|
def fetch(name, default_value)
|
|
16
27
|
@arguments.fetch(name, default_value)
|
|
17
28
|
end
|
|
18
29
|
|
|
30
|
+
# Stores value by key.
|
|
31
|
+
#
|
|
32
|
+
# @param key [Symbol] Key name
|
|
33
|
+
# @param value [Object] Value to store
|
|
34
|
+
# @return [Object] Stored value
|
|
19
35
|
def assign(key, value)
|
|
20
36
|
@arguments[key] = value
|
|
21
37
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Context
|
|
5
|
+
module Warehouse
|
|
6
|
+
# Unified crate for service context data.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Crate provides a single object that holds all three data sections
|
|
11
|
+
# (inputs, internals, outputs) in separate hashes. This reduces object
|
|
12
|
+
# allocations compared to creating separate warehouse instances.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# Crate is used internally by Setup class:
|
|
17
|
+
#
|
|
18
|
+
# ```ruby
|
|
19
|
+
# crate = Crate.new
|
|
20
|
+
# crate.inputs[:name] = "value"
|
|
21
|
+
# crate.outputs[:result] = result
|
|
22
|
+
# ```
|
|
23
|
+
class Crate
|
|
24
|
+
attr_reader :inputs,
|
|
25
|
+
:internals,
|
|
26
|
+
:outputs
|
|
27
|
+
|
|
28
|
+
# Creates new crate with empty sections.
|
|
29
|
+
#
|
|
30
|
+
# @return [Crate] New crate instance
|
|
31
|
+
def initialize
|
|
32
|
+
@inputs = {}
|
|
33
|
+
@internals = {}
|
|
34
|
+
@outputs = {}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -3,41 +3,72 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Context
|
|
5
5
|
module Warehouse
|
|
6
|
+
# View for accessing service input values.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Inputs provides access to service input data with dynamic method access,
|
|
11
|
+
# predicate support, and error handling. It references Crate data
|
|
12
|
+
# instead of creating its own.
|
|
6
13
|
class Inputs < Base
|
|
7
|
-
|
|
14
|
+
# Creates inputs view referencing Crate data.
|
|
15
|
+
#
|
|
16
|
+
# @param context [Object] Service context for error handling
|
|
17
|
+
# @param arguments [Hash] Reference to Crate#inputs
|
|
18
|
+
# @return [Inputs] New inputs view
|
|
19
|
+
def initialize(context, arguments)
|
|
8
20
|
@context = context
|
|
9
21
|
|
|
10
22
|
super(arguments)
|
|
11
23
|
end
|
|
12
24
|
|
|
25
|
+
# Returns array of input names.
|
|
26
|
+
#
|
|
27
|
+
# @return [Array<Symbol>] Input names
|
|
13
28
|
def names
|
|
14
29
|
@arguments.keys
|
|
15
30
|
end
|
|
16
31
|
|
|
32
|
+
# Merges new arguments into storage.
|
|
33
|
+
#
|
|
34
|
+
# @param arguments [Hash] Input name-value pairs to merge
|
|
35
|
+
# @return [Hash] Updated arguments hash
|
|
17
36
|
def merge!(arguments)
|
|
18
37
|
@arguments.merge!(arguments)
|
|
19
38
|
end
|
|
20
39
|
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
# Delegates method calls to stored inputs.
|
|
41
|
+
#
|
|
42
|
+
# Supports predicate methods when enabled in config.
|
|
43
|
+
#
|
|
44
|
+
# @param name [Symbol] Method name (input or predicate)
|
|
45
|
+
# @param _args [Array] Method arguments (unused)
|
|
46
|
+
# @return [Object] Input value or predicate result
|
|
23
47
|
def method_missing(name, *_args)
|
|
24
|
-
|
|
48
|
+
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
49
|
+
|
|
50
|
+
input_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
25
51
|
|
|
26
52
|
input_value = @arguments.fetch(input_name) { raise_error_for(input_name) }
|
|
27
53
|
|
|
28
|
-
|
|
29
|
-
Servactory::Utils.query_attribute(input_value)
|
|
30
|
-
else
|
|
31
|
-
input_value
|
|
32
|
-
end
|
|
54
|
+
predicate ? Servactory::Utils.query_attribute(input_value) : input_value
|
|
33
55
|
end
|
|
34
56
|
|
|
57
|
+
# Checks if method corresponds to stored input.
|
|
58
|
+
#
|
|
59
|
+
# @param name [Symbol] Method name to check
|
|
60
|
+
# @param _include_private [Boolean] Include private methods in check
|
|
61
|
+
# @return [Boolean] True if input exists for this method
|
|
35
62
|
def respond_to_missing?(name, *)
|
|
36
63
|
@arguments.fetch(name) { raise_error_for(name) }
|
|
37
64
|
end
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
private
|
|
40
67
|
|
|
68
|
+
# Raises error for undefined input.
|
|
69
|
+
#
|
|
70
|
+
# @param input_name [Symbol] Name of undefined input
|
|
71
|
+
# @raise [Exception] Failure exception with localized message
|
|
41
72
|
def raise_error_for(input_name)
|
|
42
73
|
message_text = @context.send(:servactory_service_info).translate(
|
|
43
74
|
"inputs.undefined.for_fetch",
|
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Context
|
|
5
5
|
module Warehouse
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# View for accessing service internal values.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Internals provides simple key-value storage for intermediate
|
|
11
|
+
# service state. It references Crate data.
|
|
12
|
+
class Internals < Base; end
|
|
8
13
|
end
|
|
9
14
|
end
|
|
10
15
|
end
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Context
|
|
5
5
|
module Warehouse
|
|
6
|
+
# View for accessing service output values.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Outputs provides storage for service result values with iteration
|
|
11
|
+
# support via each_pair delegation. Used by Result class to build
|
|
12
|
+
# output hash.
|
|
6
13
|
class Outputs < Base
|
|
7
14
|
extend Forwardable
|
|
8
15
|
|
|
@@ -3,69 +3,92 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Context
|
|
5
5
|
module Warehouse
|
|
6
|
+
# Orchestrator for service context data.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Setup manages a unified Crate instance and provides the public
|
|
11
|
+
# interface for accessing inputs, internals, and outputs. It creates
|
|
12
|
+
# view objects lazily on first access.
|
|
6
13
|
class Setup
|
|
14
|
+
# Creates setup with unified crate.
|
|
15
|
+
#
|
|
16
|
+
# @param context [Object] Service context
|
|
17
|
+
# @return [Setup] New setup instance
|
|
7
18
|
def initialize(context)
|
|
8
19
|
@context = context
|
|
20
|
+
@crate = Crate.new
|
|
9
21
|
end
|
|
10
22
|
|
|
23
|
+
# Merges input arguments into storage.
|
|
24
|
+
#
|
|
25
|
+
# @param arguments [Hash] Input name-value pairs
|
|
26
|
+
# @return [Hash] Updated inputs hash
|
|
11
27
|
def assign_inputs(arguments)
|
|
12
|
-
|
|
28
|
+
inputs.merge!(arguments)
|
|
13
29
|
end
|
|
14
30
|
|
|
31
|
+
# Retrieves input value by name.
|
|
32
|
+
#
|
|
33
|
+
# @param name [Symbol] Input name
|
|
34
|
+
# @return [Object, nil] Input value or nil
|
|
15
35
|
def fetch_input(name)
|
|
16
36
|
inputs.fetch(name, nil)
|
|
17
37
|
end
|
|
18
38
|
|
|
39
|
+
# Stores internal value by name.
|
|
40
|
+
#
|
|
41
|
+
# @param name [Symbol] Internal name
|
|
42
|
+
# @param value [Object] Value to store
|
|
43
|
+
# @return [Object] Stored value
|
|
19
44
|
def assign_internal(name, value)
|
|
20
|
-
|
|
45
|
+
internals.assign(name, value)
|
|
21
46
|
end
|
|
22
47
|
|
|
48
|
+
# Retrieves internal value by name.
|
|
49
|
+
#
|
|
50
|
+
# @param name [Symbol] Internal name
|
|
51
|
+
# @return [Object, nil] Internal value or nil
|
|
23
52
|
def fetch_internal(name)
|
|
24
53
|
internals.fetch(name, nil)
|
|
25
54
|
end
|
|
26
55
|
|
|
56
|
+
# Stores output value by name.
|
|
57
|
+
#
|
|
58
|
+
# @param name [Symbol] Output name
|
|
59
|
+
# @param value [Object] Value to store
|
|
60
|
+
# @return [Object] Stored value
|
|
27
61
|
def assign_output(name, value)
|
|
28
|
-
|
|
62
|
+
outputs.assign(name, value)
|
|
29
63
|
end
|
|
30
64
|
|
|
65
|
+
# Retrieves output value by name.
|
|
66
|
+
#
|
|
67
|
+
# @param name [Symbol] Output name
|
|
68
|
+
# @return [Object, nil] Output value or nil
|
|
31
69
|
def fetch_output(name)
|
|
32
70
|
outputs.fetch(name, nil)
|
|
33
71
|
end
|
|
34
72
|
|
|
73
|
+
# Returns inputs view object.
|
|
74
|
+
#
|
|
75
|
+
# @return [Inputs] Inputs view
|
|
35
76
|
def inputs
|
|
36
|
-
@inputs ||=
|
|
77
|
+
@inputs ||= Inputs.new(@context, @crate.inputs)
|
|
37
78
|
end
|
|
38
79
|
|
|
80
|
+
# Returns internals view object.
|
|
81
|
+
#
|
|
82
|
+
# @return [Internals] Internals view
|
|
39
83
|
def internals
|
|
40
|
-
@internals ||=
|
|
84
|
+
@internals ||= Internals.new(@crate.internals)
|
|
41
85
|
end
|
|
42
86
|
|
|
87
|
+
# Returns outputs view object.
|
|
88
|
+
#
|
|
89
|
+
# @return [Outputs] Outputs view
|
|
43
90
|
def outputs
|
|
44
|
-
@outputs ||=
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
def assign_attribute(section, name, value)
|
|
50
|
-
context_data[section].assign(name, value)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def context_data
|
|
54
|
-
@context_data ||= state.fetch(context_id)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def state
|
|
58
|
-
{
|
|
59
|
-
context_id => {
|
|
60
|
-
inputs: Servactory::Context::Warehouse::Inputs.new(@context),
|
|
61
|
-
internals: Servactory::Context::Warehouse::Internals.new,
|
|
62
|
-
outputs: Servactory::Context::Warehouse::Outputs.new
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def context_id
|
|
68
|
-
@context_id ||= "context_#{@context.object_id}"
|
|
91
|
+
@outputs ||= Outputs.new(@crate.outputs)
|
|
69
92
|
end
|
|
70
93
|
end
|
|
71
94
|
end
|
|
@@ -39,7 +39,9 @@ module Servactory
|
|
|
39
39
|
|
|
40
40
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Lint/UnusedMethodArgument
|
|
41
41
|
def fetch_with(name:, &block)
|
|
42
|
-
|
|
42
|
+
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
43
|
+
|
|
44
|
+
input_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
43
45
|
|
|
44
46
|
input = @collection_of_inputs.find_by(name: input_name)
|
|
45
47
|
|
|
@@ -51,14 +53,10 @@ module Servactory
|
|
|
51
53
|
input_value = input.default
|
|
52
54
|
end
|
|
53
55
|
|
|
54
|
-
input_prepare = input.prepare
|
|
55
|
-
input_value = input_prepare.call(value: input_value) if input_prepare
|
|
56
|
+
input_prepare = input.prepare[:in]
|
|
57
|
+
input_value = input_prepare.call(value: input_value) if input_prepare
|
|
56
58
|
|
|
57
|
-
|
|
58
|
-
Servactory::Utils.query_attribute(input_value)
|
|
59
|
-
else
|
|
60
|
-
input_value
|
|
61
|
-
end
|
|
59
|
+
predicate ? Servactory::Utils.query_attribute(input_value) : input_value
|
|
62
60
|
end
|
|
63
61
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Lint/UnusedMethodArgument
|
|
64
62
|
|
|
@@ -53,8 +53,10 @@ module Servactory
|
|
|
53
53
|
@context.send(:servactory_service_warehouse).assign_internal(internal.name, value)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def fetch_with(name:, &block) # rubocop:disable Metrics/MethodLength,
|
|
57
|
-
|
|
56
|
+
def fetch_with(name:, &block) # rubocop:disable Metrics/MethodLength, Lint/UnusedMethodArgument
|
|
57
|
+
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
58
|
+
|
|
59
|
+
internal_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
58
60
|
internal = @collection_of_internals.find_by(name: internal_name)
|
|
59
61
|
|
|
60
62
|
return yield if internal.nil?
|
|
@@ -67,11 +69,7 @@ module Servactory
|
|
|
67
69
|
value: internal_value
|
|
68
70
|
)
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
Servactory::Utils.query_attribute(internal_value)
|
|
72
|
-
else
|
|
73
|
-
internal_value
|
|
74
|
-
end
|
|
72
|
+
predicate ? Servactory::Utils.query_attribute(internal_value) : internal_value
|
|
75
73
|
end
|
|
76
74
|
|
|
77
75
|
def raise_error_for(type, name)
|
|
@@ -53,19 +53,17 @@ module Servactory
|
|
|
53
53
|
@context.send(:servactory_service_warehouse).assign_output(output.name, value)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def fetch_with(name:, &block) # rubocop:disable
|
|
57
|
-
|
|
56
|
+
def fetch_with(name:, &block) # rubocop:disable Lint/UnusedMethodArgument
|
|
57
|
+
predicate = @context.config.predicate_methods_enabled && name.end_with?("?")
|
|
58
|
+
|
|
59
|
+
output_name = predicate ? name.to_s.chomp("?").to_sym : name
|
|
58
60
|
output = @collection_of_outputs.find_by(name: output_name)
|
|
59
61
|
|
|
60
62
|
return yield if output.nil?
|
|
61
63
|
|
|
62
64
|
output_value = @context.send(:servactory_service_warehouse).fetch_output(output.name)
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
Servactory::Utils.query_attribute(output_value)
|
|
66
|
-
else
|
|
67
|
-
output_value
|
|
68
|
-
end
|
|
66
|
+
predicate ? Servactory::Utils.query_attribute(output_value) : output_value
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
def raise_error_for(type, name)
|
|
@@ -16,27 +16,37 @@ module Servactory
|
|
|
16
16
|
@types = input.types
|
|
17
17
|
@default = input.default
|
|
18
18
|
@options = input.options
|
|
19
|
+
@attribute = input
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def system_name
|
|
23
|
+
@attribute.system_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def i18n_name
|
|
27
|
+
@attribute.i18n_name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def optional?
|
|
31
|
+
@attribute.optional?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def required?
|
|
35
|
+
@attribute.required?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The methods below are required to support the internal work.
|
|
39
|
+
|
|
40
|
+
def input?
|
|
41
|
+
true
|
|
42
|
+
end
|
|
19
43
|
|
|
20
|
-
|
|
44
|
+
def internal?
|
|
45
|
+
false
|
|
21
46
|
end
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def define_identity_methods(input) # rubocop:disable Metrics/MethodLength
|
|
26
|
-
methods_map = {
|
|
27
|
-
system_name: -> { input.system_name },
|
|
28
|
-
i18n_name: -> { input.i18n_name },
|
|
29
|
-
optional?: -> { input.optional? },
|
|
30
|
-
required?: -> { input.required? },
|
|
31
|
-
# The methods below are required to support the internal work.
|
|
32
|
-
input?: -> { true },
|
|
33
|
-
internal?: -> { false },
|
|
34
|
-
output?: -> { false }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
methods_map.each do |method_name, implementation|
|
|
38
|
-
define_singleton_method(method_name, &implementation)
|
|
39
|
-
end
|
|
48
|
+
def output?
|
|
49
|
+
false
|
|
40
50
|
end
|
|
41
51
|
end
|
|
42
52
|
|
|
@@ -11,11 +11,13 @@ module Servactory
|
|
|
11
11
|
def initialize(context, collection_of_inputs)
|
|
12
12
|
@context = context
|
|
13
13
|
@collection_of_inputs = collection_of_inputs
|
|
14
|
+
@first_error = nil
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def validate!
|
|
17
18
|
@collection_of_inputs.each do |input|
|
|
18
19
|
process_input(input)
|
|
20
|
+
break if @first_error.present?
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
raise_errors
|
|
@@ -26,6 +28,7 @@ module Servactory
|
|
|
26
28
|
def process_input(input)
|
|
27
29
|
input.options_for_checks.each do |check_key, check_options|
|
|
28
30
|
process_option(check_key, check_options, input:)
|
|
31
|
+
break if @first_error.present?
|
|
29
32
|
end
|
|
30
33
|
end
|
|
31
34
|
|
|
@@ -34,16 +37,17 @@ module Servactory
|
|
|
34
37
|
return if validation_classes.empty?
|
|
35
38
|
|
|
36
39
|
validation_classes.each do |validation_class|
|
|
37
|
-
|
|
40
|
+
error_message = process_validation_class(
|
|
38
41
|
validation_class:,
|
|
39
42
|
input:,
|
|
40
43
|
check_key:,
|
|
41
44
|
check_options:
|
|
42
45
|
)
|
|
43
46
|
|
|
44
|
-
next if
|
|
47
|
+
next if error_message.blank?
|
|
45
48
|
|
|
46
|
-
|
|
49
|
+
@first_error = error_message
|
|
50
|
+
break
|
|
47
51
|
end
|
|
48
52
|
end
|
|
49
53
|
|
|
@@ -70,14 +74,10 @@ module Servactory
|
|
|
70
74
|
|
|
71
75
|
########################################################################
|
|
72
76
|
|
|
73
|
-
def errors
|
|
74
|
-
@errors ||= Servactory::Maintenance::Attributes::Tools::CheckErrors.new
|
|
75
|
-
end
|
|
76
|
-
|
|
77
77
|
def raise_errors
|
|
78
|
-
return if
|
|
78
|
+
return if @first_error.nil?
|
|
79
79
|
|
|
80
|
-
@context.fail_input!(nil, message:
|
|
80
|
+
@context.fail_input!(nil, message: @first_error)
|
|
81
81
|
end
|
|
82
82
|
end
|
|
83
83
|
end
|
|
@@ -3,45 +3,69 @@
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Inputs
|
|
5
5
|
module Validations
|
|
6
|
-
|
|
6
|
+
# Validates that required inputs have non-empty values.
|
|
7
|
+
#
|
|
8
|
+
# ## Purpose
|
|
9
|
+
#
|
|
10
|
+
# Required validator ensures that inputs marked as `required: true`
|
|
11
|
+
# have meaningful values. It checks for presence using `value_present?`
|
|
12
|
+
# which handles nil, empty strings, and blank values.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# This validator is automatically applied to inputs with `required: true`:
|
|
17
|
+
#
|
|
18
|
+
# ```ruby
|
|
19
|
+
# class MyService < ApplicationService::Base
|
|
20
|
+
# input :email, type: String, required: true
|
|
21
|
+
# input :name, type: String, required: { message: "Name is mandatory" }
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
class Required
|
|
25
|
+
extend Servactory::Maintenance::Attributes::Validations::Concerns::ErrorBuilder
|
|
26
|
+
|
|
27
|
+
# Validates that a required input has a present value.
|
|
28
|
+
#
|
|
29
|
+
# @param context [Object] Service context for error message formatting
|
|
30
|
+
# @param attribute [Inputs::Input] Input attribute to validate
|
|
31
|
+
# @param value [Object] Value to check for presence
|
|
32
|
+
# @param check_key [Symbol] Must be :required to trigger validation
|
|
33
|
+
# @return [String, nil] Error message on failure, nil on success
|
|
7
34
|
def self.check(context:, attribute:, value:, check_key:, **)
|
|
8
35
|
return unless should_be_checked_for?(attribute, check_key)
|
|
36
|
+
return if Servactory::Utils.value_present?(value)
|
|
9
37
|
|
|
10
|
-
|
|
38
|
+
build_error_message(context:, input: attribute, value:)
|
|
11
39
|
end
|
|
12
40
|
|
|
41
|
+
# Determines if validation should run for given input and check key.
|
|
42
|
+
#
|
|
43
|
+
# @param input [Inputs::Input] Input to check
|
|
44
|
+
# @param check_key [Symbol] Current validation check key
|
|
45
|
+
# @return [Boolean] true if this validator should run
|
|
13
46
|
def self.should_be_checked_for?(input, check_key)
|
|
14
47
|
check_key == :required && input.required?
|
|
15
48
|
end
|
|
49
|
+
private_class_method :should_be_checked_for?
|
|
16
50
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def check
|
|
28
|
-
return if Servactory::Utils.value_present?(@value)
|
|
29
|
-
|
|
30
|
-
_, message = @input.required.values_at(:is, :message)
|
|
31
|
-
|
|
32
|
-
add_error_with(message)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
51
|
+
# Builds error message from validation failure.
|
|
52
|
+
#
|
|
53
|
+
# @param context [Object] Service context
|
|
54
|
+
# @param input [Inputs::Input] Input that failed validation
|
|
55
|
+
# @param value [Object] The invalid value
|
|
56
|
+
# @return [String] Processed error message
|
|
57
|
+
def self.build_error_message(context:, input:, value:)
|
|
58
|
+
_, message = input.required.values_at(:is, :message)
|
|
59
|
+
message = message.presence || Servactory::Inputs::Translator::Required.default_message
|
|
36
60
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
value: @value
|
|
61
|
+
process_message(
|
|
62
|
+
message,
|
|
63
|
+
service: context.send(:servactory_service_info),
|
|
64
|
+
input:,
|
|
65
|
+
value:
|
|
43
66
|
)
|
|
44
67
|
end
|
|
68
|
+
private_class_method :build_error_message
|
|
45
69
|
end
|
|
46
70
|
end
|
|
47
71
|
end
|
|
@@ -12,25 +12,29 @@ module Servactory
|
|
|
12
12
|
@name = internal.name
|
|
13
13
|
@types = internal.types
|
|
14
14
|
@options = internal.options
|
|
15
|
+
@attribute = internal
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def system_name
|
|
19
|
+
@attribute.system_name
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def i18n_name
|
|
23
|
+
@attribute.i18n_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The methods below are required to support the internal work.
|
|
27
|
+
|
|
28
|
+
def input?
|
|
29
|
+
false
|
|
30
|
+
end
|
|
15
31
|
|
|
16
|
-
|
|
32
|
+
def internal?
|
|
33
|
+
true
|
|
17
34
|
end
|
|
18
35
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def define_identity_methods(internal)
|
|
22
|
-
methods_map = {
|
|
23
|
-
system_name: -> { internal.system_name },
|
|
24
|
-
i18n_name: -> { internal.i18n_name },
|
|
25
|
-
# The methods below are required to support the internal work.
|
|
26
|
-
input?: -> { false },
|
|
27
|
-
internal?: -> { true },
|
|
28
|
-
output?: -> { false }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
methods_map.each do |method_name, implementation|
|
|
32
|
-
define_singleton_method(method_name, &implementation)
|
|
33
|
-
end
|
|
36
|
+
def output?
|
|
37
|
+
false
|
|
34
38
|
end
|
|
35
39
|
end
|
|
36
40
|
|