servactory 2.3.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/en.yml +31 -3
  3. data/config/locales/ru.yml +31 -3
  4. data/lib/servactory/context/callable.rb +28 -17
  5. data/lib/servactory/context/store.rb +58 -0
  6. data/lib/servactory/context/workspace/internals.rb +2 -2
  7. data/lib/servactory/context/workspace/outputs.rb +2 -2
  8. data/lib/servactory/context/workspace.rb +12 -25
  9. data/lib/servactory/errors/input_error.rb +4 -2
  10. data/lib/servactory/errors/internal_error.rb +4 -2
  11. data/lib/servactory/errors/output_error.rb +4 -2
  12. data/lib/servactory/exceptions/input.rb +4 -2
  13. data/lib/servactory/exceptions/internal.rb +4 -2
  14. data/lib/servactory/exceptions/output.rb +4 -2
  15. data/lib/servactory/inputs/input.rb +36 -3
  16. data/lib/servactory/internals/internal.rb +32 -3
  17. data/lib/servactory/maintenance/attributes/options/registrar.rb +15 -1
  18. data/lib/servactory/maintenance/attributes/translator/must.rb +4 -3
  19. data/lib/servactory/maintenance/attributes/translator/type.rb +2 -2
  20. data/lib/servactory/maintenance/attributes/validations/inclusion.rb +1 -1
  21. data/lib/servactory/maintenance/attributes/validations/must.rb +16 -9
  22. data/lib/servactory/outputs/output.rb +32 -3
  23. data/lib/servactory/result.rb +1 -1
  24. data/lib/servactory/test_kit/result.rb +3 -3
  25. data/lib/servactory/tool_kit/dynamic_options/format.rb +130 -0
  26. data/lib/servactory/tool_kit/dynamic_options/max.rb +68 -0
  27. data/lib/servactory/tool_kit/dynamic_options/min.rb +68 -0
  28. data/lib/servactory/tool_kit/dynamic_options/must.rb +130 -0
  29. data/lib/servactory/utils.rb +30 -4
  30. data/lib/servactory/version.rb +2 -2
  31. data/lib/servactory.rb +2 -0
  32. metadata +9 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9598623fa8187288fe92566315cf8bf293f0fec4995cd778186b7b29bba90504
4
- data.tar.gz: 03d3236b8b846e1fecc11ee19d754a235c5b40a9b7d58f36298767a2ea36b241
3
+ metadata.gz: 3996c6a138a1683fe3e46b04e9a57f9e135c93f3fd3855287374595e3b3111b3
4
+ data.tar.gz: 72e31e10893e67f5fd57ce54f0241ac35720071d6b1837bda85a953e717c8ae5
5
5
  SHA512:
6
- metadata.gz: 9a56ab102855338ee035e6b4163af6e39d618ea56c3a8277b9f6bae7c5974d203ade30db911ed32a0cad0c541e8f2b3e7dced9fb7015ee905e0e15ef66b06094
7
- data.tar.gz: c2d2014270e95e382de071858c8ce43f555c6b05d019f24b30f824fad934c63cd849b837813dbe53faf5afde8b6da4cb32d5f7ee72c6074ccc275d4a2a0dc453
6
+ metadata.gz: ec9727d9cd881bfb898f6843488fe7676ba18746ae40fd88e6feb391352c2e6eebf89137ca7d0743577d92caadde20a673973268040c2482eece16af82db2d5b
7
+ data.tar.gz: a73916a75bcd4b681ad41fc23ba63546d1a7ae46ff738eeb0f36671f191bd1195c64778e9ea7ddbec80a06c0c14dd46741b43bd521c09a94f9b6eb0cc395ab1f
@@ -16,7 +16,16 @@ en:
16
16
  default_error: "[%{service_class_name}] Wrong value in `%{input_name}`, must be one of `%{input_inclusion}`"
17
17
  must:
18
18
  default_error: "[%{service_class_name}] Input `%{input_name}` must \"%{code}\""
19
- syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{input_name}` input"
19
+ syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{input_name}` input: %{exception_message}"
20
+ dynamic_options:
21
+ format:
22
+ default: "[%{service_class_name}] Input `%{input_name}` does not match `%{format_name}` format"
23
+ wrong_pattern: "[%{service_class_name}] Input `%{input_name}` does not match `%{format_name}` format"
24
+ unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for input `%{input_name}`"
25
+ min:
26
+ default: "[%{service_class_name}] Input `%{input_name}` received value `%{value}`, which is less than `%{option_value}`"
27
+ max:
28
+ default: "[%{service_class_name}] Input `%{input_name}` received value `%{value}`, which is greater than `%{option_value}`"
20
29
  required:
