servactory 2.14.0 → 2.15.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f6718e3b5e71152099847ec1713940116c44a7641dbc3c63d7a4ef60478ef3c
4
- data.tar.gz: 9ecd3809f2792504bb5f2694b4ceb9969e5bd4ad386dccfbdb5af6e0a8e55a9a
3
+ metadata.gz: c53c25c36b576e8a9c501b60a5ad223bbbfc3001dbf2186fd4086d937cbae084
4
+ data.tar.gz: 6c08533b7fa4562860d53d2489c98e67674697c6442901c54d6aff1c98cd4793
5
5
  SHA512:
6
- metadata.gz: 1810233557cfb71509fcf04e618295cde0ab190511b8bb50569cd098cf359c606730de1299d89eaaeec8e6c55d65b901fc7bbea895e46532a1a3f825d5bfcc6f
7
- data.tar.gz: 4011679f170547c0ee3b4e1c527dfadccb961185446d4386c3a9ff6da81da9a6da683d7d5102ca339a5609c644d61ded644321115cc1697368451301c53a8078
6
+ metadata.gz: e4ca873a1c77929813b5d7bb8f8e380cbfbacd58c7d249fdc629f12d95ef5b76c5920698bbf0a1accfd400d707c39b7550a4d7f1e002258419c6b5fc31d85a75
7
+ data.tar.gz: a83ebf3a2a6564d775c5d128c0939cf23c7d90041fd119137696ef7d400592b33d10cb205f4e2f997fe74da351e6f8aef40ec7c30d1a9d208d478965f22451a8
@@ -86,11 +86,11 @@ module Servactory
86
86
  collection_of_stages << current_stage
87
87
  end
88
88
 
89
- def method_missing(name, ...)
90
- return method_missing_for_action_aliases(...) if config.action_aliases.include?(name)
89
+ def method_missing(name, *args, &block)
90
+ return method_missing_for_action_aliases(*args) if config.action_aliases.include?(name)
91
91
 
92
92
  if (action_shortcut = config.action_shortcuts.find_by(name:)).present?
93
- return method_missing_for_shortcuts_for_make(action_shortcut, ...)
93
+ return method_missing_for_shortcuts_for_make(action_shortcut, *args)
94
94
  end
95
95
 
96
96
  super
@@ -53,7 +53,7 @@ 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/AbcSize, Lint/UnusedMethodArgument
56
+ def fetch_with(name:, &block) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Lint/UnusedMethodArgument
57
57
  internal_name = @context.class.config.predicate_methods_enabled? ? name.to_s.chomp("?").to_sym : name
58
58
  internal = @collection_of_internals.find_by(name: internal_name)
59
59
 
@@ -61,6 +61,12 @@ module Servactory
61
61
 
62
62
  internal_value = @context.send(:servactory_service_warehouse).fetch_internal(internal.name)
63
63
 
64
+ Servactory::Maintenance::Attributes::Tools::Validation.validate!(
65
+ context: @context,
66
+ attribute: internal,
67
+ value: internal_value
68
+ )
69
+
64
70
  if name.to_s.end_with?("?") && @context.class.config.predicate_methods_enabled?
65
71
  Servactory::Utils.query_attribute(internal_value)
66
72
  else
@@ -2,7 +2,19 @@
2
2
 
3
3
  module Servactory
4
4
  module DSL
5
- @extensions = []
5
+ module Extensions
6
+ def self.registry
7
+ @registry ||= []
8
+ end
9
+
10
+ def self.register(*extensions)
11
+ registry.concat(extensions)
12
+ end
13
+
14
+ def self.clear
15
+ @registry = []
16
+ end
17
+ end
6
18
 
7
19
  def self.included(base)
8
20
  base.include(Configuration::DSL)
@@ -12,21 +24,15 @@ module Servactory
12
24
  base.include(Internals::DSL)
13
25
  base.include(Outputs::DSL)
14
26
 
15
- extensions.each { |extension| base.include(extension) }
27
+ Extensions.registry.each { |extension| base.include(extension) }
16
28
 
17
29
  base.include(Actions::DSL)
18
30
  end
19
31
 
20
32
  def self.with_extensions(*extensions)
21
- @extensions = extensions
22
-
33
+ Extensions.clear
34
+ Extensions.register(*extensions)
23
35
  self
24
36
  end
25
-
26
- def self.extensions
27
- @extensions
28
- end
29
-
30
- private_class_method :extensions
31
37
  end
