servactory 1.9.6 → 2.0.1

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 (63) 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/{methods/method.rb → actions/action.rb} +2 -2
  5. data/lib/servactory/{methods/aliases_for_make → actions/aliases}/collection.rb +2 -2
  6. data/lib/servactory/{methods/method_collection.rb → actions/collection.rb} +3 -3
  7. data/lib/servactory/{methods → actions}/dsl.rb +9 -9
  8. data/lib/servactory/{methods/shortcuts_for_make → actions/shortcuts}/collection.rb +2 -2
  9. data/lib/servactory/actions/stages/collection.rb +20 -0
  10. data/lib/servactory/actions/stages/stage.rb +30 -0
  11. data/lib/servactory/{methods → actions}/tools/runner.rb +1 -1
  12. data/lib/servactory/{methods → actions}/workspace.rb +4 -2
  13. data/lib/servactory/base.rb +1 -1
  14. data/lib/servactory/configuration/dsl.rb +4 -2
  15. data/lib/servactory/configuration/factory.rb +12 -4
  16. data/lib/servactory/configuration/setup.rb +24 -8
  17. data/lib/servactory/context/workspace/inputs.rb +43 -7
  18. data/lib/servactory/context/workspace/outputs.rb +1 -1
  19. data/lib/servactory/context/workspace.rb +0 -4
  20. data/lib/servactory/dsl.rb +18 -1
  21. data/lib/servactory/inputs/collection.rb +1 -1
  22. data/lib/servactory/inputs/dsl.rb +2 -0
  23. data/lib/servactory/inputs/input.rb +106 -88
  24. data/lib/servactory/inputs/tools/distributor.rb +24 -0
  25. data/lib/servactory/inputs/tools/rules.rb +3 -3
  26. data/lib/servactory/inputs/tools/{find_unnecessary.rb → unnecessary.rb} +4 -4
  27. data/lib/servactory/inputs/tools/validation.rb +5 -7
  28. data/lib/servactory/inputs/validations/base.rb +1 -1
  29. data/lib/servactory/inputs/validations/inclusion.rb +15 -10
  30. data/lib/servactory/inputs/validations/must.rb +26 -17
  31. data/lib/servactory/inputs/validations/required.rb +16 -11
  32. data/lib/servactory/inputs/validations/type.rb +19 -69
  33. data/lib/servactory/inputs/workspace.rb +6 -3
  34. data/lib/servactory/internals/dsl.rb +6 -1
  35. data/lib/servactory/internals/internal.rb +81 -14
  36. data/lib/servactory/internals/validations/base.rb +1 -1
  37. data/lib/servactory/internals/validations/type.rb +6 -43
  38. data/lib/servactory/maintenance/attributes/define_conflict.rb +15 -0
  39. data/lib/servactory/maintenance/attributes/define_method.rb +17 -0
  40. data/lib/servactory/maintenance/attributes/option.rb +121 -0
  41. data/lib/servactory/maintenance/attributes/option_helper.rb +17 -0
  42. data/lib/servactory/maintenance/attributes/option_helpers_collection.rb +20 -0
  43. data/lib/servactory/maintenance/attributes/options_collection.rb +48 -0
  44. data/lib/servactory/maintenance/collection_mode/class_names_collection.rb +16 -0
  45. data/lib/servactory/maintenance/hash_mode/class_names_collection.rb +16 -0
  46. data/lib/servactory/maintenance/validations/collection.rb +66 -0
  47. data/lib/servactory/maintenance/validations/object_schema.rb +116 -0
  48. data/lib/servactory/maintenance/validations/types.rb +181 -0
  49. data/lib/servactory/outputs/dsl.rb +6 -1
  50. data/lib/servactory/outputs/output.rb +79 -19
  51. data/lib/servactory/outputs/validations/base.rb +1 -1
  52. data/lib/servactory/outputs/validations/type.rb +6 -45
  53. data/lib/servactory/version.rb +5 -4
  54. data/lib/servactory.rb +1 -1
  55. metadata +56 -30
  56. data/lib/servactory/inputs/define_input_conflict.rb +0 -13
  57. data/lib/servactory/inputs/define_input_method.rb +0 -15
  58. data/lib/servactory/inputs/option.rb +0 -98
  59. data/lib/servactory/inputs/option_helper.rb +0 -15
  60. data/lib/servactory/inputs/option_helpers_collection.rb +0 -18
  61. data/lib/servactory/inputs/options_collection.rb +0 -46
  62. data/lib/servactory/methods/stage.rb +0 -28
  63. data/lib/servactory/methods/stage_collection.rb +0 -18
