convenient_service 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/ROADMAP.md +14 -1
  4. data/convenient_service.gemspec +2 -1
  5. data/lib/convenient_service/aliases.rb +10 -0
  6. data/lib/convenient_service/common/plugins/has_around_callbacks/middleware.rb +24 -4
  7. data/lib/convenient_service/common/plugins/has_callbacks/container.rb +17 -0
  8. data/lib/convenient_service/common/plugins/has_callbacks/entities/callback.rb +98 -5
  9. data/lib/convenient_service/common/plugins/has_callbacks/middleware.rb +25 -4
  10. data/lib/convenient_service/common/plugins/has_callbacks.rb +1 -0
  11. data/lib/convenient_service/common/plugins/has_constructor_without_initialize/concern.rb +19 -0
  12. data/lib/convenient_service/common/plugins.rb +1 -1
  13. data/lib/convenient_service/configs/minimal.rb +176 -0
  14. data/lib/convenient_service/configs/standard.rb +30 -105
  15. data/lib/convenient_service/configs.rb +1 -0
  16. data/lib/convenient_service/dependencies.rb +20 -0
  17. data/lib/convenient_service/examples/dry/gemfile/dry_service/config.rb +5 -5
  18. data/lib/convenient_service/examples/rails/gemfile/rails_service/config.rb +7 -7
  19. data/lib/convenient_service/examples/rails/gemfile/services/format.rb +35 -6
  20. data/lib/convenient_service/examples/rails/gemfile/services/format_header.rb +1 -2
  21. data/lib/convenient_service/examples/rails/gemfile/services/merge_sections.rb +25 -0
  22. data/lib/convenient_service/examples/rails/gemfile/services/replace_file_content.rb +37 -0
  23. data/lib/convenient_service/examples/rails/gemfile/services.rb +8 -4
  24. data/lib/convenient_service/examples/standard/gemfile/services/format.rb +45 -6
  25. data/lib/convenient_service/examples/standard/gemfile/services/merge_sections.rb +52 -0
  26. data/lib/convenient_service/examples/standard/gemfile/services/replace_file_content.rb +48 -0
  27. data/lib/convenient_service/examples/standard/gemfile/services.rb +8 -4
  28. data/lib/convenient_service/factories/arguments.rb +43 -0
  29. data/lib/convenient_service/factories/results.rb +214 -0
  30. data/lib/convenient_service/factories/services.rb +189 -0
  31. data/lib/convenient_service/factories/step/instance.rb +32 -0
  32. data/lib/convenient_service/factories/step.rb +3 -0
  33. data/lib/convenient_service/factories/steps.rb +126 -0
  34. data/lib/convenient_service/factories.rb +22 -0
  35. data/lib/convenient_service/factory.rb +21 -0
  36. data/lib/convenient_service/rspec/helpers/custom/ignoring_error.rb +3 -0
  37. data/lib/convenient_service/rspec/helpers/custom/wrap_method/entities/wrapped_method.rb +29 -3
  38. data/lib/convenient_service/rspec/matchers/custom/be_descendant_of.rb +2 -2
  39. data/lib/convenient_service/rspec/matchers/custom/be_direct_descendant_of.rb +2 -2
  40. data/lib/convenient_service/rspec/matchers/custom/delegate_to.rb +9 -0
  41. data/lib/convenient_service/rspec/matchers/custom/results/base/commands/find_result_method_step.rb +74 -0
  42. data/lib/convenient_service/rspec/matchers/custom/results/base/commands/find_result_result_method_step.rb +66 -0
  43. data/lib/convenient_service/rspec/matchers/custom/results/base/commands/find_result_service_step.rb +48 -0
  44. data/lib/convenient_service/rspec/matchers/custom/results/base/commands/generate_expected_step_part.rb +42 -0
  45. data/lib/convenient_service/rspec/matchers/custom/results/base/commands/generate_got_step_part.rb +42 -0
  46. data/lib/convenient_service/rspec/matchers/custom/results/base/commands/match_result_step.rb +89 -0
  47. data/lib/convenient_service/rspec/matchers/custom/results/base/commands.rb +10 -0
  48. data/lib/convenient_service/rspec/matchers/custom/results/base/errors.rb +35 -0
  49. data/lib/convenient_service/rspec/matchers/custom/results/base.rb +78 -12
  50. data/lib/convenient_service/service/plugins/has_result/concern/class_methods.rb +3 -3
  51. data/lib/convenient_service/service/plugins/has_result/constants.rb +0 -3
  52. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/can_have_parent_result/concern.rb +85 -0
  53. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/can_have_parent_result/initialize/middleware.rb +27 -0
  54. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/can_have_parent_result/initialize.rb +3 -0
  55. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/can_have_parent_result/to_kwargs/middleware.rb +25 -0
  56. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/can_have_parent_result/to_kwargs.rb +3 -0
  57. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/can_have_parent_result.rb +5 -0
  58. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/concern/instance_methods.rb +7 -1
  59. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/data.rb +17 -0
  60. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_step/concern.rb +27 -0
  61. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_step/initialize/middleware.rb +27 -0
  62. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_step/initialize.rb +3 -0
  63. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_step/to_kwargs/middleware.rb +25 -0
  64. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_step/to_kwargs.rb +3 -0
  65. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_step.rb +5 -0
  66. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins.rb +2 -0
  67. data/lib/convenient_service/service/plugins/has_result_method_steps.rb +0 -2
  68. data/lib/convenient_service/service/plugins/has_result_steps/concern.rb +18 -7
  69. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/commands/define_method_in_container.rb +2 -2
  70. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/entities/callers/reassignment/commands/define_method_in_container.rb +1 -1
  71. data/lib/convenient_service/service/plugins/has_result_steps/entities/step/concern/instance_methods.rb +26 -1
  72. data/lib/convenient_service/service/plugins/has_result_steps/entities/step/plugins/can_have_parent_result/middleware.rb +23 -0
  73. data/lib/convenient_service/service/plugins/has_result_steps/entities/step/plugins/can_have_parent_result.rb +3 -0
  74. data/lib/convenient_service/service/plugins/has_result_steps/entities/step/plugins.rb +1 -0
  75. data/lib/convenient_service/service/plugins/has_result_steps/entities/step_collection.rb +13 -0
  76. data/lib/convenient_service/service/plugins/has_result_steps/middleware.rb +18 -4
  77. data/lib/convenient_service/service/plugins/raises_on_double_result/middleware.rb +37 -2
  78. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_backtrace.rb +80 -0
  79. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_cause.rb +78 -0
  80. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_exception.rb +169 -0
  81. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_line.rb +40 -0
  82. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands.rb +7 -0
  83. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/constants.rb +13 -0
  84. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/middleware.rb +58 -0
  85. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions.rb +5 -0
  86. data/lib/convenient_service/services/run_method_in_organizer.rb +28 -0
  87. data/lib/convenient_service/services/run_own_method_in_organizer.rb +64 -0
  88. data/lib/convenient_service/{service/plugins/has_result_method_steps/services.rb → services.rb} +0 -1
  89. data/lib/convenient_service/support/copyable.rb +6 -2
  90. data/lib/convenient_service/support/dependency_container/errors.rb +1 -1
  91. data/lib/convenient_service/support/not_passed.rb +3 -1
  92. data/lib/convenient_service/support/undefined.rb +9 -0
  93. data/lib/convenient_service/support.rb +2 -0
  94. data/lib/convenient_service/version.rb +1 -1
  95. data/lib/convenient_service.rb +6 -0
  96. data/logo.png +0 -0
  97. metadata +68 -11
  98. data/lib/convenient_service/service/plugins/has_result_method_steps/errors.rb +0 -23
  99. data/lib/convenient_service/service/plugins/has_result_method_steps/services/method_step_config.rb +0 -55
  100. data/lib/convenient_service/service/plugins/has_result_method_steps/services/run_method_in_organizer.rb +0 -30
  101. data/lib/convenient_service/service/plugins/has_result_method_steps/services/run_own_method_in_organizer.rb +0 -52