32
38
  end
@@ -14,6 +14,20 @@ module Servactory
14
14
 
15
15
  super(message)
16
16
  end
17
+
18
+ def method_missing(name, *_args) # rubocop:disable Naming/PredicateMethod
19
+ @type == normalize_method_name(name)
20
+ end
21
+
22
+ def respond_to_missing?(name, *)
23
+ @type == normalize_method_name(name) || super
24
+ end
25
+
26
+ private
27
+
28
+ def normalize_method_name(name)
29
+ name.to_s.chomp("?").to_sym
30
+ end
17
31
  end
18
32
  end
19
33
  end
@@ -12,90 +12,113 @@ module Servactory
12
12
  end
13
13
 
14
14
  def build(collection_of_inputs:, collection_of_internals:, collection_of_outputs:, config:)
15
- dynamic_options = config.input_option_helpers.dynamic_options
16
-
17
- build_inputs_with(collection_of_inputs:, dynamic_options:)
18
- build_internals_with(collection_of_internals:, dynamic_options:)
19
- build_outputs_with(collection_of_outputs:, dynamic_options:)
15
+ build_all_attributes(
16
+ inputs: collection_of_inputs,
17
+ internals: collection_of_internals,
18
+ outputs: collection_of_outputs,
19
+ dynamic_options: extract_dynamic_options_from(config)
20
+ )
20
21
 
21
22
  self
22
23
  end
23
24
 
24
25
  private
25
26
 
26
- def build_inputs_with(collection_of_inputs:, dynamic_options:)
27
- @inputs = collection_of_inputs.to_h do |input|
28
- options = build_options_for(input, dynamic_options)
29
- options = enrich_options_for(input, options)
27
+ def extract_dynamic_options_from(config)
28
+ config.input_option_helpers.dynamic_options
29
+ end
30
30
 
31
- options[:required] = input.required
32
- options[:default] = input.default
31
+ def build_all_attributes(inputs:, internals:, outputs:, dynamic_options:)
32
+ build_input_attributes_with(collection: inputs, dynamic_options:)
33
+ build_internal_attributes_with(collection: internals, dynamic_options:)
34
+ build_output_attributes_with(collection: outputs, dynamic_options:)
35
+ end
33
36
 
34
- [
35
- input.name,
36
- options
37
- ]
38
- end
37
+ def build_input_attributes_with(collection:, dynamic_options:)
38
+ @inputs = build_attributes_with(
39
+ collection:,
40
+ dynamic_options:,
41
+ include_specific_options: true
42
+ )
39
43
  end
40
44
 
41
- def build_internals_with(collection_of_internals:, dynamic_options:)
42
- @internals = collection_of_internals.to_h do |internal|
43
- options = build_options_for(internal, dynamic_options)
44
- options = enrich_options_for(internal, options)
45
+ def build_internal_attributes_with(collection:, dynamic_options:)
46
+ @internals = build_attributes_with(
47
+ collection:,
48
+ dynamic_options:
49
+ )
50
+ end
45
51
 
46
- [
47
- internal.name,
48
- options
49
- ]
50
- end
52
+ def build_output_attributes_with(collection:, dynamic_options:)
53
+ @outputs = build_attributes_with(
54
+ collection:,
55
+ dynamic_options:
56
+ )
51
57
  end
52
58
 
53
- def build_outputs_with(collection_of_outputs:, dynamic_options:)
54
- @outputs = collection_of_outputs.to_h do |output|
55
- options = build_options_for(output, dynamic_options)
56
- options = enrich_options_for(output, options)
59
+ def build_attributes_with(collection:, dynamic_options:, include_specific_options: false) # rubocop:disable Metrics/MethodLength
60
+ collection.to_h do |attribute|
61
+ options = process_options_for(
62
+ attribute:,
63
+ dynamic_options:
64
+ )
65
+
66
+ if include_specific_options
67
+ options[:required] = attribute.required
68
+ options[:default] = attribute.default
69
+ end
57
70
 
58
- [
59
- output.name,
60
- options
61
- ]
71
+ [attribute.name, options]
62
72
  end
63
73
  end
64
74
 
65
- ##########################################################################
75
+ def process_options_for(attribute:, dynamic_options:)
76
+ options = build_dynamic_options_for(
77
+ attribute:,
78
+ dynamic_options:
79
+ )
66
80
 
