decouplio 1.0.0alpha

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 (136) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +53 -0
  3. data/.dockerignore +12 -0
  4. data/.gitignore +13 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +106 -0
  7. data/.rubocop_todo.yml +147 -0
  8. data/.ruby-version +1 -0
  9. data/.vscode/settings.json +3 -0
  10. data/Dockerfile +12 -0
  11. data/Gemfile +8 -0
  12. data/LICENSE +21 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +29 -0
  15. data/Rakefile +8 -0
  16. data/benchmarks/.ruby-version +1 -0
  17. data/benchmarks/Dockerfile +12 -0
  18. data/benchmarks/Gemfile +11 -0
  19. data/benchmarks/benchmarks.rb +527 -0
  20. data/bin/console +15 -0
  21. data/bin/setup +8 -0
  22. data/decouplio.gemspec +40 -0
  23. data/docker-compose.yml +18 -0
  24. data/docs/_config.yml +1 -0
  25. data/docs/benchmarks.md +1 -0
  26. data/docs/context.md +74 -0
  27. data/docs/context.rb +62 -0
  28. data/docs/doby.md +80 -0
  29. data/docs/doby.rb +38 -0
  30. data/docs/error_store.md +347 -0
  31. data/docs/error_store.rb +202 -0
  32. data/docs/fail.md +1016 -0
  33. data/docs/fail.rb +762 -0
  34. data/docs/index.md +25 -0
  35. data/docs/inner_action.md +63 -0
  36. data/docs/inner_action.rb +43 -0
  37. data/docs/logic_block.md +25 -0
  38. data/docs/octo.md +269 -0
  39. data/docs/octo.rb +164 -0
  40. data/docs/pass.md +309 -0
  41. data/docs/pass.rb +213 -0
  42. data/docs/quick_start.md +71 -0
  43. data/docs/quick_start.rb +38 -0
  44. data/docs/resq.md +263 -0
  45. data/docs/resq.rb +176 -0
  46. data/docs/step.md +737 -0
  47. data/docs/step.rb +526 -0
  48. data/docs/step_as_a_service.md +109 -0
  49. data/docs/step_as_a_service.rb +77 -0
  50. data/docs/wrap.md +232 -0
  51. data/docs/wrap.rb +137 -0
  52. data/lib/decouplio/action.rb +111 -0
  53. data/lib/decouplio/composer.rb +599 -0
  54. data/lib/decouplio/const/colors.rb +25 -0
  55. data/lib/decouplio/const/reserved_methods.rb +18 -0
  56. data/lib/decouplio/const/results.rb +13 -0
  57. data/lib/decouplio/const/types.rb +173 -0
  58. data/lib/decouplio/const/validations/action_option_class.rb +17 -0
  59. data/lib/decouplio/const/validations/common.rb +14 -0
  60. data/lib/decouplio/const/validations/fail.rb +34 -0
  61. data/lib/decouplio/const/validations/logic.rb +15 -0
  62. data/lib/decouplio/const/validations/octo.rb +44 -0
  63. data/lib/decouplio/const/validations/palp.rb +20 -0
  64. data/lib/decouplio/const/validations/pass.rb +32 -0
  65. data/lib/decouplio/const/validations/resq.rb +67 -0
  66. data/lib/decouplio/const/validations/step.rb +35 -0
  67. data/lib/decouplio/const/validations/wrap.rb +41 -0
  68. data/lib/decouplio/default_error_handler.rb +24 -0
  69. data/lib/decouplio/errors/action_class_error.rb +26 -0
  70. data/lib/decouplio/errors/base_error.rb +30 -0
  71. data/lib/decouplio/errors/error_store_error.rb +17 -0
  72. data/lib/decouplio/errors/extra_key_for_fail_error.rb +26 -0
  73. data/lib/decouplio/errors/extra_key_for_octo_error.rb +26 -0
  74. data/lib/decouplio/errors/extra_key_for_pass_error.rb +26 -0
  75. data/lib/decouplio/errors/extra_key_for_resq_error.rb +29 -0
  76. data/lib/decouplio/errors/extra_key_for_step_error.rb +23 -0
  77. data/lib/decouplio/errors/extra_key_for_wrap_error.rb +23 -0
  78. data/lib/decouplio/errors/fail_controversial_keys_error.rb +26 -0
  79. data/lib/decouplio/errors/fail_finish_him_error.rb +26 -0
  80. data/lib/decouplio/errors/fail_is_first_step_error.rb +18 -0
  81. data/lib/decouplio/errors/invalid_error_class_error.rb +29 -0
  82. data/lib/decouplio/errors/invalid_wrap_name_error.rb +23 -0
  83. data/lib/decouplio/errors/logic_is_not_defined_error.rb +23 -0
  84. data/lib/decouplio/errors/logic_redefinition_error.rb +23 -0
  85. data/lib/decouplio/errors/octo_block_is_not_defined_error.rb +18 -0
  86. data/lib/decouplio/errors/octo_controversial_keys_error.rb +26 -0
  87. data/lib/decouplio/errors/options_validation_error.rb +7 -0
  88. data/lib/decouplio/errors/palp_block_is_not_defined_error.rb +18 -0
  89. data/lib/decouplio/errors/palp_is_not_defined_error.rb +26 -0
  90. data/lib/decouplio/errors/palp_validation_error.rb +21 -0
  91. data/lib/decouplio/errors/pass_controversial_keys_error.rb +26 -0
  92. data/lib/decouplio/errors/pass_finish_him_error.rb +26 -0
  93. data/lib/decouplio/errors/required_options_is_missing_for_octo_error.rb +25 -0
  94. data/lib/decouplio/errors/resq_definition_error.rb +21 -0
  95. data/lib/decouplio/errors/resq_error_class_error.rb +29 -0
  96. data/lib/decouplio/errors/resq_handler_method_error.rb +26 -0
  97. data/lib/decouplio/errors/step_controversial_keys_error.rb +26 -0
  98. data/lib/decouplio/errors/step_finish_him_error.rb +26 -0
  99. data/lib/decouplio/errors/step_is_not_defined_for_fail_error.rb +26 -0
  100. data/lib/decouplio/errors/step_is_not_defined_for_step_error.rb +27 -0
  101. data/lib/decouplio/errors/step_is_not_defined_for_wrap_error.rb +26 -0
  102. data/lib/decouplio/errors/step_name_error.rb +20 -0
  103. data/lib/decouplio/errors/wrap_block_is_not_defined_error.rb +18 -0
  104. data/lib/decouplio/errors/wrap_controversial_keys_error.rb +26 -0
  105. data/lib/decouplio/errors/wrap_finish_him_error.rb +26 -0
  106. data/lib/decouplio/errors/wrap_klass_method_error.rb +23 -0
  107. data/lib/decouplio/flow.rb +17 -0
  108. data/lib/decouplio/logic_dsl.rb +96 -0
  109. data/lib/decouplio/octo_hash_case.rb +31 -0
  110. data/lib/decouplio/octo_options_validator.rb +89 -0
  111. data/lib/decouplio/options_validator.rb +566 -0
  112. data/lib/decouplio/processor.rb +20 -0
  113. data/lib/decouplio/steps/base_resq.rb +31 -0
  114. data/lib/decouplio/steps/base_step.rb +16 -0
  115. data/lib/decouplio/steps/doby.rb +32 -0
  116. data/lib/decouplio/steps/fail.rb +50 -0
  117. data/lib/decouplio/steps/if_condition_fail.rb +28 -0
  118. data/lib/decouplio/steps/if_condition_pass.rb +26 -0
  119. data/lib/decouplio/steps/inner_action_fail.rb +55 -0
  120. data/lib/decouplio/steps/inner_action_pass.rb +38 -0
  121. data/lib/decouplio/steps/inner_action_step.rb +51 -0
  122. data/lib/decouplio/steps/octo.rb +22 -0
  123. data/lib/decouplio/steps/pass.rb +35 -0
  124. data/lib/decouplio/steps/resq_fail.rb +10 -0
  125. data/lib/decouplio/steps/resq_pass.rb +10 -0
  126. data/lib/decouplio/steps/service_fail.rb +51 -0
  127. data/lib/decouplio/steps/service_pass.rb +36 -0
  128. data/lib/decouplio/steps/service_step.rb +47 -0
  129. data/lib/decouplio/steps/step.rb +43 -0
  130. data/lib/decouplio/steps/unless_condition_fail.rb +28 -0
  131. data/lib/decouplio/steps/unless_condition_pass.rb +26 -0
  132. data/lib/decouplio/steps/wrap.rb +62 -0
  133. data/lib/decouplio/validators/condition.rb +49 -0
  134. data/lib/decouplio/version.rb +5 -0
  135. data/lib/decouplio.rb +7 -0
  136. metadata +266 -0
