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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/servactory/actions/action.rb +2 -2
  3. data/lib/servactory/actions/collection.rb +1 -1
  4. data/lib/servactory/actions/stages/collection.rb +1 -1
  5. data/lib/servactory/configuration/config.rb +0 -16
  6. data/lib/servactory/configuration/configurable.rb +3 -6
  7. data/lib/servactory/configuration/hash_mode/class_names_collection.rb +1 -4
  8. data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +57 -8
  9. data/lib/servactory/context/warehouse/inputs.rb +28 -15
  10. data/lib/servactory/context/workspace/inputs.rb +27 -18
  11. data/lib/servactory/context/workspace/internals.rb +44 -27
  12. data/lib/servactory/context/workspace/outputs.rb +31 -23
  13. data/lib/servactory/dsl.rb +1 -1
  14. data/lib/servactory/info/builder.rb +2 -3
  15. data/lib/servactory/inputs/collection.rb +14 -21
  16. data/lib/servactory/inputs/input.rb +6 -103
  17. data/lib/servactory/inputs/tools/validation.rb +32 -57
  18. data/lib/servactory/inputs/validations/required.rb +3 -2
  19. data/lib/servactory/internals/collection.rb +5 -24
  20. data/lib/servactory/internals/internal.rb +4 -112
  21. data/lib/servactory/maintenance/attributes/base.rb +135 -0
  22. data/lib/servactory/maintenance/attributes/collection.rb +109 -0
  23. data/lib/servactory/maintenance/attributes/option_helper.rb +33 -12
  24. data/lib/servactory/maintenance/{attributes/options_collection.rb → options/collection.rb} +6 -7
  25. data/lib/servactory/maintenance/{attributes → options}/define_conflict.rb +1 -1
  26. data/lib/servactory/maintenance/{attributes → options}/define_method.rb +1 -1
  27. data/lib/servactory/maintenance/options/helper.rb +23 -0
  28. data/lib/servactory/maintenance/{attributes → options}/option.rb +50 -27
  29. data/lib/servactory/maintenance/options/registrar.rb +200 -0
  30. data/lib/servactory/maintenance/{attributes/validations → validations/checkers}/must.rb +25 -8
  31. data/lib/servactory/maintenance/{attributes/validations → validations/checkers}/type.rb +6 -5
  32. data/lib/servactory/maintenance/validations/concerns/error_builder.rb +50 -0
  33. data/lib/servactory/maintenance/validations/performer.rb +57 -0
  34. data/lib/servactory/maintenance/validations/support/type_validator.rb +36 -0
  35. data/lib/servactory/maintenance/{attributes → validations}/translator/must.rb +1 -1
  36. data/lib/servactory/maintenance/{attributes → validations}/translator/type.rb +1 -1
  37. data/lib/servactory/outputs/collection.rb +5 -24
  38. data/lib/servactory/outputs/output.rb +4 -112
  39. data/lib/servactory/result.rb +48 -39
  40. data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +2 -4
  41. data/lib/servactory/tool_kit/dynamic_options/must.rb +2 -2
  42. data/lib/servactory/tool_kit/dynamic_options/schema.rb +7 -9
  43. data/lib/servactory/utils.rb +0 -8
  44. data/lib/servactory/version.rb +3 -3
  45. data/lib/servactory.rb +4 -0
  46. metadata +19 -22
  47. data/lib/servactory/maintenance/attributes/options/registrar.rb +0 -183
  48. data/lib/servactory/maintenance/attributes/tools/validation.rb +0 -84
  49. data/lib/servactory/maintenance/attributes/validations/concerns/error_builder.rb +0 -52
  50. data/lib/servactory/maintenance/validations/types.rb +0 -34
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Servactory
4
4
  module Inputs
5
- class Input # rubocop:disable Metrics/ClassLength
5
+ class Input < Servactory::Maintenance::Attributes::Base
6
6
  class Actor
7
7
  attr_reader :name,
8
8
  :internal_name,
@@ -38,22 +38,19 @@ module Servactory
38
38
  # The methods below are required to support the internal work.
39
39
 
40
40
  def input?
41
- true
41
+ @attribute.input?
42
42
  end
43
43
 
44
44
  def internal?
45
- false
45
+ @attribute.internal?
46
46
  end
47
47
 
48
48
  def output?
49
- false
49
+ @attribute.output?
50
50
  end
51
51
  end
52
52
 
53
- attr_reader :name,
54
- :internal_name,
55
- :collection_of_options,
56
- :options
53
+ attr_reader :internal_name
57
54
 
