convenient_service 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/lib/convenient_service/commands/is_service.rb +28 -0
  4. data/lib/convenient_service/commands/is_service_class.rb +30 -0
  5. data/lib/convenient_service/commands.rb +4 -0
  6. data/lib/convenient_service/common/plugins.rb +2 -3
  7. data/lib/convenient_service/core/concern/class_methods.rb +3 -0
  8. data/lib/convenient_service/core/entities/config/entities/concerns/entities/stack.rb +22 -0
  9. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/container/concern/instance_methods.rb +1 -1
  10. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware_creators/observable/entities/event.rb +13 -15
  11. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middlewares/chain/commands/normalize_env.rb +55 -0
  12. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middlewares/chain/commands.rb +1 -0
  13. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middlewares/chain/concern/instance_methods.rb +3 -3
  14. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middlewares/classic.rb +14 -0
  15. data/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/stack.rb +22 -0
  16. data/lib/convenient_service/dependencies/built_in.rb +0 -7
  17. data/lib/convenient_service/dependencies/extractions/ds.rb +45 -0
  18. data/lib/convenient_service/dependencies/only_queries.rb +29 -0
  19. data/lib/convenient_service/dependencies/queries/gems/active_model.rb +38 -0
  20. data/lib/convenient_service/dependencies/queries/gems/logger.rb +39 -0
  21. data/lib/convenient_service/dependencies/queries/gems/paint.rb +39 -0
  22. data/lib/convenient_service/dependencies/queries/gems/rspec.rb +60 -0
  23. data/lib/convenient_service/dependencies/queries/ruby.rb +103 -0
  24. data/lib/convenient_service/dependencies/queries/version/null_version.rb +87 -0
  25. data/lib/convenient_service/dependencies/queries/version.rb +87 -0
  26. data/lib/convenient_service/dependencies/queries.rb +536 -0
  27. data/lib/convenient_service/dependencies.rb +2 -438
  28. data/lib/convenient_service/feature/configs/standard.rb +0 -2
  29. data/lib/convenient_service/service/configs/amazing_print_inspect/aliases.rb +7 -0
  30. data/lib/convenient_service/service/configs/amazing_print_inspect.rb +61 -0
  31. data/lib/convenient_service/service/configs/awesome_print_inspect.rb +1 -1
  32. data/lib/convenient_service/service/configs/{minimal.rb → essential.rb} +2 -107
  33. data/lib/convenient_service/service/configs/standard/v1.rb +5 -10
  34. data/lib/convenient_service/service/configs/standard.rb +19 -31
  35. data/lib/convenient_service/service/configs.rb +1 -1
  36. data/lib/convenient_service/service/plugins/can_have_connected_steps/entities/expressions/and.rb +4 -1
  37. data/lib/convenient_service/service/plugins/can_have_connected_steps/entities/expressions/empty.rb +4 -4
  38. data/lib/convenient_service/service/plugins/can_have_connected_steps/entities/step_collection.rb +7 -0
  39. data/lib/convenient_service/service/plugins/can_have_fallbacks/concern.rb +18 -0
  40. data/lib/convenient_service/service/plugins/can_have_fallbacks/exceptions.rb +22 -4
  41. data/lib/convenient_service/service/plugins/can_have_fallbacks/middleware.rb +3 -3
  42. data/lib/convenient_service/service/plugins/can_have_rollbacks/middleware.rb +66 -0
  43. data/lib/convenient_service/service/plugins/can_have_rollbacks.rb +3 -0
  44. data/lib/convenient_service/service/plugins/can_have_sequential_steps/entities/step_collection.rb +7 -0
  45. data/lib/convenient_service/service/plugins/can_have_steps/commands/is_step.rb +34 -0
  46. data/lib/convenient_service/service/plugins/can_have_steps/commands.rb +1 -0
  47. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/concern/instance_methods.rb +22 -14
  48. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/can_be_method_step/can_be_executed/middleware.rb +1 -15
  49. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/can_have_fallbacks/exceptions.rb +53 -0
  50. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/can_have_fallbacks/middleware.rb +60 -15
  51. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/can_have_fallbacks.rb +1 -0
  52. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/has_amazing_print_inspect/concern.rb +46 -0
  53. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/has_amazing_print_inspect.rb +3 -0
  54. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/has_awesome_print_inspect/concern.rb +3 -2
  55. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/has_result/middleware.rb +26 -1
  56. data/lib/convenient_service/service/plugins/can_have_steps.rb +41 -0
  57. data/lib/convenient_service/service/plugins/can_have_stubbed_results/commands/fetch_all_services_stubbed_results_cache.rb +2 -2
  58. data/lib/convenient_service/service/plugins/forbids_convenient_service_entities_as_constructor_arguments/exceptions.rb +53 -0
  59. data/lib/convenient_service/service/plugins/forbids_convenient_service_entities_as_constructor_arguments/middleware.rb +108 -0
  60. data/lib/convenient_service/service/plugins/forbids_convenient_service_entities_as_constructor_arguments.rb +4 -0
  61. data/lib/convenient_service/service/plugins/has_amazing_print_inspect/concern.rb +39 -0
  62. data/lib/convenient_service/service/plugins/has_amazing_print_inspect.rb +3 -0
  63. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/can_be_from_exception/concern.rb +32 -0
  64. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/can_be_from_exception.rb +3 -0
  65. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/can_have_step/concern.rb +7 -0
  66. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_amazing_print_inspect/concern.rb +41 -0
  67. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_amazing_print_inspect.rb +3 -0
  68. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_awesome_print_inspect/concern.rb +2 -2
  69. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/concern/instance_methods.rb +0 -140
  70. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/code/plugins/has_amazing_print_inspect/concern.rb +46 -0
  71. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/code/plugins/has_amazing_print_inspect.rb +3 -0
  72. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/data/plugins/has_amazing_print_inspect/concern.rb +46 -0
  73. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/data/plugins/has_amazing_print_inspect.rb +3 -0
  74. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/message/plugins/has_amazing_print_inspect/concern.rb +46 -0
  75. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/message/plugins/has_amazing_print_inspect.rb +3 -0
  76. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/status/plugins/has_amazing_print_inspect/concern.rb +46 -0
  77. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/has_j_send_status_and_attributes/entities/status/plugins/has_amazing_print_inspect.rb +3 -0
  78. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/helps_to_learn_similarities_with_common_objects/concern/instance_methods.rb +161 -0
  79. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/helps_to_learn_similarities_with_common_objects/concern.rb +27 -0
  80. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/helps_to_learn_similarities_with_common_objects/exceptions.rb +34 -0
  81. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins/helps_to_learn_similarities_with_common_objects.rb +4 -0
  82. data/lib/convenient_service/service/plugins/has_j_send_result/entities/result/plugins.rb +2 -0
  83. data/lib/convenient_service/service/plugins/has_mermaid_flowchart/concern.rb +6 -1
  84. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/middleware.rb +1 -0
  85. data/lib/convenient_service/service/plugins.rb +3 -1
  86. data/lib/convenient_service/support/counter.rb +2 -0
  87. data/lib/convenient_service/support/middleware/stack_builder.rb +37 -0
  88. data/lib/convenient_service/support/thread_safe_counter.rb +1 -0
  89. data/lib/convenient_service/support.rb +0 -3
  90. data/lib/convenient_service/utils/object/get_own_method.rb +54 -0
  91. data/lib/convenient_service/utils/object/safe_send.rb +84 -0
  92. data/lib/convenient_service/utils/object.rb +24 -0
  93. data/lib/convenient_service/utils/string/enclose.rb +52 -0
  94. data/lib/convenient_service/utils/string.rb +5 -0
  95. data/lib/convenient_service/version.rb +1 -1
  96. data/lib/convenient_service.rb +62 -0
  97. metadata +149 -16
  98. data/lib/convenient_service/common/plugins/normalizes_env/middleware.rb +0 -31
  99. data/lib/convenient_service/common/plugins/normalizes_env.rb +0 -3
  100. data/lib/convenient_service/service/plugins/can_have_steps/entities/step/plugins/can_have_fallbacks/result.rb +0 -3
  101. data/lib/convenient_service/services.rb +0 -4
  102. data/lib/convenient_service/support/gems/active_model.rb +0 -36
  103. data/lib/convenient_service/support/gems/logger.rb +0 -37
  104. data/lib/convenient_service/support/gems/paint.rb +0 -37
  105. data/lib/convenient_service/support/gems/rspec.rb +0 -58
  106. data/lib/convenient_service/support/ruby.rb +0 -53
  107. data/lib/convenient_service/support/version/null_version.rb +0 -85
  108. data/lib/convenient_service/support/version.rb +0 -75
  109. /data/lib/convenient_service/{support → dependencies/queries}/gems.rb +0 -0