@@ -0,0 +1,566 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'const/types'
4
+ require_relative 'const/reserved_methods'
5
+ require_relative 'errors/action_class_error'
6
+ require_relative 'errors/extra_key_for_step_error'
7
+ require_relative 'errors/extra_key_for_fail_error'
8
+ require_relative 'errors/extra_key_for_octo_error'
9
+ require_relative 'errors/extra_key_for_pass_error'
10
+ require_relative 'errors/extra_key_for_resq_error'
11
+ require_relative 'errors/extra_key_for_wrap_error'
12
+ require_relative 'errors/fail_finish_him_error'
13
+ require_relative 'errors/invalid_error_class_error'
14
+ require_relative 'errors/invalid_wrap_name_error'
15
+ require_relative 'errors/pass_finish_him_error'
16
+ require_relative 'errors/required_options_is_missing_for_octo_error'
17
+ require_relative 'errors/resq_error_class_error'
18
+ require_relative 'errors/resq_handler_method_error'
19
+ require_relative 'errors/step_finish_him_error'
20
+ require_relative 'errors/step_is_not_defined_for_step_error'
21
+ require_relative 'errors/step_is_not_defined_for_fail_error'
22
+ require_relative 'errors/step_is_not_defined_for_wrap_error'
23
+ require_relative 'errors/wrap_finish_him_error'
24
+ require_relative 'errors/wrap_klass_method_error'
25
+ require_relative 'errors/step_controversial_keys_error'
26
+ require_relative 'errors/fail_controversial_keys_error'
27
+ require_relative 'errors/pass_controversial_keys_error'
28
+ require_relative 'errors/octo_controversial_keys_error'
29
+ require_relative 'errors/wrap_controversial_keys_error'
30
+ require_relative 'errors/palp_is_not_defined_error'
31
+ require_relative 'errors/error_store_error'
32
+ require_relative 'errors/step_name_error'
33
+
34
+ module Decouplio
35
+ class OptionsValidator
36
+ def initialize(flow:, palps:, next_steps:, action_class:)
37
+ @flow = flow
38
+ @palps = palps
39
+ @next_steps = next_steps
40
+ @action_class = action_class
41
+ end
42
+
43
+ def call
44
+ @flow.each_with_index do |(_step_id, options), index|
45
+ step_names = extract_step_names(flow: @flow.to_a[index..].to_h.merge(@next_steps || {}))
46
+
47
+ validate(options: options, step_names: step_names)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def validate(options:, step_names:)
54
+ filtered_options = options.reject { |key, _val| OPTIONS_TO_FILTER.include?(key) }
55
+
56
+ validate_name(name: options[:name])
57
+ @palps.each_key do |palp_name|
58
+ validate_name(name: palp_name)
59
+ end
60
+
61
+ case options[:type]
62
+ when Decouplio::Const::Types::STEP_TYPE
63
+ validate_step(options: filtered_options, step_names: step_names)
64
+ when Decouplio::Const::Types::FAIL_TYPE
65
+ validate_fail(options: filtered_options, step_names: step_names)
66
+ when Decouplio::Const::Types::PASS_TYPE
67
+ validate_pass(options: filtered_options)
68
+ when Decouplio::Const::Types::OCTO_TYPE
69
+ validate_octo(options: filtered_options, hash_case: options[:hash_case])
70
+ when Decouplio::Const::Types::WRAP_TYPE
71
+ validate_wrap(options: filtered_options, name: options[:name], step_names: step_names)
72
+ when Decouplio::Const::Types::ACTION_TYPE_STEP
73
+ validate_action(action_class: options[:action], type: Decouplio::Const::Types::STEP_TYPE)
74
+ validate_error_store(parent_action_class: @action_class, child_action_class: options[:action])
75
+ validate_step(options: filtered_options, step_names: step_names)
76
+ when Decouplio::Const::Types::ACTION_TYPE_FAIL
77
+ validate_action(action_class: options[:action], type: Decouplio::Const::Types::FAIL_TYPE)
78
+ validate_error_store(parent_action_class: @action_class, child_action_class: options[:action])
79
+ validate_fail(options: filtered_options, step_names: step_names)
80
+ when Decouplio::Const::Types::ACTION_TYPE_PASS
81
+ validate_action(action_class: options[:action], type: Decouplio::Const::Types::PASS_TYPE)
82
+ validate_error_store(parent_action_class: @action_class, child_action_class: options[:action])
83
+ validate_pass(options: filtered_options)
84
+ when Decouplio::Const::Types::RESQ_TYPE_STEP,
85
+ Decouplio::Const::Types::RESQ_TYPE_FAIL,
86
+ Decouplio::Const::Types::RESQ_TYPE_PASS
87
+ validate(options: options[:step_to_resq], step_names: step_names)
88
+ validate_resq(options: filtered_options)
89
+ end
90
+ end
91
+
92
+ def validate_action(action_class:, type:)
93
+ return if action_class.is_a?(Class) && action_class <= Decouplio::Action
94
+
95
+ raise Decouplio::Errors::ActionClassError.new(
96
+ step_type: type,
97
+ errored_option: "action: #{action_class}"
98
+ )
99
+ end
100
+
101
+ def validate_step(options:, step_names:)
102
+ check_step_presence_for_step(options: options, step_names: step_names)
103
+ check_step_controversial_keys(options: options)
104
+ check_step_extra_keys(options: options)
105
+ check_step_finish_him(options: options)
106
+ end
107
+
108
+ def validate_fail(options:, step_names:)
109
+ check_step_presence_for_fail(options: options, step_names: step_names)
110
+ check_fail_controversial_keys(options: options)
111
+ check_fail_extra_keys(options: options)
112
+ check_fail_finish_him(options: options)
113
+ end
114
+
115
+ def validate_pass(options:)
116
+ check_pass_extra_keys(options: options)
117
+ check_pass_finish_him(options: options)
118
+ end
119
+
120
+ def validate_octo(options:, hash_case:)
121
+ check_octo_required_keys(options: options)
122
+ check_octo_extra_keys(options: options)
123
+ check_octo_palps(hash_case: hash_case)
124
+ end
125
+
126
+ def validate_wrap(options:, name:, step_names:)
127
+ check_wrap_name(name: name)
128
+ check_wrap_controversial_keys(options: options)
129
+ check_step_presence_for_wrap(options: options, step_names: step_names)
130
+ check_wrap_extra_keys(options: options)
131
+ check_wrap_finish_him(options: options)
132
+ check_wrap_klass_method_presence(options: options)
133
+ end
134
+
135
+ def validate_resq(options:)
136
+ options[:handler_hash].each_value do |error_handler_name|
137
+ validate_name(name: error_handler_name)
138
+ end
139
+ check_resq_extra_keys(options: options)
140
+ check_resq_handler_method_is_a_symbol(options: options)
141
+ check_resq_error_classes(options: options)
142
+ check_resq_exception_classes_inheritance(options: options)
143
+ end
144
+
145
+ def validate_name(name:)
146
+ return unless Decouplio::Const::ReservedMethods::NAMES.include?(name)
147
+
148
+ raise Decouplio::Errors::StepNameError.new(
149
+ errored_option: name
150
+ )
151
+ end
152
+
153
+ def check_step_presence_for_step(options:, step_names:)
154
+ options.slice(*STEP_CHECK_STEP_PRESENCE).each do |option_key, option_value|
155
+ next if %i[on_success on_failure].include?(option_key) && option_value == :finish_him
156
+
157
+ next if step_names.keys.include?(option_value)
158
+
159
+ raise Decouplio::Errors::StepIsNotDefinedForStepError.new(
160
+ errored_option: options.slice(option_key).to_s,
161
+ details: option_value
162
+ )
163
+ end
164
+ end
165
+
166
+ def check_step_presence_for_fail(options:, step_names:)
167
+ options.slice(*STEP_CHECK_STEP_PRESENCE).each do |option_key, option_value|
168
+ next if %i[on_success on_failure].include?(option_key) && option_value == :finish_him
169
+
170
+ next if step_names.keys.include?(option_value)
171
+
172
+ raise Decouplio::Errors::StepIsNotDefinedForFailError.new(
173
+ errored_option: options.slice(option_key).to_s,
174
+ details: option_value
175
+ )
176
+ end
177
+ end
178
+
179
+ def check_step_presence_for_wrap(options:, step_names:)
180
+ options.slice(*WRAP_CHECK_STEP_PRESENCE).each do |option_key, option_value|
181
+ next if %i[on_success on_failure].include?(option_key) && option_value == :finish_him
182
+
183
+ next if step_names.keys.include?(option_value)
184
+
185
+ raise Decouplio::Errors::StepIsNotDefinedForWrapError.new(
186
+ errored_option: options.slice(option_key).to_s,
187
+ details: option_value
188
+ )
189
+ end
190
+ end
191
+
192
+ def check_step_extra_keys(options:)
193
+ extra_keys = options.keys - STEP_ALLOWED_OPTIONS
194
+
195
+ if extra_keys.size.positive?
196
+ raise Decouplio::Errors::ExtraKeyForStepError.new(
197
+ errored_option: options.slice(*extra_keys).to_s
198
+ )
199
+ end
200
+ end
201
+
202
+ def check_step_controversial_keys(options:)
203
+ on_success_on_failure = options.slice(:on_success, :on_failure)
204
+ finish_him = options.slice(:finish_him)
205
+
206
+ if !on_success_on_failure.empty? && !finish_him.empty?
207
+ raise Decouplio::Errors::StepControversialKeysError.new(
208
+ errored_option: on_success_on_failure.merge(finish_him).to_s,
209
+ details: [on_success_on_failure.keys.join(', '), :finish_him]
210
+ )
211
+ end
212
+ end
213
+
214
+ def validate_error_store(parent_action_class:, child_action_class:)
215
+ return if parent_action_class.error_store == child_action_class.error_store
216
+
217
+ raise Decouplio::Errors::ErrorStoreError
218
+ end
219
+
220
+ def check_fail_extra_keys(options:)
221
+ extra_keys = options.keys - FAIL_ALLOWED_OPTIONS
222
+
223
+ return if extra_keys.size.zero?
224
+
225
+ raise Decouplio::Errors::ExtraKeyForFailError.new(
226
+ errored_option: options.slice(*extra_keys).to_s,
227
+ details: extra_keys.join(', ')
228
+ )
229
+ end
230
+
231
+ def check_fail_controversial_keys(options:)
232
+ on_success_on_failure = options.slice(:on_success, :on_failure)
233
+ finish_him = options.slice(:finish_him)
234
+
235
+ if !on_success_on_failure.empty? && !finish_him.empty?
236
+ raise Decouplio::Errors::FailControversialKeysError.new(
237
+ errored_option: on_success_on_failure.merge(finish_him).to_s,
238
+ details: [on_success_on_failure.keys.join(', '), :finish_him]
239
+ )
240
+ end
241
+ end
242
+
243
+ def check_pass_extra_keys(options:)
244
+ extra_keys = options.keys - PASS_ALLOWED_OPTIONS
245
+
246
+ if extra_keys.size.positive?
247
+ raise Decouplio::Errors::ExtraKeyForPassError.new(
248
+ errored_option: options.slice(*extra_keys).to_s,
249
+ details: extra_keys.join(', ')
250
+ )
251
+ end
252
+ end
253
+
254
+ def check_octo_extra_keys(options:)
255
+ extra_keys = options.keys - OCTO_ALLOWED_OPTIONS
256
+
257
+ if extra_keys.size.positive?
258
+ raise Decouplio::Errors::ExtraKeyForOctoError.new(
259
+ errored_option: options.slice(*extra_keys).to_s,
260
+ details: extra_keys.join(', ')
261
+ )
262
+ end
263
+ end
264
+
265
+ def check_wrap_extra_keys(options:)
266
+ extra_keys = options.keys - WRAP_ALLOWED_OPTIONS
267
+
268
+ if extra_keys.size.positive?
269
+ raise Decouplio::Errors::ExtraKeyForWrapError.new(
270
+ errored_option: options.slice(*extra_keys).to_s
271
+ )
272
+ end
273
+ end
274
+
275
+ def check_wrap_controversial_keys(options:)
276
+ on_success_on_failure = options.slice(:on_success, :on_failure)
277
+ finish_him = options.slice(:finish_him)
278
+
279
+ if !on_success_on_failure.empty? && !finish_him.empty?
280
+ raise Decouplio::Errors::WrapControversialKeysError.new(
281
+ errored_option: on_success_on_failure.merge(finish_him).to_s,
282
+ details: [on_success_on_failure.keys.join(', '), :finish_him]
283
+ )
284
+ end
285
+ end
286
+
287
+ def check_octo_palps(hash_case:)
288
+ hash_casse_palps = hash_case.map { |_octo_key, hash| hash[:palp] }.compact
289
+ undefined_palps = hash_casse_palps - @palps.keys
290
+
291
+ return if undefined_palps.empty?
292
+
293
+ errored_options = hash_case.select do |_key, val|
294
+ undefined_palps.include?(val[:palp])
295
+ end
296
+ errored_options = errored_options.map do |key, val|
297
+ "\"on :#{key}, palp: :#{val[:palp]}\""
298
+ end
299
+
300
+ raise Decouplio::Errors::PalpIsNotDefinedError.new(
301
+ errored_option: errored_options,
302
+ details: undefined_palps
303
+ )
304
+ end
305
+
306
+ def check_octo_required_keys(options:)
307
+ return if (OCTO_REQUIRED_KEYS - options.keys).size.zero?
308
+
309
+ raise Decouplio::Errors::RequiredOptionsIsMissingForOctoError.new(
310
+ details: OCTO_REQUIRED_KEYS.join(', ')
311
+ )
312
+ end
313
+
314
+ def check_step_finish_him(options:)
315
+ finish_him_value = options.dig(:finish_him)
316
+
317
+ return unless finish_him_value && options.key?(:finish_him)
318
+
319
+ unless ALLOWED_STEP_FINISH_HIM_VALUES.include?(finish_him_value)
320
+ raise Decouplio::Errors::StepFinishHimError.new(
321
+ errored_option: options.slice(:finish_him).to_s,
322
+ details: finish_him_value
323
+ )
324
+ end
325
+ end
326
+
327
+ def check_fail_finish_him(options:)
328
+ finish_him_value = options.dig(:finish_him)
329
+
330
+ return unless options.key?(:finish_him)
331
+
332
+ return if ALLOWED_FAIL_FINISH_HIM_VALUES.include?(finish_him_value)
333
+
334
+ raise Decouplio::Errors::FailFinishHimError.new(
335
+ errored_option: options.slice(:finish_him).to_s,
336
+ details: finish_him_value
337
+ )
338
+ end
339
+
340
+ def check_pass_finish_him(options:)
341
+ finish_him_value = options.dig(:finish_him)
342
+
343
+ return unless finish_him_value && options.key?(:finish_him)
344
+
345
+ unless ALLOWED_PASS_FINISH_HIM_VALUES.include?(finish_him_value)
346
+ raise Decouplio::Errors::PassFinishHimError.new(
347
+ errored_option: options.slice(:finish_him).to_s,
348
+ details: finish_him_value
349
+ )
350
+ end
351
+ end
352
+
353
+ def check_wrap_finish_him(options:)
354
+ finish_him_value = options.dig(:finish_him)
355
+
356
+ return unless finish_him_value && options.key?(:finish_him)
357
+
358
+ unless ALLOWED_WRAP_FINISH_HIM_VALUES.include?(finish_him_value)
359
+ raise Decouplio::Errors::WrapFinishHimError.new(
360
+ errored_option: options.slice(:finish_him).to_s,
361
+ details: finish_him_value
362
+ )
363
+ end
364
+ end
365
+
366
+ def check_wrap_klass_method_presence(options:)
367
+ klass_method_options = options.slice(:klass, :method)
368
+ klass_method_options_size = klass_method_options.size
369
+
370
+ return if klass_method_options_size.zero? || klass_method_options_size == 2
371
+
372
+ raise Decouplio::Errors::WrapKlassMethodError.new(
373
+ errored_option: klass_method_options.to_s
374
+ )
375
+ end
376
+
377
+ def check_wrap_name(name:)
378
+ return if name.is_a?(Symbol)
379
+
380
+ raise Decouplio::Errors::InvalidWrapNameError
381
+ end
382
+
383
+ def check_resq_extra_keys(options:)
384
+ extra_options = options[:handler_hash].values.select do |option|
385
+ RESQ_NOT_ALLOWED_OPTIONS.include?(option)
386
+ end
387
+
388
+ return if extra_options.empty?
389
+
390
+ raise Decouplio::Errors::ExtraKeyForResqError.new(
391
+ errored_option: extra_options.join(', ').to_s,
392
+ details: extra_options.join(', ').to_s
393
+ )
394
+ end
395
+
396
+ def check_resq_handler_method_is_a_symbol(options:)
397
+ options[:handler_hash].values.uniq.each do |handler_method|
398
+ next if handler_method.is_a?(Symbol)
399
+
400
+ raise Decouplio::Errors::ResqHandlerMethodError.new(
401
+ errored_option: handler_method.to_s
402
+ )
403
+ end
404
+ end
405
+
406
+ def check_resq_error_classes(options:)
407
+ options[:handler_hash].each_key do |error_class|
408
+ next if error_class.is_a?(Class)
409
+
410
+ raise Decouplio::Errors::ResqErrorClassError.new(
411
+ errored_option: error_class.to_s,
412
+ details: options[:handler_hash][error_class].to_s
413
+ )
414
+ end
415
+ end
416
+
417
+ def check_resq_exception_classes_inheritance(options:)
418
+ options[:handler_hash].each_key do |klass|
419
+ next if klass < Exception
420
+
421
+ raise Decouplio::Errors::InvalidErrorClassError.new(
422
+ errored_option: klass.to_s,
423
+ details: klass.to_s
424
+ )
425
+ end
426
+ end
427
+
428
+ def extract_step_names(flow:)
429
+ flow.to_h do |_step_id, stp|
430
+ [stp[:name], stp[:type]]
431
+ end
432
+ end
433
+
434
+ OPTIONS_TO_FILTER = %i[
435
+ step_id
436
+ flow
437
+ name
438
+ type
439
+ hash_case
440
+ wrap_flow
441
+ step_to_resq
442
+ ].freeze
443
+
444
+ # *************************************************
445
+ # STEP
446
+ # *************************************************
447
+
448
+ STEP_CHECK_METHOD_EXISTENCE_OPTIONS = %i[
449
+ on_success
450
+ on_failure
451
+ if
452
+ unless
453
+ ].freeze
454
+ STEP_CHECK_STEP_PRESENCE = %i[
455
+ on_success
456
+ on_failure
457
+ ].freeze
458
+ STEP_ALLOWED_OPTIONS = %i[
459
+ on_success
460
+ on_failure
461
+ finish_him
462
+ if
463
+ unless
464
+ action
465
+ ].freeze
466
+ ALLOWED_STEP_FINISH_HIM_VALUES = %i[
467
+ on_success
468
+ on_failure
469
+ ].freeze
470
+
471
+ # *************************************************
472
+ # FAIL
473
+ # *************************************************
474
+
475
+ FAIL_CHECK_METHOD_EXISTENCE_OPTIONS = %i[
476
+ if
477
+ unless
478
+ ].freeze
479
+ FAIL_ALLOWED_OPTIONS = %i[
480
+ on_success
481
+ on_failure
482
+ finish_him
483
+ if
484
+ unless
485
+ action
486
+ ].freeze
487
+ ALLOWED_FAIL_FINISH_HIM_VALUES = [true, :on_success, :on_failure].freeze
488
+
489
+ # *************************************************
490
+ # PASS
491
+ # *************************************************
492
+
493
+ PASS_ALLOWED_OPTIONS = %i[
494
+ finish_him
495
+ if
496
+ unless
497
+ action
498
+ ].freeze
499
+
500
+ PASS_CHECK_METHOD_EXISTENCE_OPTIONS = %i[
501
+ if
502
+ unless
503
+ ].freeze
504
+ ALLOWED_PASS_FINISH_HIM_VALUES = [true].freeze
505
+
506
+ # *************************************************
507
+ # OCTO
508
+ # *************************************************
509
+
510
+ OCTO_ALLOWED_OPTIONS = %i[
511
+ ctx_key
512
+ if
513
+ unless
514
+ ].freeze
515
+ OCTO_CHECK_METHOD_EXISTENCE_OPTIONS = %i[
516
+ if
517
+ unless
518
+ ].freeze
519
+ OCTO_REQUIRED_KEYS = %i[
520
+ ctx_key
521
+ ].freeze
522
+
523
+ # *************************************************
524
+ # WRAP
525
+ # *************************************************
526
+
527
+ WRAP_CHECK_METHOD_EXISTENCE_OPTIONS = %i[
528
+ on_success
529
+ on_failure
530
+ if
531
+ unless
532
+ ].freeze
533
+ WRAP_CHECK_STEP_PRESENCE = %i[
534
+ on_success
535
+ on_failure
536
+ ].freeze
537
+ WRAP_ALLOWED_OPTIONS = %i[
538
+ on_success
539
+ on_failure
540
+ finish_him
541
+ if
542
+ unless
543
+ klass
544
+ method
545
+ wrap_block
546
+ ].freeze
547
+ ALLOWED_WRAP_FINISH_HIM_VALUES = %i[
548
+ on_success
549
+ on_failure
550
+ ].freeze
551
+
552
+ # *************************************************
553
+ # RESQ
554
+ # *************************************************
555
+
556
+ RESQ_NOT_ALLOWED_OPTIONS = %i[
557
+ on_success
558
+ on_failure
559
+ finish_him
560
+ if
561
+ unless
562
+ klass
563
+ method
564
+ ].freeze
565
+ end
566
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decouplio
4
+ class Processor
5
+ class << self
6
+ def call(first_step:, steps_pool:, steps_flow:, instance:)
7
+ process(first_step, instance, steps_pool, steps_flow)
8
+ end
9
+
10
+ private
11
+
12
+ def process(next_step_name, instance, steps_pool, steps_flow)
13
+ while next_step_name
14
+ result = steps_pool[next_step_name].process(instance: instance)
15
+ next_step_name = steps_flow[next_step_name][result]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_step'
4
+
5
+ module Decouplio
6
+ module Steps
7
+ class BaseResq < BaseStep
8
+ def initialize(handler_hash:, step_to_resq:)
9
+ super()
10
+ @handler_hash = handler_hash
11
+ @step_to_resq = step_to_resq
12
+ end
13
+
14
+ def process(instance:)
15
+ result = @step_to_resq.process(instance: instance)
16
+ rescue *@handler_hash.keys => e
17
+ handler_method = @handler_hash[e.class]
18
+
19
+ raise e unless handler_method
20
+
21
+ instance.append_railway_flow(handler_method)
22
+ instance.public_send(handler_method, e, **instance.ctx)
23
+
24
+ instance.fail_action
25
+ Decouplio::Const::Results::ERROR
26
+ else
27
+ result
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../const/results'
4
+
5
+ module Decouplio
6
+ module Steps
7
+ class BaseStep
8
+ attr_reader :name
9
+
10
+ def process(*)
11
+ raise NotImplementedError,
12
+ 'Please implement process method'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_step'
4
+
5
+ module Decouplio
6
+ module Steps
7
+ class Doby < Decouplio::Steps::BaseStep
8
+ def initialize(name:, doby_class:, doby_options:)
9
+ super()
10
+ @name = name
11
+ @doby_class = doby_class
12
+ @doby_options = doby_options
13
+ end
14
+
15
+ def process(instance:)
16
+ instance.append_railway_flow(@name)
17
+ result = @doby_class.call(ctx: instance.ctx, **@doby_options)
18
+ resolve(result: result, instance: instance)
19
+ end
20
+
21
+ def resolve(result:, instance:)
22
+ if result
23
+ instance.pass_action
24
+ Decouplio::Const::Results::PASS
25
+ else
26
+ instance.fail_action
27
+ Decouplio::Const::Results::FAIL
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end