58
55
  # rubocop:disable Style/KeywordParametersOrder
59
56
  def initialize(
@@ -63,49 +60,16 @@ module Servactory
63
60
  option_helpers:,
64
61
  **options
65
62
  )
66
- @name = name
67
63
  @internal_name = as.presence || name
68
- @option_helpers = option_helpers
69
64
 
70
- register_options(helpers:, options:)
65
+ super(name, *helpers, option_helpers:, **options)
71
66
  end
72
67
  # rubocop:enable Style/KeywordParametersOrder
73
68
 
74
- def method_missing(name, *args, &block)
75
- option = @collection_of_options.find_by(name:)
76
- return super if option.nil?
77
-
78
- option.body
79
- end
80
-
81
- def respond_to_missing?(name, *)
82
- @collection_of_options.names.include?(name) || super
83
- end
84
-
85
- def register_options(helpers:, options:)
86
- merged_options = augment_options_with_helpers(helpers:, options:)
87
- options_registrar = create_options_registrar(options: merged_options)
88
-
89
- @options = merged_options
90
- @collection_of_options = options_registrar.collection
91
- end
92
-
93
- def options_for_checks
94
- @collection_of_options.options_for_checks
95
- end
96
-
97
69
  def conflict_code
98
70
  @collection_of_options.defined_conflict_code
99
71
  end
100
72
 
101
- def system_name
102
- self.class.name.demodulize.downcase.to_sym
103
- end
104
-
105
- def i18n_name
106
- system_name.to_s.pluralize
107
- end
108
-
109
73
  def with_conflicts?
110
74
  conflict_code.present?
111
75
  end
@@ -114,24 +78,8 @@ module Servactory
114
78
  true
115
79
  end
116
80
 
117
- def internal?
118
- false
119
- end
120
-
121
- def output?
122
- false
123
- end
124
-
125
81
  private
126
82
 
127
- def create_options_registrar(options:)
128
- Servactory::Maintenance::Attributes::Options::Registrar.register(
129
- attribute: self,
130
- options:,
131
- features: available_feature_options
132
- )
133
- end
134
-
135
83
  def available_feature_options
136
84
  {
137
85
  required: true,
@@ -141,51 +89,6 @@ module Servactory
141
89
  prepare: true
142
90
  }
143
91
  end
144
-
145
- def augment_options_with_helpers(helpers:, options:)
146
- result_options = options.dup
147
- merge_standard_helpers_into(target_options: result_options, helpers:) if helpers.present?
148
- merge_advanced_helpers_into(target_options: result_options, source_options: options)
149
- result_options
150
- end
151
-
152
- def merge_standard_helpers_into(target_options:, helpers:)
153
- standard_helpers_result = transform_helpers_to_options(helpers:)
154
- target_options.deep_merge!(standard_helpers_result)
155
- end
156
-
157
- def merge_advanced_helpers_into(target_options:, source_options:)
158
- advanced_helpers = filter_advanced_helpers(options: source_options)
159
- return if advanced_helpers.blank?
160
-
161
- advanced_helpers_result = transform_helpers_to_options(helpers: advanced_helpers)
162
- target_options.deep_merge!(advanced_helpers_result)
163
- end
164
-
165
- def filter_advanced_helpers(options:)
166
- reserved_options = Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS
167
- options.except(*reserved_options)
168
- end
169
-
170
- def transform_helpers_to_options(helpers:)
171
- helpers.each_with_object({}) do |(helper_name, values), result|
172
- helper = @option_helpers.find_by(name: helper_name)
173
- next if helper.blank?
174
-
175
- transformed_option = transform_helper_to_option(helper:, values:)
176
- result.deep_merge!(transformed_option) if transformed_option.present?
177
- end
178
- end
179
-
180
- def transform_helper_to_option(helper:, values:)
181
- return helper.equivalent unless helper.equivalent.is_a?(Proc)
182
-
183
- if values.is_a?(Hash)
184
- helper.equivalent.call(**values)
185
- else
186
- helper.equivalent.call(values)
187
- end
188
- end
189
92
  end
190
93
  end
191
94
  end
@@ -3,81 +3,56 @@
3
3
  module Servactory
4
4
  module Inputs
5
5
  module Tools
