servactory 1.9.7 → 2.0.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/en.yml +18 -4
  3. data/config/locales/ru.yml +20 -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/inclusion.rb +14 -9
  19. data/lib/servactory/inputs/validations/must.rb +26 -17
  20. data/lib/servactory/inputs/validations/required.rb +15 -10
  21. data/lib/servactory/inputs/validations/type.rb +85 -34
  22. data/lib/servactory/inputs/workspace.rb +6 -3
  23. data/lib/servactory/internals/dsl.rb +6 -1
  24. data/lib/servactory/internals/internal.rb +82 -14
  25. data/lib/servactory/internals/validations/type.rb +110 -29
  26. data/lib/servactory/maintenance/attributes/define_conflict.rb +15 -0
  27. data/lib/servactory/maintenance/attributes/define_method.rb +17 -0
  28. data/lib/servactory/maintenance/attributes/option.rb +115 -0
  29. data/lib/servactory/maintenance/attributes/option_helper.rb +17 -0
  30. data/lib/servactory/maintenance/attributes/option_helpers_collection.rb +20 -0
  31. data/lib/servactory/maintenance/attributes/options_collection.rb +48 -0
  32. data/lib/servactory/maintenance/collection_mode/class_names_collection.rb +16 -0
  33. data/lib/servactory/maintenance/hash_mode/class_names_collection.rb +16 -0
  34. data/lib/servactory/maintenance/validations/object_schema.rb +119 -0
  35. data/lib/servactory/methods/workspace.rb +2 -0
  36. data/lib/servactory/outputs/dsl.rb +6 -1
  37. data/lib/servactory/outputs/output.rb +80 -19
  38. data/lib/servactory/outputs/validations/type.rb +110 -31
  39. data/lib/servactory/version.rb +5 -4
  40. metadata +15 -11
  41. data/lib/servactory/inputs/define_input_conflict.rb +0 -13
  42. data/lib/servactory/inputs/define_input_method.rb +0 -15
  43. data/lib/servactory/inputs/option.rb +0 -98
  44. data/lib/servactory/inputs/option_helper.rb +0 -15
  45. data/lib/servactory/inputs/option_helpers_collection.rb +0 -18
  46. data/lib/servactory/inputs/options_collection.rb +0 -46
@@ -4,22 +4,63 @@ module Servactory
4
4
  module Internals
5
5
  module Validations
6
6
  class Type < Base
7
- DEFAULT_MESSAGE = lambda do |service_class_name:, internal:, expected_type:, given_type:|
8
- I18n.t(
9
- "servactory.internals.checks.type.default_error",
10
- service_class_name: service_class_name,
11
- internal_name: internal.name,
12
- expected_type: expected_type,
13
- given_type: given_type
14
- )
7
+ DEFAULT_MESSAGE = lambda do |service_class_name:, internal:, value:, key_name:, expected_type:, given_type:| # rubocop:disable Metrics/BlockLength
8
+ if internal.collection_mode?
9
+ collection_message = internal.consists_of.fetch(:message)
10
+
11
+ if collection_message.is_a?(Proc)
12
+ collection_message.call(internal: internal, expected_type: expected_type)
13
+ elsif collection_message.is_a?(String) && collection_message.present?
14
+ collection_message
15
+ elsif value.is_a?(internal.types.fetch(0, Array))
16
+ I18n.t(
17
+ "servactory.internals.checks.type.default_error.for_collection.wrong_element_type",
18
+ service_class_name: service_class_name,
19
+ internal_name: internal.name,
20
+ expected_type: expected_type,
21
+ given_type: given_type
22
+ )
23
+ else
24
+ I18n.t(
25
+ "servactory.internals.checks.type.default_error.for_collection.wrong_type",
26
+ service_class_name: service_class_name,
27
+ internal_name: internal.name,
28
+ expected_type: internal.types.fetch(0, Array),
29
+ given_type: value.class.name
30
+ )
31
+ end
32
+ elsif internal.hash_mode? && key_name.present?
33
+ I18n.t(
34
+ "servactory.internals.checks.type.default_error.for_hash.wrong_element_type",
35
+ service_class_name: service_class_name,
36
+ internal_name: internal.name,
37
+ key_name: key_name,
38
+ expected_type: expected_type,
39
+ given_type: given_type
40
+ )
41
+ else
42
+ I18n.t(
43
+ "servactory.internals.checks.type.default_error.default",
44
+ service_class_name: service_class_name,
45
+ internal_name: internal.name,
46
+ expected_type: expected_type,
47
+ given_type: given_type
48
+ )
49
+ end
15
50
  end
16
51
 
17
52
  private_constant :DEFAULT_MESSAGE
18
53
 
19
54
  def self.validate!(...)
55
+ return unless should_be_checked?
56
+
20
57
  new(...).validate!
21
58
  end
22
59
 
60
+ def self.should_be_checked?
61
+ true
62
+ end
63
+
23
64
  ##########################################################################
24
65
 
25
66
  def initialize(context:, internal:, value:)
@@ -30,38 +71,78 @@ module Servactory
30
71
  @value = value
31
72
  end
32
73
 
33
- def validate!
34
- return unless should_be_checked?
74
+ def validate! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
75
+ object_schema_validator = nil
76
+
77
+ return if prepared_types.any? do |type|
78
+ if @internal.collection_mode?
79
+ @value.is_a?(@internal.types.fetch(0, Array)) &&
80
+ @value.respond_to?(:all?) && @value.all?(type)
81
+ elsif @internal.hash_mode?
82
+ object_schema_validator = Servactory::Maintenance::Validations::ObjectSchema.validate(
83
+ object: @value,
84
+ schema: @internal.schema
85
+ )
86
+
87
+ object_schema_validator.valid?
88
+ else
89
+ @value.is_a?(type)
90
+ end
91
+ end
92
+
93
+ if (first_error = object_schema_validator&.errors&.first).present?
94
+ raise_default_object_error_with(first_error)
95
+ end
96
+
97
+ raise_default_error
98
+ end
99
+
100
+ private
35
101
 
