servactory 3.0.0 → 3.1.0.rc2

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 (45) 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/option_helpers/option_helpers_collection.rb +60 -2
  6. data/lib/servactory/context/warehouse/inputs.rb +2 -2
  7. data/lib/servactory/context/workspace/inputs.rb +22 -10
  8. data/lib/servactory/context/workspace/internals.rb +35 -13
  9. data/lib/servactory/context/workspace/outputs.rb +12 -6
  10. data/lib/servactory/dsl.rb +1 -1
  11. data/lib/servactory/info/builder.rb +2 -3
  12. data/lib/servactory/inputs/collection.rb +14 -21
  13. data/lib/servactory/inputs/input.rb +6 -103
  14. data/lib/servactory/inputs/tools/validation.rb +32 -57
  15. data/lib/servactory/inputs/validations/required.rb +3 -2
  16. data/lib/servactory/internals/collection.rb +5 -24
  17. data/lib/servactory/internals/internal.rb +4 -112
  18. data/lib/servactory/maintenance/attributes/base.rb +135 -0
  19. data/lib/servactory/maintenance/attributes/collection.rb +109 -0
  20. data/lib/servactory/maintenance/attributes/option_helper.rb +33 -12
  21. data/lib/servactory/maintenance/{attributes/options_collection.rb → options/collection.rb} +6 -7
  22. data/lib/servactory/maintenance/{attributes → options}/define_conflict.rb +1 -1
  23. data/lib/servactory/maintenance/{attributes → options}/define_method.rb +1 -1
  24. data/lib/servactory/maintenance/options/helper.rb +23 -0
  25. data/lib/servactory/maintenance/{attributes → options}/option.rb +50 -27
  26. data/lib/servactory/maintenance/options/registrar.rb +200 -0
  27. data/lib/servactory/maintenance/{attributes/validations → validations/checkers}/must.rb +25 -8
  28. data/lib/servactory/maintenance/{attributes/validations → validations/checkers}/type.rb +6 -5
  29. data/lib/servactory/maintenance/validations/concerns/error_builder.rb +50 -0
  30. data/lib/servactory/maintenance/validations/performer.rb +57 -0
  31. data/lib/servactory/maintenance/validations/support/type_validator.rb +36 -0
  32. data/lib/servactory/maintenance/{attributes → validations}/translator/must.rb +1 -1
  33. data/lib/servactory/maintenance/{attributes → validations}/translator/type.rb +1 -1
  34. data/lib/servactory/outputs/collection.rb +5 -24
  35. data/lib/servactory/outputs/output.rb +4 -112
  36. data/lib/servactory/result.rb +12 -18
  37. data/lib/servactory/tool_kit/dynamic_options/must.rb +2 -2
  38. data/lib/servactory/tool_kit/dynamic_options/schema.rb +5 -5
  39. data/lib/servactory/utils.rb +0 -8
  40. data/lib/servactory/version.rb +2 -2
  41. metadata +16 -13
  42. data/lib/servactory/maintenance/attributes/options/registrar.rb +0 -183
  43. data/lib/servactory/maintenance/attributes/tools/validation.rb +0 -84
  44. data/lib/servactory/maintenance/attributes/validations/concerns/error_builder.rb +0 -52
  45. data/lib/servactory/maintenance/validations/types.rb +0 -34
@@ -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
- false
29
+ @attribute.input?
30
30
  end
31
31
 
32
32
  def internal?
33
- false
33
+ @attribute.internal?
34
34
  end
35
35
 
36
36
  def output?
37
- true
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
@@ -44,7 +44,7 @@ module Servactory
44
44
  class Result
45
45
  # Internal container for service output values.
46
46
  #
47
- # Provides dynamic method access to output values via method_missing.
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.
@@ -65,13 +65,6 @@ module Servactory
65
65
  end
66
66
 
67
67
  # Delegates method calls to stored outputs.
68
- #
69
- # Supports both regular output access and predicate methods
70
- # when predicate_methods_enabled is true.
71
- #
72
- # @param name [Symbol] Method name (output or predicate)
73
- # @param args [Array] Method arguments
74
- # @return [Object] Output value or predicate result
75
68
  def method_missing(name, *args)
76
69
  if name.to_s.end_with?("?")
77
70
  base_name = name.to_s.chomp("?").to_sym
@@ -86,10 +79,6 @@ module Servactory
86
79
  end
87
80
 
88
81
  # Checks if method corresponds to stored output.
89
- #
90
- # @param name [Symbol] Method name to check
91
- # @param include_private [Boolean] Include private methods in check
92
- # @return [Boolean] True if output exists for this method
93
82
  def respond_to_missing?(name, include_private = false)