@@ -193,6 +193,24 @@ module ConvenientService
193
193
  outputs.find { |output| output.reassignment?(name) }
194
194
  end
195
195
 
196
+ ##
197
+ # @api private
198
+ #
199
+ # @return [String]
200
+ #
201
+ def printable_container
202
+ Utils::Class.display_name(container.klass)
203
+ end
204
+
205
+ ##
206
+ # @api private
207
+ #
208
+ # @return [String]
209
+ #
210
+ def printable_service
211
+ Utils::Class.display_name(service.klass)
212
+ end
213
+
196
214
  ##
197
215
  # @api private
198
216
  #
@@ -207,16 +225,7 @@ module ConvenientService
207
225
  # NOTE: `service_result` returns a foreign result that is later converted to own result by `HasResult` middleware.
208
226
  #
209
227
  def service_result
210
- service.klass.result(**input_values)
211
- end
212
-
213
- ##
214
- # @api private
215
- #
216
- # @return [String]
217
- #
218
- def printable_service
219
- service.klass.to_s
228
+ @service_result ||= service.klass.result(**input_values)
220
229
  end
221
230
 
222
231
  ##
@@ -399,11 +408,10 @@ module ConvenientService
399
408
  # TODO: Create `OutputMethod`. Move `validate_as_output_for_result` into it.
