servactory 1.9.6 → 2.0.0.rc3

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/en.yml +19 -4
  3. data/config/locales/ru.yml +21 -6
  4. data/lib/servactory/configuration/dsl.rb +2 -0
  5. data/lib/servactory/configuration/factory.rb +8 -0
  6. data/lib/servactory/configuration/setup.rb +20 -4
  7. data/lib/servactory/context/workspace/inputs.rb +41 -7
  8. data/lib/servactory/context/workspace/outputs.rb +1 -1
  9. data/lib/servactory/context/workspace.rb +0 -1
  10. data/lib/servactory/dsl.rb +17 -0
  11. data/lib/servactory/inputs/collection.rb +1 -1
  12. data/lib/servactory/inputs/dsl.rb +2 -0
  13. data/lib/servactory/inputs/input.rb +101 -82
  14. data/lib/servactory/inputs/tools/distributor.rb +24 -0
  15. data/lib/servactory/inputs/tools/rules.rb +3 -3
  16. data/lib/servactory/inputs/tools/{find_unnecessary.rb → unnecessary.rb} +4 -4
  17. data/lib/servactory/inputs/tools/validation.rb +5 -7
  18. data/lib/servactory/inputs/validations/base.rb +1 -1
  19. data/lib/servactory/inputs/validations/inclusion.rb +15 -10
  20. data/lib/servactory/inputs/validations/must.rb +26 -17
  21. data/lib/servactory/inputs/validations/required.rb +16 -11
  22. data/lib/servactory/inputs/validations/type.rb +19 -69
  23. data/lib/servactory/inputs/workspace.rb +6 -3
  24. data/lib/servactory/internals/dsl.rb +6 -1
  25. data/lib/servactory/internals/internal.rb +82 -14
  26. data/lib/servactory/internals/validations/base.rb +1 -1
  27. data/lib/servactory/internals/validations/type.rb +6 -43
  28. data/lib/servactory/maintenance/attributes/define_conflict.rb +15 -0
  29. data/lib/servactory/maintenance/attributes/define_method.rb +17 -0
  30. data/lib/servactory/maintenance/attributes/option.rb +115 -0
  31. data/lib/servactory/maintenance/attributes/option_helper.rb +17 -0
  32. data/lib/servactory/maintenance/attributes/option_helpers_collection.rb +20 -0
  33. data/lib/servactory/maintenance/attributes/options_collection.rb +48 -0
  34. data/lib/servactory/maintenance/collection_mode/class_names_collection.rb +16 -0
  35. data/lib/servactory/maintenance/hash_mode/class_names_collection.rb +16 -0
  36. data/lib/servactory/maintenance/validations/collection.rb +66 -0
  37. data/lib/servactory/maintenance/validations/object_schema.rb +116 -0
  38. data/lib/servactory/maintenance/validations/types.rb +164 -0
  39. data/lib/servactory/methods/workspace.rb +2 -0
  40. data/lib/servactory/outputs/dsl.rb +6 -1
  41. data/lib/servactory/outputs/output.rb +80 -19
  42. data/lib/servactory/outputs/validations/base.rb +1 -1
  43. data/lib/servactory/outputs/validations/type.rb +6 -45
  44. data/lib/servactory/version.rb +5 -4
  45. metadata +17 -11
  46. data/lib/servactory/inputs/define_input_conflict.rb +0 -13
  47. data/lib/servactory/inputs/define_input_method.rb +0 -15
  48. data/lib/servactory/inputs/option.rb +0 -98
  49. data/lib/servactory/inputs/option_helper.rb +0 -15
  50. data/lib/servactory/inputs/option_helpers_collection.rb +0 -18
  51. data/lib/servactory/inputs/options_collection.rb +0 -46
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class DefineConflict
7
+ attr_reader :content
8
+
9
+ def initialize(content:)
10
+ @content = content
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class DefineMethod
7
+ attr_reader :name,
8
+ :content
9
+
10
+ def initialize(name:, content:)
11
+ @name = name.to_sym
12
+ @content = content
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class Option
7
+ DEFAULT_BODY = ->(key:, body:, message: nil) { { key => body, message: message } }
8
+
9
+ private_constant :DEFAULT_BODY
10
+
11
+ attr_reader :name,
12
+ :validation_class,
13
+ :define_methods,
14
+ :define_conflicts,
15
+ :need_for_checks,
16
+ :body
17
+
18
+ # rubocop:disable Metrics/MethodLength
19
+ def initialize(
20
+ name:,
21
+ attribute:,
22
+ validation_class:,
23
+ need_for_checks:,
24
+ body_fallback:,
25
+ original_value: nil,
26
+ body_key: nil,
27
+ body_value: true,
28
+ define_methods: nil,
29
+ define_conflicts: nil,
30
+ with_advanced_mode: true,
31
+ **options
32
+ ) # do
33
+ @name = name.to_sym
34
+ @validation_class = validation_class
35
+ @define_methods = define_methods
36
+ @define_conflicts = define_conflicts
37
+ @need_for_checks = need_for_checks
38
+
39
+ @body = prepare_value_for(
40
+ original_value: original_value,
41
+ options: options,
42
+ body_key: body_key,
43
+ body_value: body_value,
44
+ body_fallback: body_fallback,
45
+ with_advanced_mode: with_advanced_mode
46
+ )
47
+
48
+ prepare_methods_for(attribute)
49
+ end
50
+ # rubocop:enable Metrics/MethodLength
51
+
52
+ def need_for_checks?
53
+ need_for_checks
54
+ end
55
+
56
+ private
57
+
58
+ def prepare_value_for(
59
+ original_value:,
60
+ options:,
61
+ body_key:,
62
+ body_value:,
63
+ body_fallback:,
64
+ with_advanced_mode:
65
+ )
66
+ return original_value if original_value.present?
67
+
68
+ return options.fetch(@name, body_fallback) unless with_advanced_mode
69
+
70
+ prepare_advanced_for(
71
+ body: options.fetch(@name, DEFAULT_BODY.call(key: body_key, body: body_fallback)),
72
+ body_key: body_key,
73
+ body_value: body_value,
74
+ body_fallback: body_fallback
75
+ )
76
+ end
77
+
78
+ def prepare_advanced_for(
79
+ body:,
80
+ body_key:,
81
+ body_value:,
82
+ body_fallback:
83
+ )
84
+ if body.is_a?(Hash)
85
+ message = body.fetch(:message, nil)
86
+
87
+ DEFAULT_BODY.call(
88
+ key: body_key,
89
+ body: body.fetch(body_key, message.present? ? body_value : body_fallback),
90
+ message: message
91
+ )
92
+ else
93
+ DEFAULT_BODY.call(key: body_key, body: body)
94
+ end
95
+ end
96
+
97
+ def prepare_methods_for(attribute)
98
+ attribute.instance_eval(define_methods_template) if define_methods_template.present?
99
+ end
100
+
101
+ def define_methods_template
102
+ return if @define_methods.blank?
103
+
104
+ @define_methods_template ||= @define_methods.map do |define_method|
105
+ <<-RUBY
106
+ def #{define_method.name}
107
+ #{define_method.content.call(option: @body)}
108
+ end
109
+ RUBY
110
+ end.join("\n")
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class OptionHelper
7
+ attr_reader :name,
8
+ :equivalent
9
+
10
+ def initialize(name:, equivalent:)
11
+ @name = name
12
+ @equivalent = equivalent
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class OptionHelpersCollection
7
+ extend Forwardable
8
+ def_delegators :@collection, :<<, :find, :merge
9
+
10
+ def initialize(collection)
11
+ @collection = collection
12
+ end
13
+
14
+ def find_by(name:)
15
+ find { |helper| helper.name == name }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Attributes
6
+ class OptionsCollection
7
+ extend Forwardable
8
+ def_delegators :@collection, :<<, :filter, :each, :map, :flat_map, :find
9
+
10
+ def initialize(*)
11
+ @collection = Set.new
12
+ end
13
+
14
+ def names
15
+ map(&:name)
16
+ end
17
+
18
+ def validation_classes
19
+ filter { |option| option.validation_class.present? }.map(&:validation_class).uniq
20
+ end
21
+
22
+ def options_for_checks
23
+ filter(&:need_for_checks?).to_h do |option|
24
+ option_body = if option.body.is_a?(Hash)
25
+ option.body.key?(:is) ? option.body.fetch(:is) : option.body
26
+ else
27
+ option.body
28
+ end
29
+
30
+ [option.name, option_body]
31
+ end
32
+ end
33
+
34
+ def defined_conflict_code
35
+ flat_map do |option|
36
+ option.define_conflicts&.map do |define_input_conflict|
37
+ define_input_conflict.content.call
38
+ end
39
+ end.reject(&:blank?).first
40
+ end
41
+
42
+ def find_by(name:)
43
+ find { |option| option.name == name }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module CollectionMode
6
+ class ClassNamesCollection
7
+ extend Forwardable
8
+ def_delegators :@collection, :include?
9
+
10
+ def initialize(collection)
11
+ @collection = collection
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module HashMode
6
+ class ClassNamesCollection
7
+ extend Forwardable
8
+ def_delegators :@collection, :include?
9
+
10
+ def initialize(collection)
11
+ @collection = collection
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Validations
6
+ class Collection
7
+ attr_reader :errors
8
+
9
+ def self.validate(...)
10
+ new(...).validate
11
+ end
12
+
13
+ def initialize(value:, types:, type:)
14
+ @value = value
15
+ @types = types
16
+ @type = type
17
+
18
+ @errors = []
19
+ end
20
+
21
+ def validate
22
+ unless @value.is_a?(prepared_type)
23
+ add_error(
24
+ expected_type: prepared_type.name,
25
+ given_type: @value.class.name
26
+ )
27
+
28
+ return self
29
+ end
30
+
31
+ validate_value!
32
+
33
+ self
34
+ end
35
+
36
+ def valid?
37
+ @errors.empty?
38
+ end
39
+
40
+ private
41
+
42
+ def prepared_type
43
+ @prepared_type ||= @types.fetch(0, Array)
44
+ end
45
+
46
+ def validate_value!
47
+ @value.each do |value_item|
48
+ next if value_item.is_a?(@type)
49
+
50
+ add_error(
51
+ expected_type: @type,
52
+ given_type: value_item.class.name
53
+ )
54
+ end
55
+ end
56
+
57
+ def add_error(expected_type:, given_type:)
58
+ @errors << {
59
+ expected_type: expected_type,
60
+ given_type: given_type
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Validations
6
+ class ObjectSchema
7
+ RESERVED_ATTRIBUTES = %i[type required default].freeze
8
+ private_constant :RESERVED_ATTRIBUTES
9
+
10
+ attr_reader :errors
11
+
12
+ def self.validate(...)
13
+ new(...).validate
14
+ end
15
+
16
+ def initialize(object:, schema:)
17
+ @object = object
18
+ @schema = schema
19
+
20
+ @errors = []
21
+ end
22
+
23
+ def validate
24
+ validate_for(object: @object, schema: @schema)
25
+
26
+ self
27
+ end
28
+
29
+ def valid?
30
+ @errors.empty?
31
+ end
32
+
33
+ private
34
+
35
+ def validate_for(object:, schema:, root_schema_key: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
36
+ unless object.respond_to?(:fetch)
37
+ add_error(key_name: root_schema_key, expected_type: Hash.name, given_type: object.class.name)
38
+ return
39
+ end
40
+
41
+ schema.each do |schema_key, schema_value|
42
+ attribute_type = schema_value.fetch(:type, String)
43
+
44
+ if attribute_type == Hash
45
+ validate_for(
46
+ object: object.fetch(schema_key, {}),
47
+ schema: schema_value.except(*RESERVED_ATTRIBUTES),
48
+ root_schema_key: schema_key
49
+ )
50
+ else
51
+ is_success = validate_with(
52
+ object: object,
53
+ schema_key: schema_key,
54
+ schema_value: schema_value,
55
+ attribute_type: attribute_type,
56
+ attribute_required: schema_value.fetch(:required, true)
57
+ )
58
+
59
+ next if is_success
60
+
61
+ add_error(
62
+ key_name: schema_key,
63
+ expected_type: attribute_type,
64
+ given_type: object.fetch(schema_key, nil).class.name
65
+ )
66
+ end
67
+ end
68
+ end
69
+
70
+ def validate_with(object:, schema_key:, schema_value:, attribute_type:, attribute_required:) # rubocop:disable Metrics/MethodLength
71
+ unless should_be_checked_for?(
72
+ object: object,
73
+ schema_key: schema_key,
74
+ schema_value: schema_value,
75
+ required: attribute_required
76
+ ) # do
77
+ return true
78
+ end
79
+
80
+ value = object.fetch(schema_key, nil)
81
+ prepared_value = prepare_value_from(schema_value: schema_value, value: value, required: attribute_required)
82
+
83
+ Array(attribute_type).any? { |type| prepared_value.is_a?(type) }
84
+ end
85
+
86
+ def should_be_checked_for?(object:, schema_key:, schema_value:, required:)
87
+ required || (
88
+ !required && !fetch_default_from(schema_value).nil?
89
+ ) || (
90
+ !required && !object.fetch(schema_key, nil).nil?
91
+ )
92
+ end
93
+
94
+ def prepare_value_from(schema_value:, value:, required:)
95
+ if !required && !fetch_default_from(schema_value).nil? && value.blank?
96
+ fetch_default_from(schema_value)
97
+ else
98
+ value
99
+ end
100
+ end
101
+
102
+ def fetch_default_from(value)
103
+ value.fetch(:default, nil)
104
+ end
105
+
106
+ def add_error(key_name:, expected_type:, given_type:)
107
+ @errors << {
108
+ key_name: key_name,
109
+ expected_type: expected_type,
110
+ given_type: given_type
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Maintenance
5
+ module Validations
6
+ class Types # rubocop:disable Metrics/ClassLength
7
+ DEFAULT_MESSAGE = lambda do | # rubocop:disable Metrics/BlockLength
8
+ service_class_name:,
9
+ attribute_system_name:,
10
+ attribute:,
11
+ value:,
12
+ key_name:,
13
+ expected_type:,
14
+ given_type:
15
+ | # do
16
+ if attribute.collection_mode?
17
+ collection_message = attribute.consists_of.fetch(:message)
18
+
19
+ if collection_message.is_a?(Proc)
20
+ collection_message.call(attribute_system_name => attribute, expected_type: expected_type)
21
+ elsif collection_message.is_a?(String) && collection_message.present?
22
+ collection_message
23
+ elsif value.is_a?(attribute.types.fetch(0, Array))
24
+ I18n.t(
25
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.for_collection.wrong_element_type", # rubocop:disable Layout/LineLength
26
+ service_class_name: service_class_name,
27
+ "#{attribute_system_name}_name": attribute.name,
28
+ expected_type: expected_type,
29
+ given_type: given_type
30
+ )
31
+ else
32
+ I18n.t(
33
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.for_collection.wrong_type", # rubocop:disable Layout/LineLength
34
+ service_class_name: service_class_name,
35
+ "#{attribute_system_name}_name": attribute.name,
36
+ expected_type: attribute.types.fetch(0, Array),
37
+ given_type: value.class.name
38
+ )
39
+ end
40
+ elsif attribute.hash_mode? && key_name.present?
41
+ I18n.t(
42
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.for_hash.wrong_element_type", # rubocop:disable Layout/LineLength
43
+ service_class_name: service_class_name,
44
+ "#{attribute_system_name}_name": attribute.name,
45
+ key_name: key_name,
46
+ expected_type: expected_type,
47
+ given_type: given_type
48
+ )
49
+ else
50
+ I18n.t(
51
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.default",
52
+ service_class_name: service_class_name,
53
+ "#{attribute_system_name}_name": attribute.name,
54
+ expected_type: expected_type,
55
+ given_type: given_type
56
+ )
57
+ end
58
+ end
59
+
60
+ private_constant :DEFAULT_MESSAGE
61
+
62
+ def self.validate!(...)
63
+ new(...).validate!
64
+ end
65
+
66
+ def initialize(context:, attribute:, types:, value:, error_callback:)
67
+ @context = context
68
+ @attribute = attribute
69
+ @types = types
70
+ @value = value
71
+ @error_callback = error_callback
72
+ end
73
+
74
+ def validate! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
75
+ collection_validator = nil
76
+ object_schema_validator = nil
77
+
78
+ return if prepared_types.any? do |type|
79
+ if @attribute.collection_mode?
80
+ collection_validator = Servactory::Maintenance::Validations::Collection.validate(
81
+ value: @value,
82
+ types: @attribute.types,
83
+ type: type
84
+ )
85
+
86
+ collection_validator.valid?
87
+ elsif @attribute.hash_mode?
88
+ object_schema_validator = Servactory::Maintenance::Validations::ObjectSchema.validate(
89
+ object: @value,
90
+ schema: @attribute.schema
91
+ )
92
+
93
+ object_schema_validator.valid?
94
+ else
95
+ @value.is_a?(type)
96
+ end
97
+ end
98
+
99
+ if (first_error = collection_validator&.errors&.first).present?
100
+ return @error_callback.call(
101
+ message: DEFAULT_MESSAGE,
102
+ service_class_name: @context.class.name,
103
+ attribute_system_name: attribute_system_name,
104
+ attribute: @attribute,
105
+ value: @value,
106
+ key_name: nil,
107
+ expected_type: first_error.fetch(:expected_type),
108
+ given_type: first_error.fetch(:given_type)
109
+ )
110
+ end
111
+
112
+ if (first_error = object_schema_validator&.errors&.first).present?
113
+ return @error_callback.call(
114
+ message: DEFAULT_MESSAGE,
115
+ service_class_name: @context.class.name,
116
+ attribute_system_name: attribute_system_name,
117
+ attribute: @attribute,
118
+ value: @value,
119
+ key_name: first_error.fetch(:key_name),
120
+ expected_type: first_error.fetch(:expected_type),
121
+ given_type: first_error.fetch(:given_type)
122
+ )
123
+ end
124
+
125
+ @error_callback.call(
126
+ message: DEFAULT_MESSAGE,
127
+ service_class_name: @context.class.name,
128
+ attribute_system_name: attribute_system_name,
129
+ attribute: @attribute,
130
+ value: @value,
131
+ key_name: nil,
132
+ expected_type: prepared_types.join(", "),
133
+ given_type: @value.class.name
134
+ )
135
+ end
136
+
137
+ private
138
+
139
+ def attribute_system_name
140
+ @attribute.class.name.demodulize.downcase.to_sym
141
+ end
142
+
143
+ def prepared_types
144
+ @prepared_types ||=
145
+ if @attribute.collection_mode?
146
+ prepared_types_from(Array(@attribute.consists_of.fetch(:type, [])))
147
+ else
148
+ prepared_types_from(@types)
149
+ end
150
+ end
151
+
152
+ def prepared_types_from(types)
153
+ types.map do |type|
154
+ if type.is_a?(String)
155
+ Object.const_get(type)
156
+ else
157
+ type
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -3,6 +3,8 @@
3
3
  module Servactory
4
4
  module Methods
5
5
  module Workspace
6
+ private
7
+
6
8
  def call!(collection_of_stages:, **)
7
9
  super
8
10
 
@@ -17,7 +17,12 @@ module Servactory
17
17
  private
18
18
 
19
19
  def output(name, **options)
20
- collection_of_outputs << Output.new(name, **options)
20
+ collection_of_outputs << Output.new(
21
+ name,
22
+ collection_mode_class_names: config.collection_mode_class_names,
23
+ hash_mode_class_names: config.hash_mode_class_names,
24
+ **options
25
+ )
21
26
  end
22
27
 
23
28
  def collection_of_outputs