67
- def build_options_for(attribute, dynamic_options)
81
+ enrich_options_with_common_fields(
82
+ attribute:,
83
+ options:
84
+ )
85
+ end
86
+
87
+ def build_dynamic_options_for(attribute:, dynamic_options:)
68
88
  attribute.options.to_h do |key, value|
69
89
  dynamic_option = dynamic_options.find_by(name: key)
70
-
71
90
  next [nil, nil] if dynamic_option.nil?
72
91
 
73
92
  body_key = dynamic_option.meta.fetch(:body_key)
93
+ option = format_option_value(body_key:, value:)
74
94
 
75
- option = build_option_from(body_key:, value:)
76
-
77
- [
78
- dynamic_option.name,
79
- option
80
- ]
95
+ [dynamic_option.name, option]
81
96
  end.compact
82
97
  end
83
98
 
84
- def build_option_from(body_key:, value:)
99
+ def format_option_value(body_key:, value:)
85
100
  {
86
- body_key => value.is_a?(Hash) ? value.fetch(body_key, value) : value,
87
- message: value.is_a?(Hash) ? value.fetch(:message, nil) : nil
101
+ body_key => extract_value_for_body_key(body_key:, value:),
102
+ message: extract_message_from_value(value:)
88
103
  }
89
104
  end
90
105
 
91
- def enrich_options_for(attribute, options)
106
+ def extract_value_for_body_key(body_key:, value:)
107
+ value.is_a?(Hash) ? value.fetch(body_key, value) : value
108
+ end
109
+
110
+ def extract_message_from_value(value:)
111
+ value.is_a?(Hash) ? value.fetch(:message, nil) : nil
112
+ end
113
+
114
+ def enrich_options_with_common_fields(attribute:, options:)
92
115
  actor = attribute.class::Actor.new(attribute)
93
116
  must = attribute.collection_of_options.find_by(name: :must)
94
117
 
95
118
  options.merge(
96
119
  actor:,
97
120
  types: attribute.types,
98
- must: must.body
121
+ must: must&.body
99
122
  )
100
123
  end
101
124
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Servactory
4
4
  module Inputs
5
- class Input
5
+ class Input # rubocop:disable Metrics/ClassLength
6
6
  class Actor
7
7
  attr_reader :name,
8
8
  :internal_name,
@@ -10,21 +10,33 @@ module Servactory
10
10
  :default,
11
11
  :options
12
12
 
13
- def initialize(input) # rubocop:disable Metrics/MethodLength
13
+ def initialize(input)
14
14
  @name = input.name
15
15
  @internal_name = input.internal_name
16
16
  @types = input.types
17
17
  @default = input.default
18
18
  @options = input.options
19
19
 
20
- define_singleton_method(:system_name) { input.system_name }
21
- define_singleton_method(:i18n_name) { input.i18n_name }
22
- define_singleton_method(:optional?) { input.optional? }
23
- define_singleton_method(:required?) { input.required? }
24
- # The methods below are required to support the internal work.
25
- define_singleton_method(:input?) { true }
26
- define_singleton_method(:internal?) { false }
27
- define_singleton_method(:output?) { false }
20
+ define_identity_methods(input)
21
+ end
22
+
23
+ private
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
28
40
  end
29
41
  end
30
42
 
@@ -51,7 +63,6 @@ module Servactory
51
63
 
52
64
  def method_missing(name, *args, &block)
53
65
  option = @collection_of_options.find_by(name:)
54
-
55
66
  return super if option.nil?
56
67
 
57
68
  option.body
@@ -61,49 +72,14 @@ module Servactory
61
72
  @collection_of_options.names.include?(name) || super
62
73
  end
63
74
 
64
- def register_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
65
- advanced_helpers = options.except(*Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS)
66
-
67
- options = apply_helpers_for_options(helpers:, options:) if helpers.present?
68
- options = apply_helpers_for_options(helpers: advanced_helpers, options:) if advanced_helpers.present?
69
-
70
- options_registrar = Servactory::Maintenance::Attributes::Options::Registrar.register(
71
- attribute: self,
72
- options:,
73
- features: {
74
- required: true,
75
- types: true,
76
- default: true,
77
- must: true,
78
- prepare: true
79
- }
80
- )
75
+ def register_options(helpers:, options:)
76
+ merged_options = augment_options_with_helpers(helpers:, options:)
77
+ options_registrar = create_options_registrar(options: merged_options)
81
78
 
