servactory 1.9.5 → 2.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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 +33 -15
  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