400
409
  #
401
410
  def calculate_output_values
402
- return {} unless status.unsafe_success?
403
-
404
- outputs.each { |output| ::ConvenientService.raise Exceptions::StepResultDataNotExistingAttribute.new(step: self, key: output.key.to_sym) unless unsafe_data.has_attribute?(output.key.to_sym) }
411
+ return {} if status.unsafe_not_success?
412
+ return {} if outputs.none?
405
413
 
406
- outputs.reduce({}) { |values, output| values.merge(output.name.to_sym => unsafe_data[output.key.to_sym]) }
414
+ unsafe_data.to_h
407
415
  end
408
416
 
409
417
  ##
@@ -16,9 +16,6 @@ module ConvenientService
16
16
  # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
17
17
  # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Exceptions::MethodForStepIsNotDefined]
18
18
  #
19
- # @internal
20
- # NOTE: `kwargs` are intentionally NOT passed to `own_method.call`, since all the corresponding methods are available inside `own_method` body.
21
- #
22
19
  def next(...)
23
20
  return chain.next(...) unless entity.method_step?
24
21
 
@@ -33,9 +30,6 @@ module ConvenientService
33
30
  # @param method [Method]
34
31
  # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
35
32
  #
36
- # @internal
37
- # TODO: Add specs.
38
- #
39
33
  def call_method(method)
40
34
  params = Support::MethodParameters.new(method.parameters)
41
35
 
@@ -52,16 +46,8 @@ module ConvenientService
52
46
  # @internal
53
47
  # TODO: A possible bottleneck. Should be removed if receives negative feedback.