@@ -22,7 +22,7 @@ module ConvenientService
22
22
  # NOTE: This method is internally used by custom RSpec helper `stub_service`. It should NOT be used in the client code.
23
23
  #
24
24
  def success(
25
- service: Constants::DEFAULT_SERVICE_INSTANCE,
25
+ service: create_without_initialize,
26
26
  data: Constants::DEFAULT_SUCCESS_DATA
27
27
  )
28
28
  result_class.create(
@@ -42,7 +42,7 @@ module ConvenientService
42
42
  # NOTE: This method is internally used by custom RSpec helper `stub_service`. It should NOT be used in the client code.
43
43
  #
44
44
  def failure(
45
- service: Constants::DEFAULT_SERVICE_INSTANCE,
45
+ service: create_without_initialize,
46
46
  data: Constants::DEFAULT_FAILURE_DATA,
47
47
  message: data.any? ? data.first.join(" ") : Constants::DEFAULT_FAILURE_MESSAGE
48
48
  )
@@ -63,7 +63,7 @@ module ConvenientService
63
63
  # NOTE: This method is internally used by custom RSpec helper `stub_service`. It should NOT be used in the client code.
64
64
  #
65
65
  def error(
66
- service: Constants::DEFAULT_SERVICE_INSTANCE,
66
+ service: create_without_initialize,
67
67
  message: Constants::DEFAULT_ERROR_MESSAGE,
68
68
  code: Constants::DEFAULT_ERROR_CODE
69
69
  )
@@ -5,9 +5,6 @@ module ConvenientService
5
5
  module Plugins
6
6
  module HasResult
7
7
  module Constants
8
- DEFAULT_SERVICE_CLASS = ::Class.new { include ::ConvenientService::Service::Plugins::HasResult::Concern }
9
- DEFAULT_SERVICE_INSTANCE = DEFAULT_SERVICE_CLASS.new
10
-
11
8
  SUCCESS_STATUS = :success
12
9
  FAILURE_STATUS = :failure
13
10
  ERROR_STATUS = :error
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResult
7
+ module Entities
8
+ class Result
9
+ module Plugins
10
+ module CanHaveParentResult
11
+ module Concern
12
+ include Support::Concern
13
+
14
+ instance_methods do
15
+ ##
16
+ # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result, nil]
17
+ #
18
+ def parent
19
+ @parent ||= internals.cache[:parent]
20
+ end
21
+
22
+ ##
23
+ # @param include_self [Boolean]
24
+ # @return [Array<ConvenientService::Service::Plugins::HasResult::Entities::Result>]
25
+ #
26
+ # @internal
27
+ # Without enumerator `parents` method is roughly equivalent to the following code:
28
+ #
29
+ # def parents(include_self: false)
30
+ # parents = []
31
+ #
32
+ # parents << self if include_self
33
+ #
34
+ # ##
35
+ # # NOTE: Empty parentheses are used to force a method call
36
+ # # https://docs.ruby-lang.org/en/2.7.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods
37
+ # #
38
+ # parent = parent()
39
+ #
40
+ # while parent
41
+ # parents << parent
42
+ #
43
+ # parent = parent.parent
44
+ # end
45
+ #
46
+ # parents
47
+ # end
48
+ #
49
+ def parents(include_self: false)
50
+ parents_enum(include_self: include_self).to_a
51
+ end
52
+
53
+ ##
54
+ # @param include_self [Boolean]
55
+ # @return [Enumerator<ConvenientService::Service::Plugins::HasResult::Entities::Result>]
56
+ #
57
+ # @see https://ruby-doc.org/core-2.7.0/Enumerator.html
58
+ #
59
+ def parents_enum(include_self: false)
60
+ ::Enumerator.new do |yielder|
61
+ yielder.yield(self) if include_self
62
+
63
+ ##
64
+ # NOTE: Empty parentheses are used to force a method call
65
+ # https://docs.ruby-lang.org/en/2.7.0/syntax/assignment_rdoc.html#label-Local+Variables+and+Methods
66
+ #
67
+ parent = parent()
68
+
69
+ while parent
70
+ yielder.yield(parent)
71
+
72
+ parent = parent.parent
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResult
7
+ module Entities
8
+ class Result
9
+ module Plugins
10
+ module CanHaveParentResult
11
+ module Initialize
12
+ class Middleware < Core::MethodChainMiddleware
13
+ def next(*args, **kwargs, &block)
14
+ entity.internals.cache[:parent] = kwargs[:parent]
15
+
16
+ chain.next(*args, **kwargs, &block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "initialize/middleware"
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResult
7
+ module Entities
8
+ class Result
9
+ module Plugins
10
+ module CanHaveParentResult
11
+ module ToKwargs
12
+ class Middleware < Core::MethodChainMiddleware
13
+ def next(...)
14
+ chain.next(...).merge(parent: entity.parent)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "to_kwargs/middleware"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "can_have_parent_result/concern"
4
+ require_relative "can_have_parent_result/initialize"
5
+ require_relative "can_have_parent_result/to_kwargs"
@@ -113,7 +113,13 @@ module ConvenientService
113
113
  # @return [Hash]
114
114
  #
115
115
  def to_kwargs
116
- {service: service, status: status, data: data, message: message, code: code}
116
+ {
117
+ service: service,
118
+ status: status,
119
+ data: unsafe_data,
120
+ message: unsafe_message,
121
+ code: unsafe_code
122
+ }
117
123
  end
118
124
  end
119
125
  end
@@ -18,6 +18,19 @@ module ConvenientService
18
18
 
19
19
  attr_reader :value
20
20
 
21
+ ##
22
+ # @internal
23
+ # NOTE: Ruby hashes enumerate their values in the order that the corresponding keys were inserted.
24
+ # That is why the end-user can be 100% sure that the failure message is always generated from the first key/value pair.
25
+ # - https://ruby-doc.org/core-2.7.0/Hash.html
26
+ #
27
+ # TODO: A test that crashes when such behaviour is broken.
28
+ #
29
+ # NOTE: As a result, there is NO need to use any custom `OrderedHash` implementations.
30
+ # - https://api.rubyonrails.org/v5.1/classes/ActiveSupport/OrderedHash.html
31
+ # - https://github.com/rails/rails/issues/22681
32
+ # - https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html
33
+ #
21
34
  def initialize(value:)
22
35
  @value = value
23
36
  end
@@ -45,6 +58,10 @@ module ConvenientService
45
58
  def to_h
46
59
  @to_h ||= value.to_h
47
60
  end
61
+
62
+ def to_s
63
+ @to_s ||= to_h.to_s
64
+ end
48
65
  end
49
66
  end
50
67
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResult
7
+ module Entities
8
+ class Result
9
+ module Plugins
10
+ module HasStep
11
+ module Concern
12
+ include Support::Concern
13
+
14
+ instance_methods do
15
+ def step
16
+ @step ||= internals.cache[:step]
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResult
7
+ module Entities
8
+ class Result
9
+ module Plugins
10
+ module HasStep
11
+ module Initialize
12
+ class Middleware < Core::MethodChainMiddleware
13
+ def next(*args, **kwargs, &block)
14
+ entity.internals.cache[:step] = kwargs[:step]
15
+
16
+ chain.next(*args, **kwargs, &block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "initialize/middleware"
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResult
7
+ module Entities
8
+ class Result
9
+ module Plugins
10
+ module HasStep
11
+ module ToKwargs
12
+ class Middleware < Core::MethodChainMiddleware
13
+ def next(...)
14
+ chain.next(...).merge(step: entity.step)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "to_kwargs/middleware"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "has_step/concern"
4
+ require_relative "has_step/initialize"
5
+ require_relative "has_step/to_kwargs"
@@ -3,6 +3,8 @@
3
3
  require_relative "plugins/can_recalculate_result"
4
4
  require_relative "plugins/has_jsend_status_and_attributes"
5
5
  require_relative "plugins/has_inspect"
6
+ require_relative "plugins/has_step"
7
+ require_relative "plugins/can_have_parent_result"
6
8
  require_relative "plugins/has_result_short_syntax"
7
9
  require_relative "plugins/marks_result_status_as_checked"
8
10
  require_relative "plugins/raises_on_not_checked_result_status"
@@ -1,5 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "has_result_method_steps/errors"
4
3
  require_relative "has_result_method_steps/middleware"
5
- require_relative "has_result_method_steps/services"
@@ -9,7 +9,7 @@ module ConvenientService
9
9
 
10
10
  instance_methods do
11
11
  ##
12
- # @return [Array]
12
+ # @return [Array<ConvenientService::Service::Plugins::HasResultSteps::Entities::Step>]
13
13
  #
14
14
  def steps
15
15
  internals.cache.fetch(:steps) do
@@ -21,18 +21,26 @@ module ConvenientService
21
21
  end
22
22
 
23
23
  ##
24
- # TODO: Create for callbacks.
24
+ # Returns step by index.
25
+ # Returns `nil` when index is out of range.
25
26
  #
26
- # def step(step_result)
27
- # step_result
28
- # end
27
+ # @param index [Integer]
28
+ # @return [ConvenientService::Service::Plugins::HasResultSteps::Entities::Step]
29
+ #
30
+ # @note This method was initially designed as a hook (callback trigger).
31
+ # @see ConvenientService::Service::Plugins::HasResultSteps::Middleware#next
29
32
  #
33
+ def step(index)
34
+ steps[index]
35
+ end
30
36
  end
31
37
 
32
38
  class_methods do
33
39
  ##
34
- # @params args [Array]
35
- # @params kwargs [Hash]
40
+ # Registers a step (step definition).
41
+ #
42
+ # @param args [Array]
43
+ # @param kwargs [Hash]
36
44
  # @return [ConvenientService::Service::Plugins::HasResultSteps::Entities::Step]
37
45
  #
38
46
  def step(*args, **kwargs)
@@ -41,6 +49,9 @@ module ConvenientService
41
49
  end
42
50
 
43
51
  ##
52
+ # @param value [Object] Can be any type.
53
+ # @return [ConvenientService::Support::RawValue]
54
+ #
44
55
  # Allows to pass a value to `in` method without its intermediate processing.
45
56
  # @see https://marian13.github.io/convenient_service_docs/basics/step_to_result_translation_table
46
57
  #
@@ -30,9 +30,9 @@ module ConvenientService
30
30
 
31
31
  raise #{not_completed_step_error}.new(step: step, method_name: method_name) unless step.completed?
32
32
 
33
- raise #{not_existing_step_result_data_attribute_error}.new(step: step, key: key) unless step.result.data.has_attribute?(key)
33
+ raise #{not_existing_step_result_data_attribute_error}.new(step: step, key: key) unless step.result.unsafe_data.has_attribute?(key)
34
34
 
35
- step.result.data[key]
35
+ step.result.unsafe_data[key]
36
36
  end
37
37
  RUBY
38
38
 
@@ -46,7 +46,7 @@ module ConvenientService
46
46
 
47
47
  key = step.reassignment(__method__).key.to_sym
48
48
 
49
- step.result.data[key]
49
+ step.result.unsafe_data[key]
50
50
  end
51
51
  RUBY
52
52
 
@@ -18,6 +18,12 @@ module ConvenientService
18
18
  :not_success?,
19
19
  :not_failure?,
20
20
  :not_error?,
21
+ :data,
22
+ :message,
23
+ :code,
24
+ :unsafe_data,
25
+ :unsafe_message,
26
+ :unsafe_code,
21
27
  to: :result
22
28
 
23
29
  delegate \
@@ -74,6 +80,10 @@ module ConvenientService
74
80
  @input_values ||= calculate_input_values
75
81
  end
76
82
 
83
+ def original_result
84
+ @original_result ||= calculate_original_result
85
+ end
86
+
77
87
  def result
78
88
  @result ||= calculate_result
79
89
  end
@@ -88,6 +98,13 @@ module ConvenientService
88
98
  service.klass.to_s
89
99
  end
90
100
 
101
+ ##
102
+ # @return [Class]
103
+ #
104
+ def service_class
105
+ service.klass
106
+ end
107
+
91
108
  def validate!
92
109
  inputs.each { |input| input.validate_as_input_for_container!(container) }
93
110
 
@@ -114,6 +131,10 @@ module ConvenientService
114
131
 
115
132
  attr_reader :args, :kwargs
116
133
 
134
+ ##
135
+ # @internal
136
+ # TODO: Commands instead of private methods.
137
+ #
117
138
  def calculate_input_values
118
139
  assert_has_organizer!
119
140
 
@@ -124,7 +145,7 @@ module ConvenientService
124
145
  # @internal
125
146
  # IMPORTANT: `service.result(**input_values)` is the reason, why services should have only kwargs as arguments.
126
147
  #
127
- def calculate_result
148
+ def calculate_original_result
128
149
  assert_has_organizer!
129
150
 
130
151
  result = service.result(**input_values)
@@ -134,6 +155,10 @@ module ConvenientService
134
155
  result
135
156
  end
136
157
 
158
+ def calculate_result
159
+ original_result.copy(overrides: {kwargs: {step: self, service: organizer}})
160
+ end
161
+
137
162
  def resolve_params
138
163
  original_params = Commands::ExtractParams.call(args: args, kwargs: kwargs)
139
164
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module HasResultSteps
7
+ module Entities
8
+ class Step
9
+ module Plugins
10
+ module CanHaveParentResult
11
+ class Middleware < Core::MethodChainMiddleware
12
+ def next(...)
13
+ chain.next(...).copy(overrides: {kwargs: {parent: entity.original_result}})
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "can_have_parent_result/middleware"
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "plugins/can_have_parent_result"
3
4
  require_relative "plugins/has_inspect"
@@ -36,6 +36,19 @@ module ConvenientService
36
36
  steps.each(&block)
37
37
  end
38
38
 
39
+ ##
40
+ # Returns step by index.
41
+ #
42
+ # @param index [Integer]
43
+ # @return [ConvenientService::Service::Plugins::HasResultSteps::Entities::Step]
44
+ #
45
+ # @note Works in a similar way as `Array#[]`.
46
+ # @see https://ruby-doc.org/core-2.7.0/Array.html#method-i-5B-5D
47
+ #
48
+ def [](index)
49
+ steps[index]
50
+ end
51
+
39
52
  def <<(step)
40
53
  steps << step.copy(overrides: {kwargs: {index: next_available_index}})
41
54
  end
@@ -5,6 +5,9 @@ module ConvenientService
5
5
  module Plugins
6
6
  module HasResultSteps
7
7
  class Middleware < Core::MethodChainMiddleware
8
+ ##
9
+ # @return [ConvenientService::Service::Plugins::HasResultSteps::Entities::Step]
10
+ #
8
11
  def next(...)
9
12
  return chain.next(...) if entity.steps.none?
10
13
 
@@ -13,11 +16,22 @@ module ConvenientService
13
16
 
14
17
  private
15
18
 
19
+ ##
20
+ # @return [ConvenientService::Service::Plugins::HasResultSteps::Entities::Step]
21
+ #
16
22
  def last_step
17
- ##
18
- # TODO: Use `entity.steps.find { |step| step.result.tap { |result| entity.step(result) }.not_success? }` for callbacks.
19
- #
20
- @last_step ||= entity.steps.find(&:not_success?) || entity.steps.last
23
+ @last_step ||= entity.steps.find.with_index { |step, index| not_success?(step, index) } || entity.steps.last
24
+ end
25
+
26
+ ##
27
+ # @return [ConvenientService::Service::Plugins::HasResultSteps::Entities::Step]
28
+ #
29
+ # @internal
30
+ # NOTE: `entity.step(index)` is used as a hook (callbacks trigger).
31
+ # IMPORTANT: `step` status MUST be checked before triggering callbacks.
32
+ #
33
+ def not_success?(step, index)
34
+ step.not_success?.tap { entity.step(index) }
21
35
  end
22
36
  end
23
37
  end
@@ -5,6 +5,26 @@ module ConvenientService
5
5
  module Plugins
6
6
  module RaisesOnDoubleResult
7
7
  class Middleware < Core::MethodChainMiddleware
8
+ ##
9
+ # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result]
10
+ #
11
+ # @internal
12
+ # TODO: Rewrite `RaisesOnDoubleResult` to make it thread-safe.
13
+ #
14
+ # NOTE: Minimal reproducible example.
15
+ #
16
+ # class Service
17
+ # include ConvenientService::Standard::Config
18
+ #
19
+ # def result
20
+ # success
21
+ # end
22
+ # end
23
+ #
24
+ # service = Service.new
25
+ #
26
+ # 10.times.reduce([]) { |threads| threads << Thread.new { service.result } }.join
27
+ #
8
28
  def next(...)
9
29
  refute_has_result! || mark_as_has_result!
10
30
 
@@ -14,8 +34,14 @@ module ConvenientService
14
34
  private
15
35
 
16
36
  ##
17
- # NOTE: `refute` is `!assert`.
18
- # https://docs.seattlerb.org/minitest
37
+ # @return [Boolean]
38
+ # @raise [ConvenientService::Service::Plugins::RaisesOnDoubleResult::Errors::DoubleResult]
39
+ #
40
+ # @internal
41
+ # NOTE: `refute` is `!assert`.
42
+ # - https://docs.seattlerb.org/minitest
43
+ #
44
+ # NOTE: This method contains a trailing exclamation mark in its name since it is mutable.
19
45
  #
20
46
  def refute_has_result!
21
47
  return unless entity.internals.cache.exist?(:has_result)
@@ -23,6 +49,15 @@ module ConvenientService
23
49
  raise Errors::DoubleResult.new(service: entity)
24
50
  end
25
51
 
52
+ ##
53
+ # @return [Boolean]
54
+ #
55
+ # @internal
56
+ # NOTE: Name is inspired by `mark_as_read`.
57
+ # - https://github.com/ledermann/unread
58
+ #
59
+ # NOTE: This method contains a trailing exclamation mark in its name since it is mutable.
60
+ #
26
61
  def mark_as_has_result!
27
62
  entity.internals.cache.write(:has_result, true)
28
63
  end