82
- @options = options
79
+ @options = merged_options
83
80
  @collection_of_options = options_registrar.collection
84
81
  end
85
82
 
86
- def apply_helpers_for_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
87
- prepared_options = {}
88
-
89
- helpers.each do |(helper, values)|
90
- found_helper = @option_helpers.find_by(name: helper)
91
-
92
- next if found_helper.blank?
93
-
94
- prepared_option =
95
- if found_helper.equivalent.is_a?(Proc)
96
- values.is_a?(Hash) ? found_helper.equivalent.call(**values) : found_helper.equivalent.call(values)
97
- else
98
- found_helper.equivalent
99
- end
100
-
101
- prepared_options.deep_merge!(prepared_option)
102
- end
103
-
104
- options.deep_merge(prepared_options)
105
- end
106
-
107
83
  def options_for_checks
108
84
  @collection_of_options.options_for_checks
109
85
  end
@@ -135,6 +111,71 @@ module Servactory
135
111
  def output?
136
112
  false
137
113
  end
114
+
115
+ private
116
+
117
+ def create_options_registrar(options:)
118
+ Servactory::Maintenance::Attributes::Options::Registrar.register(
119
+ attribute: self,
120
+ options:,
121
+ features: available_feature_options
122
+ )
123
+ end
124
+
125
+ def available_feature_options
126
+ {
127
+ required: true,
128
+ types: true,
129
+ default: true,
130
+ must: true,
131
+ prepare: true
132
+ }
133
+ end
134
+
135
+ def augment_options_with_helpers(helpers:, options:)
136
+ result_options = options.dup
137
+ merge_standard_helpers_into(target_options: result_options, helpers:) if helpers.present?
138
+ merge_advanced_helpers_into(target_options: result_options, source_options: options)
139
+ result_options
140
+ end
141
+
142
+ def merge_standard_helpers_into(target_options:, helpers:)
143
+ standard_helpers_result = transform_helpers_to_options(helpers:)
144
+ target_options.deep_merge!(standard_helpers_result)
145
+ end
146
+
147
+ def merge_advanced_helpers_into(target_options:, source_options:)
148
+ advanced_helpers = filter_advanced_helpers(options: source_options)
149
+ return if advanced_helpers.blank?
150
+
151
+ advanced_helpers_result = transform_helpers_to_options(helpers: advanced_helpers)
152
+ target_options.deep_merge!(advanced_helpers_result)
153
+ end
154
+
155
+ def filter_advanced_helpers(options:)
156
+ reserved_options = Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS
157
+ options.except(*reserved_options)
158
+ end
159
+
160
+ def transform_helpers_to_options(helpers:)
161
+ helpers.each_with_object({}) do |(helper_name, values), result|
162
+ helper = @option_helpers.find_by(name: helper_name)
163
+ next if helper.blank?
164
+
165
+ transformed_option = transform_helper_to_option(helper:, values:)
166
+ result.deep_merge!(transformed_option) if transformed_option.present?
167
+ end
168
+ end
169
+
170
+ def transform_helper_to_option(helper:, values:)
171
+ return helper.equivalent unless helper.equivalent.is_a?(Proc)
172
+
173
+ if values.is_a?(Hash)
174
+ helper.equivalent.call(**values)
175
+ else
176
+ helper.equivalent.call(values)
177
+ end
178
+ end
138
179
  end
139
180
  end
140
181
  end
@@ -36,9 +36,11 @@ module Servactory
36
36
  input:,
37
37
  check_key:,
38
38
  check_options:
39
- )
39
+ ).to_a
40
40
 
41
- errors.merge(errors_from_checks.to_a)
41
+ next if errors_from_checks.empty?
42
+
43
+ errors.merge(errors_from_checks)
42
44
  end
43
45
  end
44
46
 
@@ -13,12 +13,24 @@ module Servactory
13
13
  @types = internal.types
14
14
  @options = internal.options
15
15
 
16
- define_singleton_method(:system_name) { internal.system_name }
17
- define_singleton_method(:i18n_name) { internal.i18n_name }
18
- # The methods below are required to support the internal work.
19
- define_singleton_method(:input?) { false }
20
- define_singleton_method(:internal?) { true }
21
- define_singleton_method(:output?) { false }
16
+ define_identity_methods(internal)
17
+ end
18
+
19
+ private
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
22
34
  end
23
35
  end
24
36
 