54
48
  #
55
- # NOTE: `own_method.bind(organizer).call` is logically the same as `own_method.bind_call(organizer)`.
56
- # - https://ruby-doc.org/core-2.7.1/UnboundMethod.html#method-i-bind_call
57
- # - https://blog.saeloun.com/2019/10/17/ruby-2-7-adds-unboundmethod-bind_call-method.html
58
- #
59
49
  def own_method
60
- method = Utils::Module.get_own_instance_method(organizer.class, method_name, private: true)
61
-
62
- return unless method
63
-
64
- method.bind(organizer)
50
+ Utils.memoize_including_falsy_values(self, :@own_method) { Utils::Object.own_method(organizer, method_name, private: true) }
65
51
  end
66
52
 
67
53
  ##
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module CanHaveSteps
7
+ module Entities
8
+ class Step
9
+ module Plugins
10
+ module CanHaveFallbacks
11
+ module Exceptions
12
+ class FallbackResultIsNotOverridden < ::ConvenientService::Exception
13
+ ##
14
+ # @param step [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
15
+ # @param service [ConvenientService::Service]
16
+ # @param status [Symbol]
17
+ # @return [void]
18
+ #
19
+ def initialize_with_kwargs(step:, service:, status:)
20
+ message = <<~TEXT
21
+ Neither `fallback_#{status}_result` nor `fallback_result` methods of `#{service.class}` are overridden, but the step is marked to be fallbacked.
22
+
23
+ Either override one of those methods or remove the `fallback` option from the corresponding step definition in `#{step.container.klass}`.
24
+ TEXT
25
+
26
+ initialize(message)
27
+ end
28
+ end
29
+
30
+ class MethodStepCanNotHaveFallback < ::ConvenientService::Exception
31
+ ##
32
+ # @param step [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
33
+ # @return [void]
34
+ #
35
+ def initialize_with_kwargs(step:)
36
+ message = <<~TEXT
37
+ Method step can NOT have fallback.
38
+
39
+ Either remove `fallback` option from step `:#{step.method}` in `#{step.container.klass}` or consider to refactor it into a service step.
40
+ TEXT
41
+
42
+ initialize(message)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -11,6 +11,11 @@ module ConvenientService
11
11
  class Middleware < MethodChainMiddleware
12
12
  intended_for :result, entity: :step
13
13
 
14
+ ##
15
+ # @return [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
16
+ #
17
+ alias_method :step, :entity
18
+
14
19
  ##
15
20
  # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
16
21
  #
@@ -33,28 +38,28 @@ module ConvenientService
33
38
  # @return [Boolean]
34
39
  #
35
40
  def fallback_failure_step?
36
- entity.fallback_failure_step? || fallback_true_step_as_failure_step?
41
+ step.fallback_failure_step? || fallback_true_step_as_failure_step?
37
42
  end
38
43
 
39
44
  ##
40
45
  # @return [Boolean]
41
46
  #
42
47
  def fallback_true_step_as_failure_step?
43
- entity.fallback_true_step? && fallback_true_status == :failure
48
+ step.fallback_true_step? && fallback_true_status == :failure
44
49
  end
45
50
 
46
51
  ##
47
52
  # @return [Boolean]
48
53
  #
49
54
  def fallback_error_step?
50
- entity.fallback_error_step? || fallback_true_step_as_error_step?
55
+ step.fallback_error_step? || fallback_true_step_as_error_step?
51
56
  end
52
57
 
53
58
  ##
54
59
  # @return [Boolean]
55
60
  #
56
61
  def fallback_true_step_as_error_step?
57
- entity.fallback_true_step? && fallback_true_status == :error
62
+ step.fallback_true_step? && fallback_true_status == :error
58
63
  end
59
64
 
60
65
  ##
@@ -66,26 +71,66 @@ module ConvenientService
66
71
 
67
72
  ##
68
73
  # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