21
30
  default_error:
22
31
  default: "[%{service_class_name}] Required input `%{input_name}` is missing"
@@ -25,6 +34,7 @@ en:
25
34
  default_error:
26
35
  default: "[%{service_class_name}] Wrong type of input `%{input_name}`, expected `%{expected_type}`, got `%{given_type}`"
27
36
  for_collection:
37
+ wrong_type: "[%{service_class_name}] Wrong input collection type `%{input_name}`, expected `%{expected_type}`, got `%{given_type}`"
28
38
  wrong_element_type: "[%{service_class_name}] Wrong type in input collection `%{input_name}`, expected `%{expected_type}`, got `%{given_type}`"
29
39
  for_hash:
30
40
  wrong_element_type: "[%{service_class_name}] Wrong type in input hash `%{input_name}`, expected `%{expected_type}` for `%{key_name}`, got `%{given_type}`"
@@ -42,7 +52,16 @@ en:
42
52
  default_error: "[%{service_class_name}] Wrong value in `%{internal_name}`, must be one of `%{internal_inclusion}`"
43
53
  must:
44
54
  default_error: "[%{service_class_name}] Internal attribute `%{internal_name}` must \"%{code}\""
45
- syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{internal_name}` internal attribute"
55
+ syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{internal_name}` internal attribute: %{exception_message}"
56
+ dynamic_options:
57
+ format:
58
+ default: "[%{service_class_name}] Internal attribute `%{internal_name}` does not match `%{format_name}` format"
59
+ wrong_pattern: "[%{service_class_name}] Internal attribute `%{internal_name}` does not match `%{format_name}` format"
60
+ unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for output attribute `%{internal_name}`"
61
+ min:
62
+ default: "[%{service_class_name}] Internal attribute `%{internal_name}` received value `%{value}`, which is less than `%{option_value}`"
63
+ max:
64
+ default: "[%{service_class_name}] Internal attribute `%{internal_name}` received value `%{value}`, which is greater than `%{option_value}`"
46
65
  type:
47
66
  default_error:
48
67
  default: "[%{service_class_name}] Wrong type of internal attribute `%{internal_name}`, expected `%{expected_type}`, got `%{given_type}`"
@@ -60,7 +79,16 @@ en:
60
79
  default_error: "[%{service_class_name}] Wrong value in `%{output_name}`, must be one of `%{output_inclusion}`"
61
80
  must:
62
81
  default_error: "[%{service_class_name}] Output attribute `%{output_name}` must \"%{code}\""