@@ -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.fetch(:is)
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,181 @@
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(
21
+ "#{attribute_system_name}_name": attribute.name,
22
+ expected_type: expected_type,
23
+ given_type: given_type
24
+ )
25
+ elsif collection_message.is_a?(String) && collection_message.present?
26
+ collection_message
27
+ elsif value.is_a?(attribute.types.fetch(0, Array))
28
+ I18n.t(
29
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.for_collection.wrong_element_type", # rubocop:disable Layout/LineLength
30
+ service_class_name: service_class_name,
31
+ "#{attribute_system_name}_name": attribute.name,
32
+ expected_type: expected_type,
33
+ given_type: given_type
34
+ )
35
+ else
36
+ I18n.t(
37
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.for_collection.wrong_type", # rubocop:disable Layout/LineLength
38
+ service_class_name: service_class_name,
39
+ "#{attribute_system_name}_name": attribute.name,
40
+ expected_type: attribute.types.fetch(0, Array),
41
+ given_type: value.class.name
42
+ )
43
+ end
44
+ elsif attribute.hash_mode? && key_name.present?
45
+ hash_message = attribute.schema.fetch(:message)
46
+
47
+ if hash_message.is_a?(Proc)
48
+ hash_message.call(
49
+ "#{attribute_system_name}_name": attribute.name,
50
+ key_name: key_name,
51
+ expected_type: expected_type,
52
+ given_type: given_type
53
+ )
54
+ elsif hash_message.is_a?(String) && hash_message.present?
55
+ hash_message
56
+ else
57
+ I18n.t(
58
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.for_hash.wrong_element_type", # rubocop:disable Layout/LineLength
59
+ service_class_name: service_class_name,
60
+ "#{attribute_system_name}_name": attribute.name,
61
+ key_name: key_name,
62
+ expected_type: expected_type,
63
+ given_type: given_type
64
+ )
65
+ end
66
+ else
67
+ I18n.t(
68
+ "servactory.#{attribute_system_name.to_s.pluralize}.checks.type.default_error.default",
69
+ service_class_name: service_class_name,
70
+ "#{attribute_system_name}_name": attribute.name,
71
+ expected_type: expected_type,
72
+ given_type: given_type
73
+ )
74
+ end
75
+ end
76
+
77
+ private_constant :DEFAULT_MESSAGE
78
+
79
+ def self.validate!(...)
80
+ new(...).validate!
81
+ end
82
+
83
+ def initialize(context:, attribute:, types:, value:, error_callback:)
84
+ @context = context
85
+ @attribute = attribute
86
+ @types = types
87
+ @value = value
88
+ @error_callback = error_callback
89
+ end
90
+
91
+ def validate! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
+ collection_validator = nil
93
+ object_schema_validator = nil
94
+
95
+ return if prepared_types.any? do |type|
96
+ if @attribute.collection_mode?
97
+ collection_validator = Servactory::Maintenance::Validations::Collection.validate(
98
+ value: @value,
99
+ types: @attribute.types,
100
+ type: type
101
+ )
102
+
103
+ collection_validator.valid?
104
+ elsif @attribute.hash_mode?
105
+ object_schema_validator = Servactory::Maintenance::Validations::ObjectSchema.validate(
106
+ object: @value,
107
+ schema: @attribute.schema
108
+ )
109
+
110
+ object_schema_validator.valid?
111
+ else
112
+ @value.is_a?(type)
113
+ end
114
+ end
115
+
116
+ if (first_error = collection_validator&.errors&.first).present?
117
+ return @error_callback.call(
118
+ message: DEFAULT_MESSAGE,
119
+ service_class_name: @context.class.name,
120
+ attribute_system_name: attribute_system_name,
121
+ attribute: @attribute,
122
+ value: @value,
123
+ key_name: nil,
124
+ expected_type: first_error.fetch(:expected_type),
125
+ given_type: first_error.fetch(:given_type)
126
+ )
127
+ end
128
+
129
+ if (first_error = object_schema_validator&.errors&.first).present?
130
+ return @error_callback.call(
131
+ message: DEFAULT_MESSAGE,
132
+ service_class_name: @context.class.name,
133
+ attribute_system_name: attribute_system_name,
134
+ attribute: @attribute,
135
+ value: @value,
136
+ key_name: first_error.fetch(:key_name),
137
+ expected_type: first_error.fetch(:expected_type),
138
+ given_type: first_error.fetch(:given_type)
139
+ )
140
+ end
141
+
142
+ @error_callback.call(
143
+ message: DEFAULT_MESSAGE,
144
+ service_class_name: @context.class.name,
145
+ attribute_system_name: attribute_system_name,
146
+ attribute: @attribute,
147
+ value: @value,
148
+ key_name: nil,
149
+ expected_type: prepared_types.join(", "),
150
+ given_type: @value.class.name
151
+ )
152
+ end
153
+
154
+ private
155
+
156
+ def attribute_system_name
157
+ @attribute.class.name.demodulize.downcase.to_sym
158
+ end
159
+
160
+ def prepared_types
161
+ @prepared_types ||=
162
+ if @attribute.collection_mode?
163
+ prepared_types_from(Array(@attribute.consists_of.fetch(:type, [])))
164
+ else
165
+ prepared_types_from(@types)
166
+ end
167
+ end
168
+
169
+ def prepared_types_from(types)
170
+ types.map do |type|
171
+ if type.is_a?(String)
172
+ Object.const_get(type)
173
+ else
174
+ type
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -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
@@ -4,36 +4,96 @@ module Servactory
4
4
  module Outputs