69
- # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepHasNoOrganizer]
70
- #
71
- # @internal
72
- # IMPORTANT: `service.klass.fallback_failure_result(**input_values)` is the reason, why services should have only kwargs as arguments.
73
- # TODO: `entity.service.fallback_failure_result`.
74
+ # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::FallbackResultIsNotOverridden, ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::MethodStepCanNotHaveFallback]
74
75
  #
75
76
  def fallback_failure_result
76
- entity.service.klass.fallback_failure_result(**entity.input_values)
77
+ refute_method_step!
78
+
79
+ fallback_failure_result_own_method&.call || fallback_result_own_method&.call || ::ConvenientService.raise(Exceptions::FallbackResultIsNotOverridden.new(step: step, service: service, status: :failure))
77
80
  end
78
81
 
79
82
  ##
80
83
  # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
81
- # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepHasNoOrganizer]
84
+ # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::FallbackResultIsNotOverridden, ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::MethodStepCanNotHaveFallback]
85
+ #
86
+ def fallback_error_result
87
+ refute_method_step!
88
+
89
+ fallback_error_result_own_method&.call || fallback_result_own_method&.call || ::ConvenientService.raise(Exceptions::FallbackResultIsNotOverridden.new(step: step, service: service, status: :error))
90
+ end
91
+
92
+ ##
93
+ # @return [Method, nil]
94
+ #
95
+ def fallback_failure_result_own_method
96
+ Utils::Object.own_method(service, :fallback_failure_result, private: true)
97
+ end
98
+
99
+ ##
100
+ # @return [Method, nil]
101
+ #
102
+ def fallback_error_result_own_method
103
+ Utils::Object.own_method(service, :fallback_error_result, private: true)
104
+ end
105
+
106
+ ##
107
+ # @return [Method, nil]
108
+ #
109
+ def fallback_result_own_method
110
+ Utils::Object.own_method(service, :fallback_result, private: true)
111
+ end
112
+
113
+ ##
114
+ # @return [ConvenientService::Service]
82
115
  #
83
116
  # @internal
84
- # IMPORTANT: `service.klass.fallback_error_result(**input_values)` is the reason, why services should have only kwargs as arguments.
85
- # TODO: `entity.service.fallback_error_result`.
117
+ # IMPORTANT: `step.service.klass.new(**input_values)` is the reason, why services should have only kwargs as arguments.
86
118
  #
87
- def fallback_error_result
88
- entity.service.klass.fallback_error_result(**entity.input_values)
119
+ def service
120
+ @service ||= step.service.klass.new(**step.input_values)
121
+ end
122
+
123
+ ##
124
+ # @return [void]
125
+ # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::MethodStepCanNotHaveFallback]
126
+ #
127
+ # @internal
128
+ # TODO: Consider to move this assertion to the build time.
129
+ #
130
+ def refute_method_step!
131
+ return unless step.method_step?
132
+
133
+ ::ConvenientService.raise Exceptions::MethodStepCanNotHaveFallback.new(step: step)
89
134
  end
90
135
  end
91
136
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "can_have_fallbacks/concern"
4
+ require_relative "can_have_fallbacks/exceptions"
4
5
  require_relative "can_have_fallbacks/middleware"
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module CanHaveSteps
7
+ module Entities
8
+ class Step
9
+ module Plugins
10
+ module HasAmazingPrintInspect
11
+ module Concern
12
+ include Support::Concern
13
+
14
+ instance_methods do
15
+ ##
16
+ # @return [String]
17
+ #
18
+ # @internal
19
+ # NOTE: `ai` is a part of public interface of `amazing_print`.
20
+ # - https://github.com/amazing-print/amazing_print?tab=readme-ov-file#usage
21
+ # - https://github.com/amazing-print/amazing_print/blob/master/lib/amazing_print/core_ext/kernel.rb
22
+ #
23
+ # TODO: `inspect_values` for class. This way `service_class.name || ...` can be shared.
24
+ #
25
+ def inspect
26
+ metadata = {
27
+ ConvenientService: {
28
+ entity: "Step",
29
+ container: container.klass.name
30
+ }
31
+ }
32
+
33
+ metadata[:ConvenientService].merge!(method_step? ? {method: ":#{method}"} : {service: service.klass.name})
34
+
35
+ metadata.ai
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "has_amazing_print_inspect/concern"
@@ -26,11 +26,12 @@ module ConvenientService
26
26
  metadata = {
27
27
  ConvenientService: {
28
28
  entity: "Step",
29
- container: container.klass.name,
30
- **(method_step? ? {method: ":#{method}"} : {service: service.klass.name})
29
+ container: container.klass.name
31
30
  }
32
31
  }