6
- class Validation
7
- def self.validate!(...)
8
- new(...).validate!
9
- end
10
-
11
- def initialize(context, collection_of_inputs)
12
- @context = context
13
- @collection_of_inputs = collection_of_inputs
14
- @first_error = nil
15
- end
16
-
17
- def validate!
18
- @collection_of_inputs.each do |input|
19
- process_input(input)
20
- break if @first_error.present?
6
+ module Validation
7
+ extend self
8
+
9
+ def validate!(context, collection_of_inputs) # rubocop:disable Metrics/MethodLength
10
+ warehouse = context.send(:servactory_service_warehouse)
11
+ first_error = nil
12
+ failed_input = nil
13
+
14
+ collection_of_inputs.each do |input|
15
+ first_error = process_input(context, warehouse, input)
16
+ if first_error.present?
17
+ failed_input = input
18
+ break
19
+ end
21
20
  end
22
21
 
23
- raise_errors
22
+ return if first_error.nil?
23
+
24
+ context.fail_input!(failed_input.name, message: first_error)
24
25
  end
25
26
 
26
27
  private
27
28
 
28
- def process_input(input)
29
+ def process_input(context, warehouse, input)
30
+ value = warehouse.fetch_input(input.name)
31
+
29
32
  input.options_for_checks.each do |check_key, check_options|
30
- process_option(check_key, check_options, input:)
31
- break if @first_error.present?
33
+ error = process_option(context, input, value, check_key, check_options)
34
+ return error if error.present?
32
35
  end
36
+
37
+ nil
33
38
  end
34
39
 
35
- def process_option(check_key, check_options, input:) # rubocop:disable Metrics/MethodLength
36
- validation_classes = validation_classes_from(input)
40
+ def process_option(context, input, value, check_key, check_options) # rubocop:disable Metrics/MethodLength
41
+ validation_classes = input.collection_of_options.validation_classes
37
42
  return if validation_classes.empty?
38
43
 
39
44
  validation_classes.each do |validation_class|