5
5
  class Output
6
6
  attr_reader :name,
7
- :types,
8
- :required,
9
- :default
7
+ :collection_mode_class_names,
8
+ :hash_mode_class_names
10
9
 
11
- def initialize(name, type:, **options)
10
+ def initialize(
11
+ name,
12
+ collection_mode_class_names:,
13
+ hash_mode_class_names:,
14
+ **options
15
+ )
12
16
  @name = name
13
- @types = Array(type)
17
+ @collection_mode_class_names = collection_mode_class_names
18
+ @hash_mode_class_names = hash_mode_class_names
14
19
 
15
- @required = options.fetch(:required, true)
16
- @default = required? ? nil : options.fetch(:default, nil)
20
+ add_basic_options_with(options)
17
21
  end
18
22
 
19
- def options_for_checks
20
- {
21
- types: types,
22
- required: required,
23
- default: default
24
- }
23
+ def method_missing(name, *args, &block)
24
+ option = collection_of_options.find_by(name: name)
25
+
26
+ return super if option.nil?
27
+
28
+ option.body
29
+ end
30
+
31
+ def respond_to_missing?(name, *)
32
+ collection_of_options.names.include?(name) || super
33
+ end
34
+
35
+ def add_basic_options_with(options)
36
+ # Check Class: Servactory::Outputs::Validations::Type
37
+ add_types_option_with(options)
38
+ add_collection_option_with(options)
39
+ add_hash_option_with(options)
40
+ end
41
+
42
+ def add_types_option_with(options)
43
+ collection_of_options << Servactory::Maintenance::Attributes::Option.new(
44
+ name: :types,
45
+ attribute: self,
46
+ validation_class: Servactory::Internals::Validations::Type,
47
+ original_value: Array(options.fetch(:type)),
48
+ need_for_checks: true,
49
+ body_fallback: nil,
50
+ with_advanced_mode: false
51
+ )
25
52
  end
26
53
 
27
- def required?
28
- Servactory::Utils.true?(required)
54
+ def add_collection_option_with(options) # rubocop:disable Metrics/MethodLength
55
+ collection_of_options << Servactory::Maintenance::Attributes::Option.new(
56
+ name: :consists_of,
57
+ attribute: self,
58
+ validation_class: Servactory::Internals::Validations::Type,
59
+ define_methods: [
60
+ Servactory::Maintenance::Attributes::DefineMethod.new(
61
+ name: :collection_mode?,
62
+ content: ->(**) { collection_mode_class_names.include?(options.fetch(:type)) }
63
+ )
64
+ ],
65
+ need_for_checks: false,
66
+ body_key: :type,
67
+ body_value: String,
68
+ body_fallback: String,
69
+ **options
70
+ )
29
71
  end