@@ -40,7 +52,6 @@ module Servactory
40
52
 
41
53
  def method_missing(name, *args, &block)
42
54
  option = @collection_of_options.find_by(name:)
43
-
44
55
  return super if option.nil?
45
56
 
46
57
  option.body
@@ -50,46 +61,14 @@ module Servactory
50
61
  @collection_of_options.names.include?(name) || super
51
62
  end
52
63
 
53
- def register_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
54
- advanced_helpers = options.except(*Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS)
55
-
56
- options = apply_helpers_for_options(helpers:, options:) if helpers.present?
57
- options = apply_helpers_for_options(helpers: advanced_helpers, options:) if advanced_helpers.present?
58
-
59
- options_registrar = Servactory::Maintenance::Attributes::Options::Registrar.register(
60
- attribute: self,
61
- options:,
62
- features: {
63
- types: true,
64
- must: true
65
- }
66
- )
64
+ def register_options(helpers:, options:)
65
+ merged_options = augment_options_with_helpers(helpers:, options:)
66
+ options_registrar = create_options_registrar(options: merged_options)
67
67
 
68
- @options = options
68
+ @options = merged_options
69
69
  @collection_of_options = options_registrar.collection
70
70
  end
71
71
 
72
- def apply_helpers_for_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
73
- prepared_options = {}
74
-
75
- helpers.each do |(helper, values)|
76
- found_helper = @option_helpers.find_by(name: helper)
77
-
78
- next if found_helper.blank?
79
-
80
- prepared_option =
81
- if found_helper.equivalent.is_a?(Proc)
82
- values.is_a?(Hash) ? found_helper.equivalent.call(**values) : found_helper.equivalent.call(values)
83
- else
84
- found_helper.equivalent
85
- end
86
-
87
- prepared_options.deep_merge!(prepared_option)
88
- end
89
-
90
- options.deep_merge(prepared_options)
91
- end
92
-
93
72
  def options_for_checks
94
73
  @collection_of_options.options_for_checks
95
74
  end
@@ -113,6 +92,68 @@ module Servactory
113
92
  def output?
114
93
  false
115
94
  end
95
+
96
+ private
97
+
98
+ def create_options_registrar(options:)
99
+ Servactory::Maintenance::Attributes::Options::Registrar.register(
100
+ attribute: self,
101
+ options:,
102
+ features: available_feature_options
103
+ )
104
+ end
105
+
106
+ def available_feature_options
107
+ {
108
+ types: true,
109
+ must: true
110
+ }
111
+ end
112
+
113
+ def augment_options_with_helpers(helpers:, options:)
114
+ result_options = options.dup
115
+ merge_standard_helpers_into(target_options: result_options, helpers:) if helpers.present?
116
+ merge_advanced_helpers_into(target_options: result_options, source_options: options)
117
+ result_options
118
+ end
119
+
120
+ def merge_standard_helpers_into(target_options:, helpers:)
121
+ standard_helpers_result = transform_helpers_to_options(helpers:)
122
+ target_options.deep_merge!(standard_helpers_result)
123
+ end
124
+
125
+ def merge_advanced_helpers_into(target_options:, source_options:)
126
+ advanced_helpers = filter_advanced_helpers(options: source_options)
127
+ return if advanced_helpers.blank?
128
+
129
+ advanced_helpers_result = transform_helpers_to_options(helpers: advanced_helpers)
130
+ target_options.deep_merge!(advanced_helpers_result)
131
+ end
132
+
133
+ def filter_advanced_helpers(options:)
134
+ reserved_options = Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS
135
+ options.except(*reserved_options)
136
+ end
137
+
138
+ def transform_helpers_to_options(helpers:)
139
+ helpers.each_with_object({}) do |(helper_name, values), result|
140
+ helper = @option_helpers.find_by(name: helper_name)
141
+ next if helper.blank?
142
+
143
+ transformed_option = transform_helper_to_option(helper:, values:)
144
+ result.deep_merge!(transformed_option) if transformed_option.present?
145
+ end
146
+ end
147
+
148
+ def transform_helper_to_option(helper:, values:)
149
+ return helper.equivalent unless helper.equivalent.is_a?(Proc)
150
+
151
+ if values.is_a?(Hash)
152
+ helper.equivalent.call(**values)
153
+ else
154
+ helper.equivalent.call(values)
155
+ end
156
+ end
116
157
  end
117
158
  end
118
159
  end