servactory 3.0.4 → 3.1.0.rc1
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/actions/action.rb +2 -2
- data/lib/servactory/actions/collection.rb +1 -1
- data/lib/servactory/actions/stages/collection.rb +1 -1
- data/lib/servactory/configuration/config.rb +0 -16
- data/lib/servactory/configuration/configurable.rb +3 -6
- data/lib/servactory/configuration/hash_mode/class_names_collection.rb +1 -4
- data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +57 -8
- data/lib/servactory/context/warehouse/inputs.rb +28 -15
- data/lib/servactory/context/workspace/inputs.rb +27 -18
- data/lib/servactory/context/workspace/internals.rb +44 -27
- data/lib/servactory/context/workspace/outputs.rb +31 -23
- data/lib/servactory/dsl.rb +1 -1
- data/lib/servactory/info/builder.rb +2 -3
- data/lib/servactory/inputs/collection.rb +14 -21
- data/lib/servactory/inputs/input.rb +6 -103
- data/lib/servactory/inputs/tools/validation.rb +32 -57
- data/lib/servactory/inputs/validations/required.rb +3 -2
- data/lib/servactory/internals/collection.rb +5 -24
- data/lib/servactory/internals/internal.rb +4 -112
- data/lib/servactory/maintenance/attributes/base.rb +135 -0
- data/lib/servactory/maintenance/attributes/collection.rb +109 -0
- data/lib/servactory/maintenance/attributes/option_helper.rb +33 -12
- data/lib/servactory/maintenance/{attributes/options_collection.rb → options/collection.rb} +6 -7
- data/lib/servactory/maintenance/{attributes → options}/define_conflict.rb +1 -1
- data/lib/servactory/maintenance/{attributes → options}/define_method.rb +1 -1
- data/lib/servactory/maintenance/options/helper.rb +23 -0
- data/lib/servactory/maintenance/{attributes → options}/option.rb +50 -27
- data/lib/servactory/maintenance/options/registrar.rb +200 -0
- data/lib/servactory/maintenance/{attributes/validations → validations/checkers}/must.rb +25 -8
- data/lib/servactory/maintenance/{attributes/validations → validations/checkers}/type.rb +6 -5
- data/lib/servactory/maintenance/validations/concerns/error_builder.rb +50 -0
- data/lib/servactory/maintenance/validations/performer.rb +57 -0
- data/lib/servactory/maintenance/validations/support/type_validator.rb +36 -0
- data/lib/servactory/maintenance/{attributes → validations}/translator/must.rb +1 -1
- data/lib/servactory/maintenance/{attributes → validations}/translator/type.rb +1 -1
- data/lib/servactory/outputs/collection.rb +5 -24
- data/lib/servactory/outputs/output.rb +4 -112
- data/lib/servactory/result.rb +48 -39
- data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +2 -4
- data/lib/servactory/tool_kit/dynamic_options/must.rb +2 -2
- data/lib/servactory/tool_kit/dynamic_options/schema.rb +7 -9
- data/lib/servactory/utils.rb +0 -8
- data/lib/servactory/version.rb +3 -3
- data/lib/servactory.rb +4 -0
- metadata +19 -22
- data/lib/servactory/maintenance/attributes/options/registrar.rb +0 -183
- data/lib/servactory/maintenance/attributes/tools/validation.rb +0 -84
- data/lib/servactory/maintenance/attributes/validations/concerns/error_builder.rb +0 -52
- data/lib/servactory/maintenance/validations/types.rb +0 -34
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Maintenance
|
|
5
|
-
module
|
|
6
|
-
module
|
|
5
|
+
module Validations
|
|
6
|
+
module Checkers
|
|
7
7
|
# Validates that attribute values match their declared types.
|
|
8
8
|
#
|
|
9
9
|
# ## Purpose
|
|
@@ -35,13 +35,14 @@ module Servactory
|
|
|
35
35
|
# @param attribute [Inputs::Input, Internals::Internal, Outputs::Output] Attribute to validate
|
|
36
36
|
# @param value [Object] Value to check against declared types
|
|
37
37
|
# @param check_key [Symbol] Must be :types to trigger validation
|
|
38
|
+
# @param check_options [Hash, nil] Unused, accepted for uniform checker interface
|
|
38
39
|
# @return [String, nil] Error message on type mismatch, nil on success
|
|
39
|
-
def self.check(context:, attribute:, value:, check_key:,
|
|
40
|
+
def self.check(context:, attribute:, value:, check_key:, check_options: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
40
41
|
return unless should_be_checked_for?(attribute, value, check_key)
|
|
41
42
|
|
|
42
43
|
prepared_value = compute_prepared_value(attribute, value)
|
|
43
44
|
|
|
44
|
-
error_data = Servactory::Maintenance::Validations::
|
|
45
|
+
error_data = Servactory::Maintenance::Validations::Support::TypeValidator.validate(
|
|
45
46
|
context:,
|
|
46
47
|
attribute:,
|
|
47
48
|
types: attribute.types,
|
|
@@ -99,7 +100,7 @@ module Servactory
|
|
|
99
100
|
|
|
100
101
|
# Builds error message from validation error data.
|
|
101
102
|
#
|
|
102
|
-
# @param error_data [Hash] Error data from
|
|
103
|
+
# @param error_data [Hash] Error data from TypeValidator.validate
|
|
103
104
|
# @return [String] Processed error message
|
|
104
105
|
def self.build_error_message(error_data)
|
|
105
106
|
process_message(error_data[:message], **error_data)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Maintenance
|
|
5
|
+
module Validations
|
|
6
|
+
module Concerns
|
|
7
|
+
# Concern providing error message processing for validators.
|
|
8
|
+
#
|
|
9
|
+
# ## Purpose
|
|
10
|
+
#
|
|
11
|
+
# ErrorBuilder provides shared logic for processing error messages that
|
|
12
|
+
# can be either static strings or dynamic Procs. This allows validators
|
|
13
|
+
# to support both simple error messages and context-aware messages.
|
|
14
|
+
#
|
|
15
|
+
# ## Usage
|
|
16
|
+
#
|
|
17
|
+
# Extend in validator classes:
|
|
18
|
+
#
|
|
19
|
+
# ```ruby
|
|
20
|
+
# class MyValidator
|
|
21
|
+
# extend Concerns::ErrorBuilder
|
|
22
|
+
#
|
|
23
|
+
# def self.build_error(...)
|
|
24
|
+
# process_message(message, **context)
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# ```
|
|
28
|
+
#
|
|
29
|
+
# ## Methods Provided
|
|
30
|
+
#
|
|
31
|
+
# - `process_message` - converts String or Proc message to String
|
|
32
|
+
module ErrorBuilder
|
|
33
|
+
# Processes a message that may be a String or Proc.
|
|
34
|
+
#
|
|
35
|
+
# If message is a Proc, calls it with the provided attributes.
|
|
36
|
+
# If message is a String, returns it unchanged.
|
|
37
|
+
#
|
|
38
|
+
# @param message [String, Proc] The message to process
|
|
39
|
+
# @param attributes [Hash] Attributes to pass to Proc if message is callable
|
|
40
|
+
# @return [String] The processed message string
|
|
41
|
+
def process_message(message, **attributes)
|
|
42
|
+
return message unless message.is_a?(Proc)
|
|
43
|
+
|
|
44
|
+
message.call(**attributes)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Maintenance
|
|
5
|
+
module Validations
|
|
6
|
+
module Performer
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
def validate!(context:, attribute:, value:)
|
|
10
|
+
first_error = process(context:, attribute:, value:)
|
|
11
|
+
return if first_error.nil?
|
|
12
|
+
|
|
13
|
+
context.public_send(
|
|
14
|
+
:"fail_#{attribute.system_name}!",
|
|
15
|
+
attribute.name,
|
|
16
|
+
message: first_error
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def process(context:, attribute:, value:) # rubocop:disable Metrics/MethodLength
|
|
23
|
+
attribute.options_for_checks.each do |check_key, check_options|
|
|
24
|
+
error = process_option(
|
|
25
|
+
context:,
|
|
26
|
+
attribute:,
|
|
27
|
+
value:,
|
|
28
|
+
check_key:,
|
|
29
|
+
check_options:
|
|
30
|
+
)
|
|
31
|
+
return error if error.present?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def process_option(context:, attribute:, value:, check_key:, check_options:) # rubocop:disable Metrics/MethodLength
|
|
38
|
+
validation_classes = attribute.collection_of_options.validation_classes
|
|
39
|
+
return if validation_classes.empty?
|
|
40
|
+
|
|
41
|
+
validation_classes.each do |validation_class|
|
|
42
|
+
error_message = validation_class.check(
|
|
43
|
+
context:,
|
|
44
|
+
attribute:,
|
|
45
|
+
value:,
|
|
46
|
+
check_key:,
|
|
47
|
+
check_options:
|
|
48
|
+
)
|
|
49
|
+
return error_message if error_message.present?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Servactory
|
|
4
|
+
module Maintenance
|
|
5
|
+
module Validations
|
|
6
|
+
module Support
|
|
7
|
+
class TypeValidator
|
|
8
|
+
# Validates value against expected types.
|
|
9
|
+
#
|
|
10
|
+
# Returns nil on success, error data Hash on failure.
|
|
11
|
+
#
|
|
12
|
+
# @param context [Object] Service context for error info
|
|
13
|
+
# @param attribute [Inputs::Input, Internals::Internal, Outputs::Output] Attribute being validated
|
|
14
|
+
# @param types [Array<Class, String, Symbol>] Expected type classes
|
|
15
|
+
# @param value [Object] Value to validate
|
|
16
|
+
# @return [Hash, nil] nil on success, error data Hash on failure
|
|
17
|
+
def self.validate(context:, attribute:, types:, value:) # rubocop:disable Metrics/MethodLength
|
|
18
|
+
prepared_types = types.map { |type| Servactory::Utils.constantize_class(type) }
|
|
19
|
+
|
|
20
|
+
return if prepared_types.any? { |type| value.is_a?(type) }
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
message: Servactory::Maintenance::Validations::Translator::Type.default_message,
|
|
24
|
+
service: context.send(:servactory_service_info),
|
|
25
|
+
attribute:,
|
|
26
|
+
value:,
|
|
27
|
+
key_name: nil,
|
|
28
|
+
expected_type: prepared_types.join(", "),
|
|
29
|
+
given_type: value.class.name
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -2,30 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Outputs
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def initialize(collection = Set.new)
|
|
11
|
-
@collection = collection
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def only(*names)
|
|
15
|
-
Collection.new(filter { |internal| names.include?(internal.name) })
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def except(*names)
|
|
19
|
-
Collection.new(filter { |internal| names.exclude?(internal.name) })
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def names
|
|
23
|
-
map(&:name)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def find_by(name:)
|
|
27
|
-
find { |output| output.name == name }
|
|
28
|
-
end
|
|
5
|
+
# Specialized collection for Output attributes.
|
|
6
|
+
#
|
|
7
|
+
# Inherits all behavior from the base Attributes::Collection
|
|
8
|
+
# without any overrides.
|
|
9
|
+
class Collection < Servactory::Maintenance::Attributes::Collection
|
|
29
10
|
end
|
|
30
11
|
end
|
|
31
12
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Servactory
|
|
4
4
|
module Outputs
|
|
5
|
-
class Output
|
|
5
|
+
class Output < Servactory::Maintenance::Attributes::Base
|
|
6
6
|
class Actor
|
|
7
7
|
attr_reader :name,
|
|
8
8
|
:types,
|
|
@@ -26,138 +26,30 @@ module Servactory
|
|
|
26
26
|
# The methods below are required to support the internal work.
|
|
27
27
|
|
|
28
28
|
def input?
|
|
29
|
-
|
|
29
|
+
@attribute.input?
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def internal?
|
|
33
|
-
|
|
33
|
+
@attribute.internal?
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def output?
|
|
37
|
-
|
|
37
|
+
@attribute.output?
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
attr_reader :name,
|
|
42
|
-
:collection_of_options,
|
|
43
|
-
:options
|
|
44
|
-
|
|
45
|
-
def initialize(
|
|
46
|
-
name,
|
|
47
|
-
*helpers,
|
|
48
|
-
option_helpers:,
|
|
49
|
-
**options
|
|
50
|
-
)
|
|
51
|
-
@name = name
|
|
52
|
-
@option_helpers = option_helpers
|
|
53
|
-
|
|
54
|
-
register_options(helpers:, options:)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def method_missing(name, *args, &block)
|
|
58
|
-
option = @collection_of_options.find_by(name:)
|
|
59
|
-
return super if option.nil?
|
|
60
|
-
|
|
61
|
-
option.body
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def respond_to_missing?(name, *)
|
|
65
|
-
@collection_of_options.names.include?(name) || super
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def register_options(helpers:, options:)
|
|
69
|
-
merged_options = augment_options_with_helpers(helpers:, options:)
|
|
70
|
-
options_registrar = create_options_registrar(options: merged_options)
|
|
71
|
-
|
|
72
|
-
@options = merged_options
|
|
73
|
-
@collection_of_options = options_registrar.collection
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def options_for_checks
|
|
77
|
-
@collection_of_options.options_for_checks
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def system_name
|
|
81
|
-
self.class.name.demodulize.downcase.to_sym
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def i18n_name
|
|
85
|
-
system_name.to_s.pluralize
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def input?
|
|
89
|
-
false
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def internal?
|
|
93
|
-
false
|
|
94
|
-
end
|
|
95
|
-
|
|
96
41
|
def output?
|
|
97
42
|
true
|
|
98
43
|
end
|
|
99
44
|
|
|
100
45
|
private
|
|
101
46
|
|
|
102
|
-
def create_options_registrar(options:)
|
|
103
|
-
Servactory::Maintenance::Attributes::Options::Registrar.register(
|
|
104
|
-
attribute: self,
|
|
105
|
-
options:,
|
|
106
|
-
features: available_feature_options
|
|
107
|
-
)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
47
|
def available_feature_options
|
|
111
48
|
{
|
|
112
49
|
types: true,
|
|
113
50
|
must: true
|
|
114
51
|
}
|
|
115
52
|
end
|
|
116
|
-
|
|
117
|
-
def augment_options_with_helpers(helpers:, options:)
|
|
118
|
-
result_options = options.dup
|
|
119
|
-
merge_standard_helpers_into(target_options: result_options, helpers:) if helpers.present?
|
|
120
|
-
merge_advanced_helpers_into(target_options: result_options, source_options: options)
|
|
121
|
-
result_options
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def merge_standard_helpers_into(target_options:, helpers:)
|
|
125
|
-
standard_helpers_result = transform_helpers_to_options(helpers:)
|
|
126
|
-
target_options.deep_merge!(standard_helpers_result)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def merge_advanced_helpers_into(target_options:, source_options:)
|
|
130
|
-
advanced_helpers = filter_advanced_helpers(options: source_options)
|
|
131
|
-
return if advanced_helpers.blank?
|
|
132
|
-
|
|
133
|
-
advanced_helpers_result = transform_helpers_to_options(helpers: advanced_helpers)
|
|
134
|
-
target_options.deep_merge!(advanced_helpers_result)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def filter_advanced_helpers(options:)
|
|
138
|
-
reserved_options = Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS
|
|
139
|
-
options.except(*reserved_options)
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def transform_helpers_to_options(helpers:)
|
|
143
|
-
helpers.each_with_object({}) do |(helper_name, values), result|
|
|
144
|
-
helper = @option_helpers.find_by(name: helper_name)
|
|
145
|
-
next if helper.blank?
|
|
146
|
-
|
|
147
|
-
transformed_option = transform_helper_to_option(helper:, values:)
|
|
148
|
-
result.deep_merge!(transformed_option) if transformed_option.present?
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def transform_helper_to_option(helper:, values:)
|
|
153
|
-
return helper.equivalent unless helper.equivalent.is_a?(Proc)
|
|
154
|
-
|
|
155
|
-
if values.is_a?(Hash)
|
|
156
|
-
helper.equivalent.call(**values)
|
|
157
|
-
else
|
|
158
|
-
helper.equivalent.call(values)
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
53
|
end
|
|
162
54
|
end
|
|
163
55
|
end
|
data/lib/servactory/result.rb
CHANGED
|
@@ -41,10 +41,10 @@ module Servactory
|
|
|
41
41
|
# ```ruby
|
|
42
42
|
# result.active? # Equivalent to Utils.query_attribute(result.active)
|
|
43
43
|
# ```
|
|
44
|
-
class Result
|
|
44
|
+
class Result # rubocop:disable Metrics/ClassLength
|
|
45
45
|
# Internal container for service output values.
|
|
46
46
|
#
|
|
47
|
-
# Provides dynamic method access to output values
|
|
47
|
+
# Provides dynamic method access to output values.
|
|
48
48
|
# Stores outputs in hash and supports predicate methods when enabled.
|
|
49
49
|
class Outputs
|
|
50
50
|
# Creates an Outputs container with given output values.
|
|
@@ -55,6 +55,8 @@ module Servactory
|
|
|
55
55
|
def initialize(outputs:, predicate_methods_enabled:)
|
|
56
56
|
@outputs = outputs
|
|
57
57
|
@predicate_methods_enabled = predicate_methods_enabled
|
|
58
|
+
|
|
59
|
+
define_attribute_methods!
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
# Returns string representation for debugging.
|
|
@@ -64,45 +66,26 @@ module Servactory
|
|
|
64
66
|
"#<#{self.class.name} #{draw_result}>"
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Defines singleton accessor methods for output values on this instance.
|
|
68
72
|
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
73
|
+
# Creates getter methods for each output key and, when predicate methods
|
|
74
|
+
# are enabled, predicate methods (e.g., user?) for boolean queries.
|
|
71
75
|
#
|
|
72
|
-
# @
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if name.to_s.end_with?("?")
|
|
77
|
-
base_name = name.to_s.chomp("?").to_sym
|
|
78
|
-
if @predicate_methods_enabled && @outputs.key?(base_name)
|
|
79
|
-
return Servactory::Utils.query_attribute(@outputs[base_name])
|
|
80
|
-
end
|
|
81
|
-
elsif @outputs.key?(name)
|
|
82
|
-
return @outputs[name]
|
|
83
|
-
end
|
|
76
|
+
# @return [void]
|
|
77
|
+
def define_attribute_methods!
|
|
78
|
+
@outputs.each_key do |name|
|
|
79
|
+
define_singleton_method(name) { @outputs[name] }
|
|
84
80
|
|
|
85
|
-
|
|
86
|
-
end
|
|
81
|
+
next unless @predicate_methods_enabled
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# @param include_private [Boolean] Include private methods in check
|
|
92
|
-
# @return [Boolean] True if output exists for this method
|
|
93
|
-
def respond_to_missing?(name, include_private = false)
|
|
94
|
-
if name.to_s.end_with?("?")
|
|
95
|
-
base_name = name.to_s.chomp("?").to_sym
|
|
96
|
-
return true if @predicate_methods_enabled && @outputs.key?(base_name)
|
|
97
|
-
elsif @outputs.key?(name)
|
|
98
|
-
return true
|
|
83
|
+
define_singleton_method(:"#{name}?") do
|
|
84
|
+
Servactory::Utils.query_attribute(@outputs[name])
|
|
85
|
+
end
|
|
99
86
|
end
|
|
100
|
-
|
|
101
|
-
super
|
|
102
87
|
end
|
|
103
88
|
|
|
104
|
-
private
|
|
105
|
-
|
|
106
89
|
# Returns array of output attribute names.
|
|
107
90
|
#
|
|
108
91
|
# @return [Array<Symbol>] Output names without predicates
|
|
@@ -297,15 +280,16 @@ module Servactory
|
|
|
297
280
|
self
|
|
298
281
|
end
|
|
299
282
|
|
|
300
|
-
# Delegates method calls to outputs container.
|
|
283
|
+
# Delegates method calls to the outputs container.
|
|
301
284
|
#
|
|
302
|
-
#
|
|
303
|
-
#
|
|
285
|
+
# On first access, triggers lazy initialization of outputs and
|
|
286
|
+
# installs singleton delegator methods for subsequent direct access.
|
|
304
287
|
#
|
|
305
288
|
# @param name [Symbol] Method name (output attribute)
|
|
306
289
|
# @param args [Array] Method arguments
|
|
307
290
|
# @param block [Proc] Optional block
|
|
308
|
-
# @return [Object] Output value
|
|
291
|
+
# @return [Object] Output value when name matches an output attribute
|
|
292
|
+
# @raise [NoMethodError] When method is truly undefined and no context present
|
|
309
293
|
def method_missing(name, *args, &block)
|
|
310
294
|
return outputs.public_send(name, *args, &block) if outputs.respond_to?(name)
|
|
311
295
|
|
|
@@ -348,9 +332,19 @@ module Servactory
|
|
|
348
332
|
|
|
349
333
|
# Returns outputs container, lazily initialized.
|
|
350
334
|
#
|
|
335
|
+
# On first access, also defines singleton delegator methods on this
|
|
336
|
+
# Result instance for subsequent direct access.
|
|
337
|
+
#
|
|
351
338
|
# @return [Outputs] Outputs container with service values
|
|
352
339
|
def outputs
|
|
353
|
-
@outputs ||=
|
|
340
|
+
@outputs ||= build_outputs.tap { |outputs_container| define_output_delegators!(outputs_container) }
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Builds Outputs container with predicate configuration.
|
|
344
|
+
#
|
|
345
|
+
# @return [Outputs] New outputs container
|
|
346
|
+
def build_outputs
|
|
347
|
+
Outputs.new(
|
|
354
348
|
outputs: build_outputs_hash,
|
|
355
349
|
predicate_methods_enabled:
|
|
356
350
|
@context.is_a?(Servactory::TestKit::Result) || @context.config.predicate_methods_enabled
|
|
@@ -368,6 +362,21 @@ module Servactory
|
|
|
368
362
|
hash
|
|
369
363
|
end
|
|
370
364
|
|
|
365
|
+
# Defines singleton delegator methods on this Result instance
|
|
366
|
+
# for direct access to output values without method_missing.
|
|
367
|
+
#
|
|
368
|
+
# @param outputs_container [Outputs] Outputs container to delegate to
|
|
369
|
+
# @return [void]
|
|
370
|
+
def define_output_delegators!(outputs_container)
|
|
371
|
+
outputs_container.send(:output_names).each do |name|
|
|
372
|
+
define_singleton_method(name) { outputs.public_send(name) }
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
outputs_container.send(:predicate_names).each do |name|
|
|
376
|
+
define_singleton_method(name) { outputs.public_send(name) }
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
371
380
|
########################################################################
|
|
372
381
|
|
|
373
382
|
# Wraps NoMethodError with contextual failure.
|
|
@@ -88,8 +88,7 @@ module Servactory
|
|
|
88
88
|
# Creates a ConsistsOf validator instance.
|
|
89
89
|
#
|
|
90
90
|
# @param option_name [Symbol] The option name (default: :consists_of)
|
|
91
|
-
# @param collection_mode_class_names [
|
|
92
|
-
# Valid collection types
|
|
91
|
+
# @param collection_mode_class_names [Array<Class>] Valid collection types
|
|
93
92
|
# @return [Servactory::Maintenance::Attributes::OptionHelper]
|
|
94
93
|
def self.use(option_name = :consists_of, collection_mode_class_names:)
|
|
95
94
|
instance = new(option_name, :type, false)
|
|
@@ -99,8 +98,7 @@ module Servactory
|
|
|
99
98
|
|
|
100
99
|
# Assigns the list of valid collection class names.
|
|
101
100
|
#
|
|
102
|
-
# @param collection_mode_class_names [
|
|
103
|
-
# Collection types to accept
|
|
101
|
+
# @param collection_mode_class_names [Array<Class>] Collection types to accept
|
|
104
102
|
# @return [void]
|
|
105
103
|
def assign(collection_mode_class_names)
|
|
106
104
|
@collection_mode_class_names = collection_mode_class_names
|
|
@@ -155,9 +155,9 @@ module Servactory
|
|
|
155
155
|
# Creates an OptionHelper for registration with Servactory.
|
|
156
156
|
#
|
|
157
157
|
# @param name [Symbol] Internal validation name
|
|
158
|
-
# @return [Servactory::Maintenance::
|
|
158
|
+
# @return [Servactory::Maintenance::Options::Helper]
|
|
159
159
|
def must(name)
|
|
160
|
-
Servactory::Maintenance::
|
|
160
|
+
Servactory::Maintenance::Options::Helper.new(
|
|
161
161
|
name: @option_name,
|
|
162
162
|
equivalent: equivalent_with(name),
|
|
163
163
|
meta: {
|
|
@@ -127,8 +127,7 @@ module Servactory
|
|
|
127
127
|
# Creates a Schema validator instance.
|
|
128
128
|
#
|
|
129
129
|
# @param option_name [Symbol] The option name (default: :schema)
|
|
130
|
-
# @param default_hash_mode_class_names [
|
|
131
|
-
# Valid Hash-like types
|
|
130
|
+
# @param default_hash_mode_class_names [Array<Class>] Valid Hash-like types
|
|
132
131
|
# @return [Servactory::Maintenance::Attributes::OptionHelper]
|
|
133
132
|
def self.use(option_name = :schema, default_hash_mode_class_names:)
|
|
134
133
|
instance = new(option_name, :is, false)
|
|
@@ -138,8 +137,7 @@ module Servactory
|
|
|
138
137
|
|
|
139
138
|
# Assigns the list of valid Hash-compatible class names.
|
|
140
139
|
#
|
|
141
|
-
# @param default_hash_mode_class_names [
|
|
142
|
-
# Hash-like types to accept
|
|
140
|
+
# @param default_hash_mode_class_names [Array<Class>] Hash-like types to accept
|
|
143
141
|
# @return [void]
|
|
144
142
|
def assign(default_hash_mode_class_names)
|
|
145
143
|
@default_hash_mode_class_names = default_hash_mode_class_names
|
|
@@ -279,7 +277,7 @@ module Servactory
|
|
|
279
277
|
return true
|
|
280
278
|
end
|
|
281
279
|
|
|
282
|
-
value = object
|
|
280
|
+
value = object[schema_key]
|
|
283
281
|
prepared_value = prepare_value_from(schema_value:, value:, required: attribute_required)
|
|
284
282
|
|
|
285
283
|
[
|
|
@@ -299,7 +297,7 @@ module Servactory
|
|
|
299
297
|
required || (
|
|
300
298
|
!required && !fetch_default_from(schema_value).nil?
|
|
301
299
|
) || (
|
|
302
|
-
!required && !object
|
|
300
|
+
!required && !object[schema_key].nil?
|
|
303
301
|
)
|
|
304
302
|
end
|
|
305
303
|
|
|
@@ -322,7 +320,7 @@ module Servactory
|
|
|
322
320
|
# @param value [Hash] Schema definition
|
|
323
321
|
# @return [Object, nil] Default value or nil
|
|
324
322
|
def fetch_default_from(value)
|
|
325
|
-
value
|
|
323
|
+
value[:default]
|
|
326
324
|
end
|
|
327
325
|
|
|
328
326
|
########################################################################
|
|
@@ -354,14 +352,14 @@ module Servactory
|
|
|
354
352
|
)
|
|
355
353
|
else
|
|
356
354
|
# Apply scalar defaults.
|
|
357
|
-
default_value = schema_value
|
|
355
|
+
default_value = schema_value[:default]
|
|
358
356
|
|
|
359
357
|
if !required && !default_value.nil? && !Servactory::Utils.value_present?(object_value)
|
|
360
358
|
object[schema_key] = default_value
|
|
361
359
|
end
|
|
362
360
|
|
|
363
361
|
# Execute prepare callback if defined.
|
|
364
|
-
unless (input_prepare = schema_value
|
|
362
|
+
unless (input_prepare = schema_value[:prepare]).nil?
|
|
365
363
|
object[schema_key] = input_prepare.call(value: object[schema_key])
|
|
366
364
|
end
|
|
367
365
|
|
data/lib/servactory/utils.rb
CHANGED
|
@@ -12,14 +12,6 @@ module Servactory
|
|
|
12
12
|
data.symbolize_keys
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def fetch_hash_with_desired_attribute(attribute)
|
|
16
|
-
return { input: attribute.class::Actor.new(attribute) } if really_input?(attribute)
|
|
17
|
-
return { internal: attribute.class::Actor.new(attribute) } if really_internal?(attribute)
|
|
18
|
-
return { output: attribute.class::Actor.new(attribute) } if really_output?(attribute)
|
|
19
|
-
|
|
20
|
-
raise ArgumentError, "Failed to define attribute"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
15
|
def define_attribute_with(input: nil, internal: nil, output: nil)
|
|
24
16
|
return input if really_input?(input)
|
|
25
17
|
return internal if really_internal?(internal)
|
data/lib/servactory/version.rb
CHANGED
data/lib/servactory.rb
CHANGED
|
@@ -16,6 +16,10 @@ loader.inflector.inflect(
|
|
|
16
16
|
)
|
|
17
17
|
loader.setup
|
|
18
18
|
|
|
19
|
+
# Eager load DSL to initialize Stroma::Registry.
|
|
20
|
+
# Registry must be populated before any service class is defined.
|
|
21
|
+
require_relative "servactory/dsl"
|
|
22
|
+
|
|
19
23
|
module Servactory; end
|
|
20
24
|
|
|
21
25
|
require "servactory/engine" if defined?(Rails::Engine)
|