33
32
 
33
+ metadata[:ConvenientService].merge!(method_step? ? {method: ":#{method}"} : {service: service.klass.name})
34
+
34
35
  metadata.ai
35
36
  end
36
37
  end
@@ -11,6 +11,11 @@ module ConvenientService
11
11
  class Middleware < MethodChainMiddleware
12
12
  intended_for :result, entity: :step
13
13
 
14
+ ##
15
+ # @return [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
16
+ #
17
+ alias_method :step, :entity
18
+
14
19
  ##
15
20
  # @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
16
21
  # @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepHasNoOrganizer]
@@ -20,7 +25,27 @@ module ConvenientService
20
25
  # TODO: `service.result`.
21
26
  #
22
27
  def next(...)
23
- chain.next(...).copy(overrides: {kwargs: {step: entity, service: entity.organizer}})
28
+ result = chain.next(...)
29
+
30
+ result.copy(overrides: {kwargs: {data: extract_data(result), step: step, service: step.organizer}})
31
+ end
32
+
33
+ private
34
+
35
+ ##
36
+ # @param result [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
37
+ # @return [Hash]
38
+ #
39
+ # @internal
40
+ # TODO: Remove `step.outputs.none?` to enforce users to specify outputs for all steps. Will be a breaking change.
41
+ #
42
+ def extract_data(result)
43
+ return result.unsafe_data.to_h if result.status.unsafe_not_success?
44
+ return result.unsafe_data.to_h if step.outputs.none?
45
+
46
+ step.outputs.each { |output| ::ConvenientService.raise Exceptions::StepResultDataNotExistingAttribute.new(step: step, key: output.key.to_sym) unless result.unsafe_data.has_attribute?(output.key.to_sym) }
47
+
48
+ step.outputs.reduce({}) { |values, output| values.merge(output.name.to_sym => result.unsafe_data[output.key.to_sym]) }
24
49
  end
25
50
  end
26
51
  end
@@ -3,3 +3,44 @@
3
3
  require_relative "can_have_steps/commands"
4
4
  require_relative "can_have_steps/concern"
5
5
  require_relative "can_have_steps/entities"
6
+
7
+ module ConvenientService
8
+ module Service
9
+ module Plugins
10
+ module CanHaveSteps
11
+ class << self
12
+ ##
13
+ # Checks whether an object is a step instance.
14
+ #
15
+ # @api public
16
+ #
17
+ # @param step [Object] Can be any type.
18
+ # @return [Boolean]
19
+ #
20
+ # @example Simple usage.
21
+ # class Service
22
+ # include ConvenientService::Standard::Config
23
+ #
24
+ # step :result
25
+ #
26
+ # def result
27
+ # success
28
+ # end
29
+ # end
30
+ #
31
+ # step = Service.new.steps.first
32
+ #
33
+ # ConvenientService::Plugins::Service::CanHaveSteps.step?(step)
34
+ # # => true
35
+ #
36
+ # ConvenientService::Plugins::Service::CanHaveSteps.step?(42)
37
+ # # => false
38
+ #
39
+ def step?(step)
40
+ Commands::IsStep[step: step]
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -16,8 +16,8 @@ module ConvenientService
16
16
  # - https://relishapp.com/rspec/rspec-core/docs/metadata/current-example