63
- syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{output_name}` output attribute"
82
+ syntax_error: "[%{service_class_name}] Syntax error inside `%{code}` of `%{output_name}` output attribute: %{exception_message}"
83
+ dynamic_options:
84
+ format:
85
+ default: "[%{service_class_name}] Output attribute `%{output_name}` does not match `%{format_name}` format"
86
+ wrong_pattern: "[%{service_class_name}] Output attribute `%{output_name}` does not match `%{format_name}` format"
87
+ unknown: "[%{service_class_name}] Unknown `%{format_name}` format specified for output attribute `%{output_name}`"
88
+ min:
89
+ default: "[%{service_class_name}] Output attribute `%{output_name}` received value `%{value}`, which is less than `%{option_value}`"
90
+ max:
91
+ default: "[%{service_class_name}] Output attribute `%{output_name}` received value `%{value}`, which is greater than `%{option_value}`"
64
92
  type:
65
93
  default_error:
66
94
  default: "[%{service_class_name}] Wrong type of output attribute `%{output_name}`, expected `%{expected_type}`, got `%{given_type}`"
@@ -16,7 +16,16 @@ ru:
16
16
  default_error: "[%{service_class_name}] Неправильное значение в `%{input_name}`, должно быть одним из `%{input_inclusion}`"
17
17
  must:
18
18
  default_error: "[%{service_class_name}] Инпут `%{input_name}` должен \"%{code}\""
19
- syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` инпута `%{input_name}`"
19
+ syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` инпута `%{input_name}`: %{exception_message}"
20
+ dynamic_options:
21
+ format:
22
+ default: "[%{service_class_name}] Инпут `%{input_name}` не соответствует формату `%{format_name}`"
23
+ wrong_pattern: "[%{service_class_name}] Инпут `%{input_name}` не соответствует формату `%{format_name}`"
24
+ unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у инпута `%{input_name}`"
25
+ min:
26
+ default: "[%{service_class_name}] Инпут `%{input_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
27
+ max:
28
+ default: "[%{service_class_name}] Инпут `%{input_name}` получил значение `%{value}`, которое больше `%{option_value}`"
20
29
  required:
21
30
  default_error:
22
31
  default: "[%{service_class_name}] Обязательный инпут `%{input_name}` отсутствует"
@@ -25,6 +34,7 @@ ru:
25
34
  default_error:
26
35
  default: "[%{service_class_name}] Неправильный тип инпута `%{input_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
27
36
  for_collection:
37
+ wrong_type: "[%{service_class_name}] Неправильный тип коллекции инпута `%{input_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
28
38
  wrong_element_type: "[%{service_class_name}] Неправильный тип в коллекции инпута `%{input_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
29
39
  for_hash:
30
40
  wrong_element_type: "[%{service_class_name}] Неправильный тип в хеше инпута `%{input_name}`, для `%{key_name}` ожидалось `%{expected_type}`, получено `%{given_type}`"
@@ -43,7 +53,16 @@ ru:
43
53
  default_error: "[%{service_class_name}] Неправильное значение в `%{internal_name}`, должно быть одним из `%{internal_inclusion}`"
44
54
  must:
45
55
  default_error: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` должен \"%{code}\""
46
- syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` внутреннего атрибута `%{internal_name}`"
56
+ syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` внутреннего атрибута `%{internal_name}`: %{exception_message}"
57
+ dynamic_options:
58
+ format:
59
+ default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` не соответствует формату `%{format_name}`"
60
+ wrong_pattern: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` не соответствует формату `%{format_name}`"
61
+ unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у внутреннего атрибута `%{internal_name}`"
62
+ min:
63
+ default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
64
+ max:
65
+ default: "[%{service_class_name}] Внутренний атрибут `%{internal_name}` получил значение `%{value}`, которое больше `%{option_value}`"
47
66
  type:
48
67
  default_error:
49
68
  default: "[%{service_class_name}] Неправильный тип внутреннего атрибута `%{internal_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
@@ -61,7 +80,16 @@ ru:
61
80
  default_error: "[%{service_class_name}] Неправильное значение в `%{output_name}`, должно быть одним из `%{output_inclusion}`"
62
81
  must:
63
82
  default_error: "[%{service_class_name}] Выходящий атрибут `%{output_name}` должен \"%{code}\""
64
- syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` выходящего атрибута `%{output_name}`"
83
+ syntax_error: "[%{service_class_name}] Синтаксическая ошибка внутри `%{code}` выходящего атрибута `%{output_name}`: %{exception_message}"
84
+ dynamic_options:
85
+ format:
86
+ default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` не соответствует формату `%{format_name}`"
87
+ wrong_pattern: "[%{service_class_name}] Выходящий атрибут `%{output_name}` не соответствует формату `%{format_name}`"
88
+ unknown: "[%{service_class_name}] Указан неизвестный формат `%{format_name}` у выходящего атрибута `%{output_name}`"
89
+ min:
90
+ default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` получил значение `%{value}`, которое меньше `%{option_value}`"
91
+ max:
92
+ default: "[%{service_class_name}] Выходящий атрибут `%{output_name}` получил значение `%{value}`, которое больше `%{option_value}`"
65
93
  type:
66
94
  default: "[%{service_class_name}] Неправильный тип выходящего атрибута `%{output_name}`, ожидалось `%{expected_type}`, получено `%{given_type}`"
67
95
  for_collection:
@@ -3,26 +3,43 @@
3
3
  module Servactory
4
4
  module Context
5
5
  module Callable
6
- def call!(arguments = {}) # rubocop:disable Metrics/MethodLength
6
+ def call!(arguments = {})
7
+ prepare_result_class
8
+
7
9
  context = send(:new)
8
10
 
9
- context.send(
10
- :_call!,
11
- incoming_arguments: arguments.symbolize_keys,
12
- collection_of_inputs: collection_of_inputs,
13
- collection_of_internals: collection_of_internals,
14
- collection_of_outputs: collection_of_outputs,
15
- collection_of_stages: collection_of_stages
16
- )
11
+ _call!(context, **arguments)
17
12
 
18
- Servactory::Result.success_for(context: context)
13
+ self::Result.success_for(context: context)
19
14
  rescue config.success_class => e
20
- Servactory::Result.success_for(context: e.context)
15
+ self::Result.success_for(context: e.context)
21
16
  end
22
17
 
23
18
  def call(arguments = {})
19
+ prepare_result_class
20
+
24
21
  context = send(:new)
25
22
 
23
+ _call!(context, **arguments)
24
+
25
+ self::Result.success_for(context: context)
26
+ rescue config.success_class => e
27
+ self::Result.success_for(context: e.context)
28
+ rescue config.failure_class => e
29
+ self::Result.failure_for(exception: e)
30
+ end
31
+
32
+ private
33
+
34
+ def prepare_result_class
35
+ return if Object.const_defined?("#{name}::Result")
36
+
37
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
38
+ Result = Class.new(Servactory::Result)
39
+ RUBY
40
+ end
41
+
42
+ def _call!(context, **arguments)
26
43
  context.send(
27
44
  :_call!,
28
45
  incoming_arguments: arguments.symbolize_keys,
@@ -31,12 +48,6 @@ module Servactory
31
48
  collection_of_outputs: collection_of_outputs,
32
49
  collection_of_stages: collection_of_stages
33
50
  )
34
-
35
- Servactory::Result.success_for(context: context)
36
- rescue config.success_class => e
37
- Servactory::Result.success_for(context: e.context)
38
- rescue config.failure_class => e
39
- Servactory::Result.failure_for(exception: e)
40
51
  end
41
52
  end
42
53
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Servactory
4
+ module Context
5
+ class Store
6
+ def initialize(context)
7
+ @context = context
8
+ end
9
+
10
+ def fetch_internal(name)
11
+ internals.fetch(name, nil)
12
+ end
13
+
14
+ def assign_internal(name, value)
15
+ assign_attribute(:internals, name, value)
16
+ end
17
+
18
+ def fetch_output(name)
19
+ outputs.fetch(name, nil)
20
+ end
21
+
22
+ def assign_output(name, value)
23
+ assign_attribute(:outputs, name, value)
24
+ end
25
+
26
+ def outputs
27
+ @outputs ||= context_data.fetch(:outputs)
28
+ end
29
+
30
+ private
31
+
32
+ def assign_attribute(section, name, value)
33
+ context_data[section].merge!({ name => value })
34
+ end
35
+
36
+ def internals
37
+ @internals ||= context_data.fetch(:internals)
38
+ end
39
+
40
+ def context_data
41
+ @context_data ||= state.fetch(context_id)
42
+ end
43
+
44
+ def state
45
+ {
46
+ context_id => {
47
+ internals: {},
48
+ outputs: {}
49
+ }
50
+ }
51
+ end
52
+
53
+ def context_id
54
+ @context_id ||= "context_#{@context.object_id}"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -50,7 +50,7 @@ module Servactory
50
50
  value: value
51
51
  )
52
52
 
53
- @context.send(:assign_servactory_service_storage_internal, internal.name, value)
53
+ @context.send(:servactory_service_store).assign_internal(internal.name, value)
54
54
  end
55
55
 
56
56
  def getter_with(name:, &block) # rubocop:disable Lint/UnusedMethodArgument
@@ -59,7 +59,7 @@ module Servactory
59
59
 
60
60
  return yield if internal.nil?
61
61
 
62
- internal_value = @context.send(:fetch_servactory_service_storage_internal, internal.name)
62
+ internal_value = @context.send(:servactory_service_store).fetch_internal(internal.name)
63
63
 
64
64
  if name.to_s.end_with?("?")
65
65
  Servactory::Utils.query_attribute(internal_value)
@@ -50,7 +50,7 @@ module Servactory
50
50
  value: value
51
51
  )
52
52
 
53
- @context.send(:assign_servactory_service_storage_output, output.name, value)
53
+ @context.send(:servactory_service_store).assign_output(output.name, value)
54
54
  end
55
55
 
56
56
  def getter_with(name:, &block) # rubocop:disable Lint/UnusedMethodArgument
@@ -59,7 +59,7 @@ module Servactory
59
59
 
60
60
  return yield if output.nil?
61
61
 
62
- output_value = @context.send(:fetch_servactory_service_storage_output, output.name)
62
+ output_value = @context.send(:servactory_service_store).fetch_output(output.name)
63
63
 
64
64
  if name.to_s.end_with?("?")
65
65
  Servactory::Utils.query_attribute(output_value)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Servactory
4
4
  module Context
5
- module Workspace # rubocop:disable Metrics/ModuleLength
5
+ module Workspace
6
6
  def inputs
7
7
  @inputs ||= Inputs.new(
8
8
  context: self,
@@ -29,24 +29,27 @@ module Servactory
29
29
  raise self.class.config.success_class.new(context: self)
30
30
  end
31
31
 
32
- def fail_input!(input_name, message:)
32
+ def fail_input!(input_name, message:, meta: nil)
33
33
  raise self.class.config.input_exception_class.new(
34
34
  input_name: input_name,
35
- message: message
35
+ message: message,
36
+ meta: meta
36
37
  )
37
38
  end
38
39
 
39
- def fail_internal!(internal_name, message:)
40
+ def fail_internal!(internal_name, message:, meta: nil)
40
41
  raise self.class.config.internal_exception_class.new(
41
42
  internal_name: internal_name,
42
- message: message
43
+ message: message,
44
+ meta: meta
43
45
  )
44
46
  end
45
47
 
46
- def fail_output!(output_name, message:)
48
+ def fail_output!(output_name, message:, meta: nil)
47
49
  raise self.class.config.output_exception_class.new(
48
50
  output_name: output_name,
49
- message: message
51
+ message: message,
52
+ meta: meta
50
53
  )
51
54
  end
52
55
 
@@ -104,24 +107,8 @@ module Servactory
104
107
  )
105
108
  end
106
109
 
107
- def servactory_service_storage
108
- @servactory_service_storage ||= { internals: {}, outputs: {} }
109
- end
110
-
111
- def assign_servactory_service_storage_internal(key, value)
112
- servactory_service_storage[:internals].merge!({ key => value })
113
- end
114
-
115
- def fetch_servactory_service_storage_internal(key)
116
- servactory_service_storage.fetch(:internals).fetch(key, nil)
117
- end
118
-
119
- def assign_servactory_service_storage_output(key, value)
120
- servactory_service_storage[:outputs].merge!({ key => value })
121
- end
122
-
123
- def fetch_servactory_service_storage_output(key)
124
- servactory_service_storage.fetch(:outputs).fetch(key, nil)
110
+ def servactory_service_store
111
+ @servactory_service_store ||= Store.new(self)
125
112
  end
126
113
  end
127
114
  end
@@ -5,11 +5,13 @@ module Servactory
5
5
  # DEPRECATED: This class will be deleted after release 2.4.
6
6
  class InputError < Servactory::Exceptions::Base
7
7
  attr_reader :message,
8
- :input_name
8
+ :input_name,
9
+ :meta
9
10
 
10
- def initialize(message:, input_name: nil)
11
+ def initialize(message:, input_name: nil, meta: nil)
11
12
  @message = message
12
13
  @input_name = input_name&.to_sym
14
+ @meta = meta
13
15
 
14
16
  super(message)
15
17
  end
@@ -5,11 +5,13 @@ module Servactory
5
5
  # DEPRECATED: This class will be deleted after release 2.4.
6
6
  class InternalError < Servactory::Exceptions::Base
7
7
  attr_reader :message,
8
- :internal_name
8
+ :internal_name,
9
+ :meta
9
10
 
10
- def initialize(message:, internal_name: nil)
11
+ def initialize(message:, internal_name: nil, meta: nil)
11
12
  @message = message
12
13
  @internal_name = internal_name&.to_sym
14
+ @meta = meta
13
15
 
14
16
  super(message)
15
17
  end
@@ -5,11 +5,13 @@ module Servactory
5
5
  # DEPRECATED: This class will be deleted after release 2.4.
6
6
  class OutputError < Servactory::Exceptions::Base
7
7
  attr_reader :message,
8
- :output_name
8
+ :output_name,
9
+ :meta
9
10
 
10
- def initialize(message:, output_name: nil)
11
+ def initialize(message:, output_name: nil, meta: nil)
11
12
  @message = message
12
13
  @output_name = output_name&.to_sym
14
+ @meta = meta
13
15
 
14
16
  super(message)
15
17
  end
@@ -4,11 +4,13 @@ module Servactory
4
4
  module Exceptions
5
5
  class Input < Base
6
6
  attr_reader :message,
7
- :input_name
7
+ :input_name,
8
+ :meta
8
9
 
9
- def initialize(message:, input_name: nil)
10
+ def initialize(message:, input_name: nil, meta: nil)
10
11
  @message = message
11
12
  @input_name = input_name&.to_sym
13
+ @meta = meta
12
14
 
13
15
  super(message)
14
16
  end
@@ -4,11 +4,13 @@ module Servactory
4
4
  module Exceptions
5
5
  class Internal < Base
6
6
  attr_reader :message,
7
- :internal_name
7
+ :internal_name,
8
+ :meta
8
9
 
9
- def initialize(message:, internal_name: nil)
10
+ def initialize(message:, internal_name: nil, meta: nil)
10
11
  @message = message
11
12
  @internal_name = internal_name&.to_sym
13
+ @meta = meta
12
14
 
13
15
  super(message)
14
16
  end
@@ -4,11 +4,13 @@ module Servactory
4
4
  module Exceptions
5
5
  class Output < Base
6
6
  attr_reader :message,
7
- :output_name
7
+ :output_name,
8
+ :meta
8
9
 
9
- def initialize(message:, output_name: nil)
10
+ def initialize(message:, output_name: nil, meta: nil)
10
11
  @message = message
11
12
  @output_name = output_name&.to_sym
13
+ @meta = meta
12
14
 
13
15
  super(message)
14
16
  end
@@ -3,6 +3,29 @@
3
3
  module Servactory
4
4
  module Inputs
5
5
  class Input
6
+ class Work
7
+ attr_reader :name,
8
+ :internal_name,
9
+ :types,
10
+ :inclusion
11
+
12
+ def initialize(input) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
13
+ @name = input.name
14
+ @internal_name = input.internal_name
15
+ @types = input.types
16
+ @inclusion = input.inclusion.slice(:in) if input.inclusion_present?
17
+
18
+ define_singleton_method(:system_name) { input.system_name }
19
+ define_singleton_method(:i18n_name) { input.i18n_name }
20
+ define_singleton_method(:optional?) { input.optional? }
21
+ define_singleton_method(:required?) { input.required? }
22
+ # The methods below are required to support the internal work.
23
+ define_singleton_method(:input?) { true }
24
+ define_singleton_method(:internal?) { false }
25
+ define_singleton_method(:output?) { false }
26
+ end
27
+ end
28
+
6
29
  attr_reader :name,
7
30
  :internal_name,
8
31
  :collection_of_options
@@ -40,7 +63,10 @@ module Servactory
40
63
  end
41
64
 
42
65
  def register_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
66
+ advanced_helpers = options.except(*Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS)
67
+
43
68
  options = apply_helpers_for_options(helpers: helpers, options: options) if helpers.present?
69
+ options = apply_helpers_for_options(helpers: advanced_helpers, options: options) if advanced_helpers.present?
44
70
 
45
71
  options_registrar = Servactory::Maintenance::Attributes::Options::Registrar.register(
46
72
  attribute: self,
@@ -62,15 +88,22 @@ module Servactory
62
88
  @collection_of_options = options_registrar.collection
63
89
  end
64
90
 
65
- def apply_helpers_for_options(helpers:, options:)
91
+ def apply_helpers_for_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
66
92
  prepared_options = {}
67
93
 
68
- helpers.each do |helper|
94
+ helpers.each do |(helper, values)|
69
95
  found_helper = @option_helpers.find_by(name: helper)
70
96
 
71
97
  next if found_helper.blank?
72
98
 
73
- prepared_options.merge!(found_helper.equivalent)
99
+ prepared_option =
100
+ if found_helper.equivalent.is_a?(Proc)
101
+ values.is_a?(Hash) ? found_helper.equivalent.call(**values) : found_helper.equivalent.call(values)
102
+ else
103
+ found_helper.equivalent
104
+ end
105
+
106
+ prepared_options.deep_merge!(prepared_option)
74
107
  end
75
108
 
76
109
  options.merge(prepared_options)
@@ -3,6 +3,25 @@
3
3
  module Servactory
4
4
  module Internals
5
5
  class Internal
6
+ class Work
7
+ attr_reader :name,
8
+ :types,
9
+ :inclusion
10
+
11
+ def initialize(internal)
12
+ @name = internal.name
13
+ @types = internal.types
14
+ @inclusion = internal.inclusion.slice(:in) if internal.inclusion_present?
15
+
16
+ define_singleton_method(:system_name) { internal.system_name }
17
+ define_singleton_method(:i18n_name) { internal.i18n_name }
18
+ # The methods below are required to support the internal work.
19
+ define_singleton_method(:input?) { false }
20
+ define_singleton_method(:internal?) { true }
21
+ define_singleton_method(:output?) { false }
22
+ end
23
+ end
24
+
6
25
  attr_reader :name,
7
26
  :collection_of_options
8
27
 
@@ -35,7 +54,10 @@ module Servactory
35
54
  end
36
55
 
37
56
  def register_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
57
+ advanced_helpers = options.except(*Servactory::Maintenance::Attributes::Options::Registrar::RESERVED_OPTIONS)
58
+
38
59
  options = apply_helpers_for_options(helpers: helpers, options: options) if helpers.present?
60
+ options = apply_helpers_for_options(helpers: advanced_helpers, options: options) if advanced_helpers.present?
39
61
 
40
62
  options_registrar = Servactory::Maintenance::Attributes::Options::Registrar.register(
41
63
  attribute: self,
@@ -54,15 +76,22 @@ module Servactory
54
76
  @collection_of_options = options_registrar.collection
55
77
  end
56
78
 
57
- def apply_helpers_for_options(helpers:, options:)
79
+ def apply_helpers_for_options(helpers:, options:) # rubocop:disable Metrics/MethodLength
58
80
  prepared_options = {}
59
81
 
60
- helpers.each do |helper|
82
+ helpers.each do |(helper, values)|
61
83
  found_helper = @option_helpers.find_by(name: helper)
62
84
 
63
85
  next if found_helper.blank?
64
86
 
65
- prepared_options.merge!(found_helper.equivalent)
87
+ prepared_option =
88
+ if found_helper.equivalent.is_a?(Proc)
89
+ values.is_a?(Hash) ? found_helper.equivalent.call(**values) : found_helper.equivalent.call(values)
90
+ else
91
+ found_helper.equivalent
92
+ end
93
+
94
+ prepared_options.deep_merge!(prepared_option)
66
95
  end
67
96
 
68
97
  options.merge(prepared_options)
@@ -5,6 +5,17 @@ module Servactory
5
5
  module Attributes
6
6
  module Options
7
7
  class Registrar # rubocop:disable Metrics/ClassLength
8
+ RESERVED_OPTIONS = %i[
9
+ type
10
+ required
11
+ default
12
+ collection
13
+ hash
14
+ inclusion
15
+ must
16
+ prepare
17
+ ].freeze
18
+
8
19
  DEFAULT_FEATURES = {
9
20
  required: false,
10
21
  types: false,
@@ -121,7 +132,10 @@ module Servactory
121
132
  define_methods: [
122
133
  Servactory::Maintenance::Attributes::DefineMethod.new(
123
134
  name: :collection_mode?,
124
- content: ->(**) { @collection_mode_class_names.include?(@options.fetch(:type)) }
135
+ content: lambda do |**|
136
+ @collection_mode_class_names.include?(@options.fetch(:type)) &&
137
+ @options.fetch(:consists_of, true) != false
138
+ end
125
139
  )
126
140
  ],
127
141
  define_conflicts: [
@@ -7,8 +7,8 @@ module Servactory
7
7
  module Must
8
8
  module_function
9
9
 
10
- def default_message
11
- lambda do |service_class_name:, value:, code:, input: nil, internal: nil, output: nil|
10
+ def default_message # rubocop:disable Metrics/MethodLength
11
+ lambda do |service_class_name:, value:, code:, input: nil, internal: nil, output: nil, reason: nil|
12
12
  attribute = Servactory::Utils.define_attribute_with(input: input, internal: internal, output: output)
13
13
 
14
14
  I18n.t(
@@ -16,7 +16,8 @@ module Servactory
16
16
  service_class_name: service_class_name,
17
17
  "#{attribute.system_name}_name": attribute.name,
18
18
  value: value,
19
- code: code
19
+ code: code,
20
+ reason: reason
20
21
  )
21
22
  end
22
23
  end