36
- return if prepared_types.any? { |type| @value.is_a?(type) }
102
+ def prepared_types
103
+ @prepared_types ||=
104
+ if @internal.collection_mode?
105
+ prepared_types_from(Array(@internal.consists_of.fetch(:type, [])))
106
+ else
107
+ prepared_types_from(@internal.types)
108
+ end
109
+ end
37
110
 
111
+ def prepared_types_from(types)
112
+ types.map do |type|
113
+ if type.is_a?(String)
114
+ Object.const_get(type)
115
+ else
116
+ type
117
+ end
118
+ end
119
+ end
120
+
121
+ ########################################################################
122
+
123
+ def raise_default_object_error_with(error)
38
124
  raise_error_with(
39
125
  DEFAULT_MESSAGE,
40
126
  service_class_name: @context.class.name,
41
127
  internal: @internal,
42
- expected_type: prepared_types.join(", "),
43
- given_type: @value.class.name
128
+ value: @value,
129
+ key_name: error.fetch(:name),
130
+ expected_type: error.fetch(:expected_type),
131
+ given_type: error.fetch(:given_type)
44
132
  )
45
133
  end
46
134
 
47
- private
48
-
49
- def should_be_checked?
50
- @internal.required? || (
51
- @internal.optional? && !@value.nil?
135
+ def raise_default_error
136
+ raise_error_with(
137
+ DEFAULT_MESSAGE,
138
+ service_class_name: @context.class.name,
139
+ internal: @internal,
140
+ value: @value,
141
+ key_name: nil,
142
+ expected_type: prepared_types.join(", "),
143
+ given_type: @value.class.name
52
144
  )
53
145
  end
54
-
55
- def prepared_types
56
- @prepared_types ||=
57
- Array(@internal.types).map do |type|
58
- if type.is_a?(String)
59
- Object.const_get(type)
60
- else
61
- type
62
- end
63
- end
64
- end
65
146
  end
66
147
  end
67
148
  end
@@ -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,119 @@
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
+ @valid = false
21
+ @errors = []
22
+ end
23
+
24
+ def validate
25
+ @valid = validate_for(object: @object, schema: @schema)
26
+
27
+ self
28
+ end
29
+
30
+ def valid?
31
+ @valid
32
+ end
33
+
34
+ private
35
+
36
+ def validate_for(object:, schema:, root_schema_key: nil) # rubocop:disable Metrics/MethodLength
37
+ unless object.respond_to?(:fetch)
38
+ add_error(name: root_schema_key, expected_type: Hash, given_type: object.class)
39
+ return false
40
+ end
41
+
42
+ schema.all? do |schema_key, schema_value|
43
+ attribute_type = schema_value.fetch(:type, String)
44
+
45
+ if attribute_type == Hash
46
+ validate_for(
47
+ object: object.fetch(schema_key, {}),
48
+ schema: schema_value.except(*RESERVED_ATTRIBUTES),
49
+ root_schema_key: schema_key
50
+ )
51
+ else
52
+ is_success = validate_with(
53
+ object: object,
54
+ schema_key: schema_key,
55
+ schema_value: schema_value,
56
+ attribute_type: attribute_type,
57
+ attribute_required: schema_value.fetch(:required, true)
58
+ )
59
+
60
+ unless is_success
61
+ add_error(
62
+ name: schema_key,
63
+ expected_type: attribute_type,
64
+ given_type: object.fetch(schema_key, nil).class
65
+ )
66
+ end
67
+
68
+ is_success
69
+ end
70
+ end
71
+ end
72
+
73
+ def validate_with(object:, schema_key:, schema_value:, attribute_type:, attribute_required:) # rubocop:disable Metrics/MethodLength
74
+ unless should_be_checked_for?(
75
+ object: object,
76
+ schema_key: schema_key,
77
+ schema_value: schema_value,
78
+ required: attribute_required
79
+ ) # do
80
+ return true
81
+ end
82
+
83
+ value = object.fetch(schema_key, nil)
84
+ prepared_value = prepare_value_from(schema_value: schema_value, value: value, required: attribute_required)
85
+
86
+ Array(attribute_type).any? { |type| prepared_value.is_a?(type) }
87
+ end
88
+
89
+ def should_be_checked_for?(object:, schema_key:, schema_value:, required:)
90
+ required || (
91
+ !required && !fetch_default_from(schema_value).nil?
92
+ ) || (
93
+ !required && !object.fetch(schema_key, nil).nil?
94
+ )
95
+ end
96
+
97
+ def prepare_value_from(schema_value:, value:, required:)
98
+ if !required && !fetch_default_from(schema_value).nil? && value.blank?
99
+ fetch_default_from(schema_value)
100
+ else
101
+ value
102
+ end
103
+ end
104
+
105
+ def fetch_default_from(value)
106
+ value.fetch(:default, nil)
107
+ end
108
+
109
+ def add_error(name:, expected_type:, given_type:)
110
+ @errors << {
111
+ name: name,
112
+ expected_type: expected_type,
113
+ given_type: given_type
114
+ }
115
+ end
116
+ end
117
+ end
118
+ end
119
+ 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