17
17
  #
18
18
  def call
19
- if Support::Gems::RSpec.current_example
20
- Utils::Object.memoize_including_falsy_values(Support::Gems::RSpec.current_example, :@__convenient_service_stubbed_results__) { Support::Cache.create(backend: :thread_safe_array) }
19
+ if Dependencies.rspec.current_example
20
+ Utils::Object.memoize_including_falsy_values(Dependencies.rspec.current_example, :@__convenient_service_stubbed_results__) { Support::Cache.create(backend: :thread_safe_array) }
21
21
  else
22
22
  Support::Cache.create(backend: :thread_safe_array)
23
23
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module ForbidsConvenientServiceEntitiesAsConstructorArguments
7
+ module Exceptions
8
+ class ServicePassedAsConstructorArgument < ::ConvenientService::Exception
9
+ def initialize_with_kwargs(selector:, service:, other_service:)
10
+ message = <<~TEXT
11
+ Other service `#{Utils::Class.display_name(other_service.class)}` is passed as constructor argument `#{selector}` to `#{Utils::Class.display_name(service.class)}`.
12
+
13
+ It is an antipattern. It neglects the idea of steps.
14
+
15
+ Please, try to reorganize `#{Utils::Class.display_name(service.class)}` service.
16
+ TEXT
17
+
18
+ initialize(message)
19
+ end
20
+ end
21
+
22
+ class ResultPassedAsConstructorArgument < ::ConvenientService::Exception
23
+ def initialize_with_kwargs(selector:, service:, result:)
24
+ message = <<~TEXT
25
+ Result of `#{Utils::Class.display_name(result.service.class)}` is passed as constructor argument `#{selector}` to `#{Utils::Class.display_name(service.class)}`.
26
+
27
+ It is an antipattern. It neglects the idea of steps.
28
+
29
+ Please, try to reorganize `#{Utils::Class.display_name(service.class)}` service.
30
+ TEXT
31
+
32
+ initialize(message)
33
+ end
34
+ end
35
+
36
+ class StepPassedAsConstructorArgument < ::ConvenientService::Exception
37
+ def initialize_with_kwargs(selector:, service:, step:)
38
+ message = <<~TEXT
39
+ Step of `#{step.printable_container}` is passed as constructor argument `#{selector}` to `#{Utils::Class.display_name(service.class)}`.
40
+
41
+ It is an antipattern. It neglects the idea of steps.
42
+
43
+ Please, try to reorganize `#{Utils::Class.display_name(service.class)}` service.
44
+ TEXT
45
+
46
+ initialize(message)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module ForbidsConvenientServiceEntitiesAsConstructorArguments
7
+ class Middleware < MethodChainMiddleware
8
+ intended_for :initialize, entity: :service
9
+
10
+ ##
11
+ # @return [ConvenientService::Service]
12
+ #
13
+ alias_method :service, :entity
14
+
15
+ ##
16
+ # @param args [Array<Object>]
17
+ # @param kwargs [Hash{Symbol => Object}]
18
+ # @param block [Proc, nil] Can be any type.
19
+ # @raise [ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::ServicePassedAsConstructorArgument, ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::ResultPassedAsConstructorArgument, ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::StepPassedAsConstructorArgument]
20
+ # @return [void]
21
+ #
22
+ def next(*args, **kwargs, &block)
23
+ args.each_with_index { |value, index| validate!(:args, index, value) }
24
+
25
+ kwargs.each_pair { |key, value| validate!(:kwargs, key, value) }
26
+
27
+ chain.next(*args, **kwargs, &block)
28
+ end
29
+
30
+ private
31
+
32
+ ##
33
+ # @param collection_type [Symbol]
34
+ # @param key [Integer, Symbol]
35
+ # @param value [Object] Can be any type.
36
+ # @raise [ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::ServicePassedAsConstructorArgument, ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::ResultPassedAsConstructorArgument, ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::StepPassedAsConstructorArgument]
37
+ # @return [void]
38
+ #
39
+ def validate!(collection_type, key, value)
40
+ refute_service!(collection_type, key, value)
41
+ refute_result!(collection_type, key, value)
42
+ refute_step!(collection_type, key, value)
43
+ end
44
+
45
+ ##
46
+ # @param collection_type [Symbol]
47
+ # @param key [Integer, Symbol]
48
+ # @param value [Object] Can be any type.
49
+ # @raise [ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::ServicePassedAsConstructorArgument]
50
+ # @return [void]
51
+ #
52
+ # @internal
53
+ # NOTE: `refute` is `!assert`.
54
+ # - https://docs.seattlerb.org/minitest
55
+ #
56
+ def refute_service!(collection_type, key, value)
57
+ return unless ::ConvenientService.service?(value)
58
+
59
+ ::ConvenientService.raise Exceptions::ServicePassedAsConstructorArgument.new(selector: selector_from(collection_type, key), service: service, other_service: value)
60
+ end
61
+
62
+ ##
63
+ # @param collection_type [Symbol]
64
+ # @param key [Integer, Symbol]
65
+ # @param value [Object] Can be any type.
66
+ # @raise [ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::ResultPassedAsConstructorArgument]
67
+ # @return [void]
68
+ #
69
+ # @internal
70
+ # NOTE: `refute` is `!assert`.
71
+ # - https://docs.seattlerb.org/minitest
72
+ #
73
+ def refute_result!(collection_type, key, value)
74
+ return unless ::ConvenientService::Plugins::Service::HasJSendResult.result?(value)
75
+
76
+ ::ConvenientService.raise Exceptions::ResultPassedAsConstructorArgument.new(selector: selector_from(collection_type, key), service: service, result: value)
77
+ end
78
+
79
+ ##
80
+ # @param collection_type [Symbol]
81
+ # @param key [Integer, Symbol]
82
+ # @param value [Object] Can be any type.
83
+ # @raise [ConvenientService::Service::Plugins::ForbidsConvenientServiceEntitiesAsConstructorArguments::Exceptions::StepPassedAsConstructorArgument]
84
+ # @return [void]
85
+ #
86
+ # @internal
87
+ # NOTE: `refute` is `!assert`.
88
+ # - https://docs.seattlerb.org/minitest
89
+ #
90
+ def refute_step!(collection_type, key, value)
91
+ return unless ::ConvenientService::Plugins::Service::CanHaveSteps.step?(value)
92
+
93
+ ::ConvenientService.raise Exceptions::StepPassedAsConstructorArgument.new(selector: selector_from(collection_type, key), service: service, step: value)
94
+ end
95
+
96
+ ##
97
+ # @param collection_type [Symbol]
98
+ # @param key [Integer, Symbol]
99
+ # @return [String]
100
+ #
101
+ def selector_from(collection_type, key)
102
+ "#{collection_type}[#{key.inspect}]"
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "forbids_convenient_service_entities_as_constructor_arguments/exceptions"
4
+ require_relative "forbids_convenient_service_entities_as_constructor_arguments/middleware"
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasAmazingPrintInspect
7
+ module Concern
8
+ include Support::Concern
9
+
10
+ instance_methods do
11
+ ##
12
+ # @return [String]
13
+ #
14
+ def inspect
15
+ metadata = {
16
+ ConvenientService: {
17
+ entity: "Service",
18
+ name: inspect_values[:name]
19
+ }
20
+ }
21
+
22
+ metadata.ai
23
+ end
24
+
25
+ ##
26
+ # @return [Hash{Symbol => Object}]
27
+ #
28
+ # @internal
29
+ # NOTE: It is a coincidence that `HasInspect#inspect_values` has exactly the same implementation. There is NO intention to keep them in sync.
30
+ #
31
+ def inspect_values
32
+ {name: Utils::Class.display_name(self.class)}
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "has_amazing_print_inspect/concern"