94
83
  if name.to_s.end_with?("?")
95
84
  base_name = name.to_s.chomp("?").to_sym
@@ -297,15 +286,13 @@ module Servactory
297
286
  self
298
287
  end
299
288
 
300
- # Delegates method calls to outputs container.
301
- #
302
- # Provides access to output attributes as if they were
303
- # defined on the Result instance itself.
289
+ # Delegates method calls to the outputs container.
304
290
  #
305
291
  # @param name [Symbol] Method name (output attribute)
306
292
  # @param args [Array] Method arguments
307
293
  # @param block [Proc] Optional block
308
- # @return [Object] Output value
294
+ # @return [Object] Output value when name matches an output attribute
295
+ # @raise [NoMethodError] When method is truly undefined and no context present
309
296
  def method_missing(name, *args, &block)
310
297
  return outputs.public_send(name, *args, &block) if outputs.respond_to?(name)
311
298
 
@@ -350,7 +337,14 @@ module Servactory
350
337
  #
351
338
  # @return [Outputs] Outputs container with service values
352
339
  def outputs
353
- @outputs ||= Outputs.new(
340
+ @outputs ||= build_outputs
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
@@ -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::Attributes::OptionHelper]
158
+ # @return [Servactory::Maintenance::Options::Helper]
159
159
  def must(name)