30
72
 
31
- def optional?
32
- !required?
73
+ def add_hash_option_with(options) # rubocop:disable Metrics/MethodLength
74
+ collection_of_options << Servactory::Maintenance::Attributes::Option.new(
75
+ name: :schema,
76
+ attribute: self,
77
+ validation_class: Servactory::Inputs::Validations::Type,
78
+ define_methods: [
79
+ Servactory::Maintenance::Attributes::DefineMethod.new(
80
+ name: :hash_mode?,
81
+ content: ->(**) { hash_mode_class_names.include?(options.fetch(:type)) }
82
+ )
83
+ ],
84
+ need_for_checks: false,
85
+ body_key: :is,
86
+ body_fallback: {},
87
+ **options
88
+ )
33
89
  end
34
90
 
35
- def default_value_present?
36
- !default.nil?
91
+ def collection_of_options
92
+ @collection_of_options ||= Servactory::Maintenance::Attributes::OptionsCollection.new
93
+ end
94
+
95
+ def options_for_checks
96
+ collection_of_options.options_for_checks
37
97
  end
38
98
  end
39
99
  end
@@ -6,7 +6,7 @@ module Servactory
6
6
  class Base
7
7
  protected
8
8
 
9
- def raise_error_with(message, **attributes)
9
+ def raise_error_with(message:, **attributes)
10
10
  message = message.call(**attributes) if message.is_a?(Proc)
11
11
 
12
12
  raise @context.class.config.output_error_class.new(message: message)
@@ -4,24 +4,10 @@ module Servactory
4
4
  module Outputs
5
5
  module Validations
6
6
  class Type < Base
7
- DEFAULT_MESSAGE = lambda do |service_class_name:, output:, expected_type:, given_type:|
8
- I18n.t(
9
- "servactory.outputs.checks.type.default_error",
10
- service_class_name: service_class_name,
11
- output_name: output.name,
12
- expected_type: expected_type,
13
- given_type: given_type
14
- )
15
- end
16
-
17
- private_constant :DEFAULT_MESSAGE
18
-
19
7
  def self.validate!(...)
20
8
  new(...).validate!
21
9
  end
22
10
 
23
- ##########################################################################
24
-
25
11
  def initialize(context:, output:, value:)
26
12
  super()
27
13
 
@@ -31,39 +17,14 @@ module Servactory
31
17
  end
32
18
 
33
19
  def validate!
34
- return unless should_be_checked?
35
-
36
- return if prepared_types.any? { |type| @value.is_a?(type) }
37
-
38
- raise_error_with(
39
- DEFAULT_MESSAGE,
40
- service_class_name: @context.class.name,
41
- output: @output,
42
- expected_type: prepared_types.join(", "),
43
- given_type: @value.class.name
20
+ Servactory::Maintenance::Validations::Types.validate!(
21
+ context: @context,
22
+ attribute: @output,
23
+ types: @output.types,
24
+ value: @value,
25
+ error_callback: ->(**args) { raise_error_with(**args) }
44
26
  )
45
27
  end
46
-
47
- private
48
-
49
- def should_be_checked?
50
- @output.required? || (
51
- @output.optional? && !@output.default.nil?
52
- ) || (
53
- @output.optional? && !@value.nil?
54
- )
55
- end
56
-
57
- def prepared_types
58
- @prepared_types ||=
59
- Array(@output.types).map do |type|
60
- if type.is_a?(String)
61
- Object.const_get(type)
62
- else
63
- type
64
- end
65
- end
66
- end
67
28
  end
68
29
  end
69
30
  end
@@ -2,10 +2,11 @@
2
2
 
3
3
  module Servactory
4
4
  module VERSION
5
- MAJOR = 1
6
- MINOR = 9
7
- PATCH = 6
5
+ MAJOR = 2
6
+ MINOR = 0
7
+ PATCH = 1
8
+ PRE = nil
8
9
 
9
- STRING = [MAJOR, MINOR, PATCH].join(".")
10
+ STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
10
11
  end
11
12
  end
data/lib/servactory.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "zeitwerk"
4
4
 
5
- require "active_support/core_ext/string"
5
+ require "active_support/all"
6
6
 
7
7
  loader = Zeitwerk::Loader.for_gem
8
8
  loader.inflector.inflect(