40
- error_message = process_validation_class(
41
- validation_class:,
42
- input:,
45
+ error_message = validation_class.check(
46
+ context:,
47
+ attribute: input,
48
+ value:,
43
49
  check_key:,
44
50
  check_options:
45
51
  )
46
-
47
- next if error_message.blank?
48
-
49
- @first_error = error_message
50
- break
52
+ return error_message if error_message.present?
51
53
  end
52
- end
53
-
54
- def process_validation_class(
55
- validation_class:,
56
- input:,
57
- check_key:,
58
- check_options:
59
- )
60
- validation_class.check(
61
- context: @context,
62
- attribute: input,
63
- value: @context.send(:servactory_service_warehouse).fetch_input(input.name),
64
- check_key:,
65
- check_options:
66
- )
67
- end
68
-
69
- ########################################################################
70
-
71
- def validation_classes_from(input)
72
- @validation_classes_cache ||= input.collection_of_options.validation_classes # rubocop:disable Naming/MemoizedInstanceVariableName
73
- end
74
-
75
- ########################################################################
76
-
77
- def raise_errors
78
- return if @first_error.nil?
79
54
 
80
- @context.fail_input!(nil, message: @first_error)
55
+ nil
81
56
  end
82
57
  end
83
58
  end
@@ -22,7 +22,7 @@ module Servactory
22
22
  # end
23
23
  # ```
24
24
  class Required
25
- extend Servactory::Maintenance::Attributes::Validations::Concerns::ErrorBuilder
25
+ extend Servactory::Maintenance::Validations::Concerns::ErrorBuilder
26
26
 
27
27
  # Validates that a required input has a present value.
28
28
  #
@@ -30,8 +30,9 @@ module Servactory
30
30
  # @param attribute [Inputs::Input] Input attribute to validate
31
31
  # @param value [Object] Value to check for presence
32
32
  # @param check_key [Symbol] Must be :required to trigger validation
33
+ # @param check_options [Hash, nil] Unused, accepted for uniform checker interface
33
34
  # @return [String, nil] Error message on failure, nil on success
34
- def self.check(context:, attribute:, value:, check_key:, **)
35
+ def self.check(context:, attribute:, value:, check_key:, check_options: nil) # rubocop:disable Lint/UnusedMethodArgument
35
36
  return unless should_be_checked_for?(attribute, check_key)
36
37
  return if Servactory::Utils.value_present?(value)
37
38
 
@@ -2,30 +2,11 @@
2
2
 
3
3
  module Servactory
4
4
  module Internals
5
- class Collection
6
- extend Forwardable
7
-
8
- def_delegators :@collection, :<<, :filter, :each, :map, :to_h, :merge, :find
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 { |internal| internal.name == name }
28
- end
5
+ # Specialized collection for Internal 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 Internals
5
- class Internal
5
+ class Internal < 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
- false
29
+ @attribute.input?
30
30
  end
31
31
 
32
32
  def internal?
33
- true
33
+ @attribute.internal?
34
34
  end
35
35
 
36
36
  def output?
37
- false
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
41
  def internal?
93
42
  true
94
43
  end
95
44
 
96
- def output?
97
- false
98
- end
99
-
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
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class Base
7
+ attr_reader :name,
8
+ :collection_of_options,
9
+ :options
10
+
11
+ def initialize(
12
+ name,
13
+ *helpers,
14
+ option_helpers:,
15
+ **options
16
+ )
17
+ @name = name
18
+ @option_helpers = option_helpers
19
+
20
+ register_options(helpers:, options:)
21
+ end
22
+
23
+ def method_missing(name, *args, &block)
24
+ option = @collection_of_options.find_by(name:)
25
+ return super if option.nil?
26
+
27
+ option.body_for_access
28
+ end
29
+
30
+ def respond_to_missing?(name, *)
31
+ @collection_of_options.names.include?(name) || super
32
+ end
33
+
34
+ def register_options(helpers:, options:)
35
+ merged_options = augment_options_with_helpers(helpers:, options:)
36
+ options_registrar = create_options_registrar(options: merged_options)
37
+
38
+ @options = merged_options
39
+ @collection_of_options = options_registrar.collection
40
+ end
41
+
42
+ def options_for_checks
43
+ @collection_of_options.options_for_checks
44
+ end
45
+
46
+ def system_name
47
+ @system_name ||= self.class.name.demodulize.downcase.to_sym
48
+ end
49
+
50
+ def i18n_name
51
+ @i18n_name ||= system_name.to_s.pluralize
52
+ end
53
+
54
+ def actor
55
+ @actor ||= self.class::Actor.new(self)
56
+ end
57
+
58
+ def typed_actor_kwargs
59
+ @typed_actor_kwargs ||= { system_name => actor }.freeze
60
+ end
61
+
62
+ def input?
63
+ false
64
+ end
65
+
66
+ def internal?
67
+ false
68
+ end
69
+
70
+ def output?
71
+ false
72
+ end
73
+
74
+ private
75
+
76
+ def create_options_registrar(options:)
77
+ Servactory::Maintenance::Options::Registrar.register(
78
+ attribute: self,
79
+ options:,
80
+ features: available_feature_options
81
+ )
82
+ end
83
+
84
+ def available_feature_options
85
+ raise NotImplementedError, "Subclass must implement available_feature_options"
86
+ end
87
+
88
+ def augment_options_with_helpers(helpers:, options:)
89
+ result_options = options.dup
90
+ merge_standard_helpers_into(target_options: result_options, helpers:) if helpers.present?
91
+ merge_advanced_helpers_into(target_options: result_options, source_options: options)
92
+ result_options
93
+ end
94
+
95
+ def merge_standard_helpers_into(target_options:, helpers:)
96
+ standard_helpers_result = transform_helpers_to_options(helpers:)
97
+ target_options.deep_merge!(standard_helpers_result)
98
+ end
99
+
100
+ def merge_advanced_helpers_into(target_options:, source_options:)
101
+ advanced_helpers = filter_advanced_helpers(options: source_options)
102
+ return if advanced_helpers.blank?
103
+
104
+ advanced_helpers_result = transform_helpers_to_options(helpers: advanced_helpers)
105
+ target_options.deep_merge!(advanced_helpers_result)
106
+ end
107
+
108
+ def filter_advanced_helpers(options:)
109
+ reserved_options = Servactory::Maintenance::Options::Registrar::RESERVED_OPTIONS
110
+ options.except(*reserved_options)
111
+ end
112
+
113
+ def transform_helpers_to_options(helpers:)
114
+ helpers.each_with_object({}) do |(helper_name, values), result|
115
+ helper = @option_helpers.find_by(name: helper_name)
116
+ next if helper.blank?
117
+
118
+ transformed_option = transform_helper_to_option(helper:, values:)
119
+ result.deep_merge!(transformed_option) if transformed_option.present?
120
+ end
121
+ end
122
+
123
+ def transform_helper_to_option(helper:, values:)
124
+ return helper.equivalent unless helper.equivalent.is_a?(Proc)
125
+
126
+ if values.is_a?(Hash)
127
+ helper.equivalent.call(**values)
128
+ else
129
+ helper.equivalent.call(values)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end