160
- Servactory::Maintenance::Attributes::OptionHelper.new(
160
+ Servactory::Maintenance::Options::Helper.new(
161
161
  name: @option_name,
162
162
  equivalent: equivalent_with(name),
163
163
  meta: {
@@ -277,7 +277,7 @@ module Servactory
277
277
  return true
278
278
  end
279
279
 
280
- value = object.fetch(schema_key, nil)
280
+ value = object[schema_key]
281
281
  prepared_value = prepare_value_from(schema_value:, value:, required: attribute_required)
282
282
 
283
283
  [
@@ -297,7 +297,7 @@ module Servactory
297
297
  required || (
298
298
  !required && !fetch_default_from(schema_value).nil?
299
299
  ) || (
300
- !required && !object.fetch(schema_key, nil).nil?
300
+ !required && !object[schema_key].nil?
301
301
  )
302
302
  end
303
303
 
@@ -320,7 +320,7 @@ module Servactory
320
320
  # @param value [Hash] Schema definition
321
321
  # @return [Object, nil] Default value or nil
322
322
  def fetch_default_from(value)
323
- value.fetch(:default, nil)
323
+ value[:default]
324
324
  end
325
325
 
326
326
  ########################################################################
@@ -352,14 +352,14 @@ module Servactory
352
352
  )
353
353
  else
354
354
  # Apply scalar defaults.
355
- default_value = schema_value.fetch(:default, nil)
355
+ default_value = schema_value[:default]
356
356
 
357
357
  if !required && !default_value.nil? && !Servactory::Utils.value_present?(object_value)
358
358
  object[schema_key] = default_value
359
359
  end
360
360
 
361
361
  # Execute prepare callback if defined.
362
- unless (input_prepare = schema_value.fetch(:prepare, nil)).nil?
362
+ unless (input_prepare = schema_value[:prepare]).nil?
363
363
  object[schema_key] = input_prepare.call(value: object[schema_key])
364
364
  end
365
365
 
@@ -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)
@@ -3,9 +3,9 @@
3
3
  module Servactory
4
4
  module VERSION
5
5
  MAJOR = 3
6
- MINOR = 0
6
+ MINOR = 1
7
7
  PATCH = 0
8
- PRE = nil
8
+ PRE = "rc2"
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: servactory
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov
@@ -264,19 +264,22 @@ files:
264
264
  - lib/servactory/internals/collection.rb
265
265
  - lib/servactory/internals/dsl.rb
266
266
  - lib/servactory/internals/internal.rb
267
- - lib/servactory/maintenance/attributes/define_conflict.rb
268
- - lib/servactory/maintenance/attributes/define_method.rb
269
- - lib/servactory/maintenance/attributes/option.rb
267
+ - lib/servactory/maintenance/attributes/base.rb
268
+ - lib/servactory/maintenance/attributes/collection.rb
270
269
  - lib/servactory/maintenance/attributes/option_helper.rb
271
- - lib/servactory/maintenance/attributes/options/registrar.rb
272
- - lib/servactory/maintenance/attributes/options_collection.rb
273
- - lib/servactory/maintenance/attributes/tools/validation.rb
274
- - lib/servactory/maintenance/attributes/translator/must.rb
275
- - lib/servactory/maintenance/attributes/translator/type.rb
276
- - lib/servactory/maintenance/attributes/validations/concerns/error_builder.rb
277
- - lib/servactory/maintenance/attributes/validations/must.rb
278
- - lib/servactory/maintenance/attributes/validations/type.rb
279
- - lib/servactory/maintenance/validations/types.rb
270
+ - lib/servactory/maintenance/options/collection.rb
271
+ - lib/servactory/maintenance/options/define_conflict.rb
272
+ - lib/servactory/maintenance/options/define_method.rb
273
+ - lib/servactory/maintenance/options/helper.rb
274
+ - lib/servactory/maintenance/options/option.rb
275
+ - lib/servactory/maintenance/options/registrar.rb
276
+ - lib/servactory/maintenance/validations/checkers/must.rb
277
+ - lib/servactory/maintenance/validations/checkers/type.rb
278
+ - lib/servactory/maintenance/validations/concerns/error_builder.rb
279
+ - lib/servactory/maintenance/validations/performer.rb
280
+ - lib/servactory/maintenance/validations/support/type_validator.rb
281
+ - lib/servactory/maintenance/validations/translator/must.rb
282
+ - lib/servactory/maintenance/validations/translator/type.rb
280
283
  - lib/servactory/outputs/collection.rb
281
284
  - lib/servactory/outputs/dsl.rb
282
285
  - lib/servactory/outputs/output.rb
@@ -1,183 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- module Maintenance
5
- module Attributes
6
- module Options
7
- class Registrar # rubocop:disable Metrics/ClassLength
8
- RESERVED_OPTIONS = %i[
9
- type
10
- required
11
- default
12
- collection
13
- must
14
- prepare
15
- ].freeze
16
-
17
- DEFAULT_FEATURES = {
18
- required: false,
19
- types: false,
20
- default: false,
21
- must: false,
22
- prepare: false
23
- }.freeze
24
-
25
- private_constant :DEFAULT_FEATURES
26
-
27
- def self.register(...)
28
- new(...).register
29
- end
30
-
31
- def initialize(attribute:, options:, features:)
32
- @attribute = attribute
33
- @options = options
34
- @features = DEFAULT_FEATURES.merge(features)
35
- end
36
-
37
- ########################################################################
38
-
39
- def register
40
- register_feature(:required, Servactory::Inputs::Validations::Required)
41
- register_feature(:types, Servactory::Maintenance::Attributes::Validations::Type)
42
- register_feature(:default, Servactory::Maintenance::Attributes::Validations::Type)
43
- register_feature(:must, Servactory::Maintenance::Attributes::Validations::Must)
44
- register_feature(:prepare, nil)
45
-
46
- self
47
- end
48
-
49
- def collection
50
- @collection ||= Servactory::Maintenance::Attributes::OptionsCollection.new
51
- end
52
-
53
- private
54
-
55
- def register_feature(feature_name, validation_class)
56
- return unless @features.fetch(feature_name)
57
-
58
- method_name = "register_#{feature_name}_option"
59
- send(method_name, validation_class)
60
- end
61
-
62
- ########################################################################
63
-
64
- def register_required_option(validation_class)
65
- create_option(
66
- name: :required,
67
- validation_class:,
68
- define_methods: required_define_methods,
69
- define_conflicts: required_define_conflicts,
70
- need_for_checks: true,
71
- body_key: :is,
72
- body_fallback: true
73
- )
74
- end
75
-
76
- def register_types_option(validation_class)
77
- create_option(
78
- name: :types,
79
- validation_class:,
80
- original_value: Array(@options.fetch(:type)).uniq,
81
- need_for_checks: true,
82
- body_key: :is,
83
- body_fallback: nil,
84
- with_advanced_mode: false
85
- )
86
- end
87
-
88
- def register_default_option(validation_class) # rubocop:disable Metrics/MethodLength
89
- create_option(
90
- name: :default,
91
- validation_class:,
92
- define_methods: [
93
- create_define_method(
94
- name: :default_value_present?,
95
- content: ->(option:) { !option.nil? }
96
- )
97
- ],
98
- need_for_checks: true,
99
- body_key: :is,
100
- body_fallback: nil,
101
- with_advanced_mode: false
102
- )
103
- end
104
-
105
- def register_must_option(validation_class) # rubocop:disable Metrics/MethodLength
106
- create_option(
107
- name: :must,
108
- validation_class:,
109
- define_methods: [
110
- create_define_method(
111
- name: :must_present?,
112
- content: ->(option:) { option.present? }
113
- )
114
- ],
115
- need_for_checks: true,
116
- body_key: :is,
117
- body_fallback: nil,
118
- with_advanced_mode: false
119
- )
120
- end
121
-
122
- def register_prepare_option(_validation_class) # rubocop:disable Metrics/MethodLength
123
- create_option(
124
- name: :prepare,
125
- validation_class: nil,
126
- define_methods: [
127
- create_define_method(
128
- name: :prepare_present?,
129
- content: ->(option:) { option[:in].present? }
130
- )
131
- ],
132
- need_for_checks: false,
133
- body_key: :in,
134
- body_fallback: false
135
- )
136
- end
137
-
138
- ########################################################################
139
-
140
- def required_define_methods
141
- [
142
- create_define_method(
143
- name: :required?,
144
- content: ->(option:) { Servactory::Utils.true?(option[:is]) }
145
- ),
146
- create_define_method(
147
- name: :optional?,
148
- content: ->(option:) { !Servactory::Utils.true?(option[:is]) }
149
- )
150
- ]
151
- end
152
-
153
- def required_define_conflicts
154
- [
155
- Servactory::Maintenance::Attributes::DefineConflict.new(
156
- content: -> { :required_vs_default if @attribute.required? && @attribute.default_value_present? }
157
- )
158
- ]
159
- end
160
-
161
- ########################################################################
162
-
163
- def create_option(name:, validation_class:, **options)
164
- collection << Servactory::Maintenance::Attributes::Option.new(
165
- name:,
166
- attribute: @attribute,
167
- validation_class:,
168
- **options,
169
- **@options
170
- )
171
- end
172
-
173
- def create_define_method(name:, content:)
174
- Servactory::Maintenance::Attributes::DefineMethod.new(
175
- name:,
176
- content:
177
- )
178
- end
179
- end
180
- end
181
- end
182
- end
183
- end
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- module Maintenance
5
- module Attributes
6
- module Tools
7
- class Validation
8
- def self.validate!(...)
9
- new(...).validate!
10
- end
11
-
12
- def initialize(context:, attribute:, value:)
13
- @context = context
14
- @attribute = attribute
15
- @value = value
16
- @first_error = nil
17
- end
18
-
19
- def validate!
20
- process
21
-
22
- raise_errors
23
- end
24
-
25
- private
26
-
27
- def process
28
- @attribute.options_for_checks.each do |check_key, check_options|
29
- process_option(check_key, check_options)
30
- break if @first_error.present?
31
- end
32
- end
33
-
34
- def process_option(check_key, check_options) # rubocop:disable Metrics/MethodLength
35
- return if validation_classes.empty?
36
-
37
- validation_classes.each do |validation_class|
38
- error_message = process_validation_class(
39
- validation_class:,
40
- check_key:,
41
- check_options:
42
- )
43
-
44
- next if error_message.blank?
45
-
46
- @first_error = error_message
47
- break
48
- end
49
- end
50
-
51
- def process_validation_class(
52
- validation_class:,
53
- check_key:,
54
- check_options:
55
- )
56
- validation_class.check(
57
- context: @context,
58
- attribute: @attribute,
59
- value: @value,
60
- check_key:,
61
- check_options:
62
- )
63
- end
64
-
65
- ########################################################################
66
-
67
- def validation_classes
68
- @validation_classes ||= @attribute.collection_of_options.validation_classes
69
- end
70
-
71
- ########################################################################
72
-
73
- def raise_errors
74
- return if @first_error.nil?
75
-
76
- raise @context.config
77
- .public_send(:"#{@attribute.system_name}_exception_class")
78
- .new(context: @context, message: @first_error)
79
- end
80
- end
81
- end
82
- end
83
- end
84
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Servactory
4
- module Maintenance
5
- module Attributes
6
- module Validations
7
- module Concerns
8
- # Concern providing error message processing for validators.
9
- #
10
- # ## Purpose
11
- #
12
- # ErrorBuilder provides shared logic for processing error messages that
13
- # can be either static strings or dynamic Procs. This allows validators
14
- # to support both simple error messages and context-aware messages.
15
- #
16
- # ## Usage
17
- #
18
- # Extend in validator classes:
19
- #
20
- # ```ruby
21
- # class MyValidator
22
- # extend Concerns::ErrorBuilder
23
- #
24
- # def self.build_error(...)
25
- # process_message(message, **context)
26
- # end
27
- # end
28
- # ```
29
- #
30
- # ## Methods Provided
31
- #
32
- # - `process_message` - converts String or Proc message to String
33
- module ErrorBuilder
34
- # Processes a message that may be a String or Proc.
35
- #
36
- # If message is a Proc, calls it with the provided attributes.
37
- # If message is a String, returns it unchanged.
38
- #
39
- # @param message [String, Proc] The message to process
40
- # @param attributes [Hash] Attributes to pass to Proc if message is callable
41
- # @return [String] The processed message string
42
- def process_message(message, **attributes)
43
- return message unless message.is_a?(Proc)
44
-
45
- message.call(**attributes)
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end