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
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ module Commands
8
+ class FormatBacktrace < Support::Command
9
+ ##
10
+ # @!attribute [r] backtrace
11
+ # @return [Array, nil]
12
+ #
13
+ attr_reader :backtrace
14
+
15
+ ##
16
+ # @!attribute [r] max_size
17
+ # @return [Integer]
18
+ #
19
+ attr_reader :max_size
20
+
21
+ ##
22
+ # @param backtrace [Array, nil]
23
+ # @param max_size [Integer]
24
+ # @return [void]
25
+ #
26
+ # @internal
27
+ # IMPORTANT: Sometimes `exception.backtrace` can be `nil`.
28
+ # - https://blog.kalina.tech/2019/04/exception-without-backtrace-in-ruby.html
29
+ # - https://github.com/jruby/jruby/issues/4467
30
+ #
31
+ def initialize(backtrace:, max_size:)
32
+ @backtrace = backtrace.to_a
33
+ @max_size = max_size
34
+ end
35
+
36
+ ##
37
+ # @return [String]
38
+ #
39
+ # @note Exceptions formatting is inspired by RSpec. It has almost the same output (at least for RSpec 3).
40
+ #
41
+ # @example Backtrace with upto 10 lines.
42
+ #
43
+ # # /gem/lib/convenient_service/factories/services.rb:120:in `result'
44
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `call'
45
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `block in result'
46
+ # # /gem/lib/convenient_service/dependencies/extractions/ruby_middleware/middleware/runner.rb:67:in `block (2 levels) in build_call_chain'
47
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
48
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `block in next'
49
+ # # /gem/lib/convenient_service/support/cache.rb:110:in `fetch'
50
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `next'
51
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
52
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
53
+ #
54
+ # @example Backtrace with more than 10 lines.
55
+ #
56
+ # # /gem/lib/convenient_service/factories/services.rb:120:in `result'
57
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `call'
58
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `block in result'
59
+ # # /gem/lib/convenient_service/dependencies/extractions/ruby_middleware/middleware/runner.rb:67:in `block (2 levels) in build_call_chain'
60
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
61
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `block in next'
62
+ # # /gem/lib/convenient_service/support/cache.rb:110:in `fetch'
63
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `next'
64
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
65
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
66
+ # # ...
67
+ #
68
+ def call
69
+ message = backtrace.take(max_size).map { |line| Commands::FormatLine.call(line: line) }.join("\n")
70
+
71
+ message << "\n# ..." if backtrace.size > max_size
72
+
73
+ message
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ module Commands
8
+ class FormatCause < Support::Command
9
+ ##
10
+ # @!attribute [r] exception
11
+ # @return [StandardError, nil]
12
+ #
13
+ attr_reader :cause
14
+
15
+ ##
16
+ # @param cause [StandardError]
17
+ # @return [void]
18
+ #
19
+ def initialize(cause:)
20
+ @cause = cause
21
+ end
22
+
23
+ ##
24
+ # @return [String]
25
+ #
26
+ # @note Cause formatting is inspired by RSpec. It has almost the same output (at least for RSpec 3).
27
+ #
28
+ # @example Cause.
29
+ #
30
+ # ------------------
31
+ # --- Caused by: ---
32
+ # StandardError:
33
+ # cause message
34
+ # # /gem/lib/convenient_service/factories/service/class.rb:41:in `result'
35
+ #
36
+ def call
37
+ return "" unless cause
38
+
39
+ <<~MESSAGE.chomp
40
+ ------------------
41
+ --- Caused by: ---
42
+ #{cause.class}:
43
+ #{cause.message}
44
+ #{formatted_cause_first_line}
45
+ MESSAGE
46
+ end
47
+
48
+ private
49
+
50
+ ##
51
+ # @return [String, nil]
52
+ #
53
+ # @internal
54
+ # IMPORTANT: Sometimes `exception.backtrace` can be `nil`.
55
+ # - https://blog.kalina.tech/2019/04/exception-without-backtrace-in-ruby.html
56
+ # - https://github.com/jruby/jruby/issues/4467
57
+ #
58
+ def cause_first_line
59
+ return "" unless cause
60
+ return "" unless cause.backtrace
61
+
62
+ cause.backtrace.first
63
+ end
64
+
65
+ ##
66
+ # @return [String, nil]
67
+ #
68
+ def formatted_cause_first_line
69
+ return "" unless cause_first_line
70
+
71
+ Commands::FormatLine.call(line: cause_first_line)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ module Commands
8
+ class FormatException < Support::Command
9
+ ##
10
+ # @!attribute [r] exception
11
+ # @return [StandardError]
12
+ #
13
+ attr_reader :exception
14
+
15
+ ##
16
+ # @!attribute [r] args
17
+ # @return [StandardError]
18
+ #
19
+ attr_reader :args
20
+
21
+ ##
22
+ # @!attribute [r] kwargs
23
+ # @return [StandardError]
24
+ #
25
+ attr_reader :kwargs
26
+
27
+ ##
28
+ # @!attribute [r] block
29
+ # @return [StandardError]
30
+ #
31
+ attr_reader :block
32
+
33
+ ##
34
+ # @param exception [StandardError]
35
+ # @param args [Array]
36
+ # @param kwargs [Hash]
37
+ # @param block [Proc, nil]
38
+ # @return [void]
39
+ #
40
+ def initialize(exception:, args:, kwargs:, block:)
41
+ @exception = exception
42
+ @args = args
43
+ @kwargs = kwargs
44
+ @block = block
45
+ end
46
+
47
+ ##
48
+ # @return [String]
49
+ #
50
+ # @note Exceptions formatting is inspired by RSpec. It has almost the same output (at least for RSpec 3).
51
+ #
52
+ # @example Simple exception.
53
+ #
54
+ # StandardError:
55
+ # exception message
56
+ # # /gem/lib/convenient_service/factories/services.rb:120:in `result'
57
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `call'
58
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `block in result'
59
+ # # /gem/lib/convenient_service/dependencies/extractions/ruby_middleware/middleware/runner.rb:67:in `block (2 levels) in build_call_chain'
60
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
61
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `block in next'
62
+ # # /gem/lib/convenient_service/support/cache.rb:110:in `fetch'
63
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `next'
64
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
65
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
66
+ #
67
+ # @example Exception with backtrace with more than 10 lines.
68
+ #
69
+ # StandardError:
70
+ # exception message
71
+ # # /gem/lib/convenient_service/factories/services.rb:120:in `result'
72
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `call'
73
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `block in result'
74
+ # # /gem/lib/convenient_service/dependencies/extractions/ruby_middleware/middleware/runner.rb:67:in `block (2 levels) in build_call_chain'
75
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
76
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `block in next'
77
+ # # /gem/lib/convenient_service/support/cache.rb:110:in `fetch'
78
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `next'
79
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
80
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
81
+ # # ...
82
+ #
83
+ # @example Exception with cause.
84
+ #
85
+ # StandardError:
86
+ # exception message
87
+ # # /gem/lib/convenient_service/factories/service/class.rb:43:in `rescue in result'
88
+ # # /gem/lib/convenient_service/factories/service/class.rb:40:in `result'
89
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `call'
90
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `block in result'
91
+ # # /gem/lib/convenient_service/dependencies/extractions/ruby_middleware/middleware/runner.rb:67:in `block (2 levels) in build_call_chain'
92
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
93
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `block in next'
94
+ # # /gem/lib/convenient_service/support/cache.rb:110:in `fetch'
95
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `next'
96
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
97
+ # ------------------
98
+ # --- Caused by: ---
99
+ # StandardError:
100
+ # cause message
101
+ # # /gem/lib/convenient_service/factories/service/class.rb:41:in `result'
102
+ #
103
+ # @internal
104
+ # TODO: Think about the following questions:
105
+ # - How to format args, kwargs and block?
106
+ # - Who should format them? The end-user or this plugin?
107
+ #
108
+ def call
109
+ <<~MESSAGE.rstrip
110
+ #{formatted_exception}
111
+ #{formatted_exception_backtrace}
112
+ #{formatted_exception_cause}
113
+ MESSAGE
114
+ end
115
+
116
+ private
117
+
118
+ ##
119
+ # @return [String]
120
+ #
121
+ def formatted_exception
122
+ <<~MESSAGE.chomp
123
+ #{exception.class}:
124
+ #{exception.message}
125
+ MESSAGE
126
+ end
127
+
128
+ ##
129
+ # Formats exception backtrace. When backtrace has more then `max_backtrace_size` lines, the extra lines are trimmed.
130
+ # That is especially important for the monitoring system with the limited amount of memory, since it may too expensive to store the full backtrace all the time.
131
+ #
132
+ # @return [String]
133
+ #
134
+ # @internal
135
+ # TODO: Add an ability to set `max_backtrace_size` while configuring the middleware. For example:
136
+ #
137
+ # class Service
138
+ # include ConvenientService::Standard::Config
139
+ #
140
+ # middlewares :result, scope: :class do
141
+ # use ConvenientService::Service::Plugins::RescuesResultUnhandledExceptions::Middleware, max_backtrace_size: 1_000
142
+ # end
143
+ #
144
+ # # ...
145
+ # end
146
+ #
147
+ def formatted_exception_backtrace
148
+ Commands::FormatBacktrace.call(backtrace: exception.backtrace, max_size: max_backtrace_size)
149
+ end
150
+
151
+ ##
152
+ # @return [String]
153
+ #
154
+ def formatted_exception_cause
155
+ Commands::FormatCause.call(cause: exception.cause)
156
+ end
157
+
158
+ ##
159
+ # @return [Integer]
160
+ #
161
+ def max_backtrace_size
162
+ Constants::DEFAULT_MAX_BACKTRACE_SIZE
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ module Commands
8
+ class FormatLine < Support::Command
9
+ ##
10
+ # @!attribute [r] line
11
+ # @return [String]
12
+ #
13
+ attr_reader :line
14
+
15
+ ##
16
+ # @param line [String]
17
+ # @return [void]
18
+ #
19
+ def initialize(line:)
20
+ @line = line
21
+ end
22
+
23
+ ##
24
+ # @return [String]
25
+ #
26
+ # @note Exceptions formatting is inspired by RSpec. It has almost the same output (at least for RSpec 3).
27
+ #
28
+ # @example Line.
29
+ #
30
+ # # /gem/lib/convenient_service/factories/services.rb:120:in `result'
31
+ #
32
+ def call
33
+ "# #{line}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "commands/format_backtrace"
4
+ require_relative "commands/format_cause"
5
+ require_relative "commands/format_line"
6
+
7
+ require_relative "commands/format_exception"
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ module Constants
8
+ DEFAULT_MAX_BACKTRACE_SIZE = 10
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ class Middleware < Core::MethodChainMiddleware
8
+ ##
9
+ # @param args [Array]
10
+ # @param kwargs [Hash]
11
+ # @param block [Proc, nil]
12
+ # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result]
13
+ #
14
+ # @internal
15
+ # NOTE: `rescue => exception` is the same as `rescue ::StandardError => exception`.
16
+ #
17
+ # IMPORTANT: Never rescue `Exception` since its direct descendants are used internally by Ruby, rescue `StandardError` instead.
18
+ # - https://thoughtbot.com/blog/rescue-standarderror-not-exception
19
+ # - https://stackoverflow.com/questions/10048173/why-is-it-bad-style-to-rescue-exception-e-in-ruby
20
+ # - https://ruby-doc.org/core-2.7.0/Exception.html
21
+ #
22
+ def next(*args, **kwargs, &block)
23
+ chain.next(*args, **kwargs, &block)
24
+ rescue => exception
25
+ failure_result_from(exception, *args, **kwargs, &block)
26
+ end
27
+
28
+ private
29
+
30
+ ##
31
+ # @param exception [StandardError]
32
+ # @param args [Array]
33
+ # @param kwargs [Hash]
34
+ # @param block [Proc, nil]
35
+ # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result]
36
+ #
37
+ def failure_result_from(exception, *args, **kwargs, &block)
38
+ entity.failure(
39
+ data: {exception: exception},
40
+ message: format_exception(exception, *args, **kwargs, &block)
41
+ )
42
+ end
43
+
44
+ ##
45
+ # @param exception [StandardError]
46
+ # @param args [Array]
47
+ # @param kwargs [Hash]
48
+ # @param block [Proc, nil]
49
+ # @return [String]
50
+ #
51
+ def format_exception(exception, *args, **kwargs, &block)
52
+ Commands::FormatException.call(exception: exception, args: args, kwargs: kwargs, block: block)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rescues_result_unhandled_exceptions/commands"
4
+ require_relative "rescues_result_unhandled_exceptions/constants"
5
+ require_relative "rescues_result_unhandled_exceptions/middleware"
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Services
5
+ class RunMethodInOrganizer
6
+ ##
7
+ # @internal
8
+ # TODO: Reuse parent config?
9
+ #
10
+ include Configs::Standard
11
+
12
+ attr_reader :method_name, :organizer, :kwargs
13
+
14
+ def initialize(method_name:, organizer:, **kwargs)
15
+ @method_name = method_name
16
+ @organizer = organizer
17
+ @kwargs = kwargs
18
+ end
19
+
20
+ def result
21
+ ##
22
+ # NOTE: `kwargs` are intentionally NOT passed, since all the corresponding methods are available inside `organizer.__send__(method_name)` body.
23
+ #
24
+ organizer.__send__(method_name)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Services
5
+ class RunOwnMethodInOrganizer
6
+ module Errors
7
+ class MethodForStepIsNotDefined < ConvenientService::Error
8
+ def initialize(service_class:, method_name:)
9
+ message = <<~TEXT
10
+ Service #{service_class} tries to use `#{method_name}` method in a step, but it NOT defined.
11
+
12
+ Did you forget to define it?
13
+ TEXT
14
+
15
+ super(message)
16
+ end
17
+ end
18
+ end
19
+
20
+ ##
21
+ # @internal
22
+ # TODO: Reuse parent config?
23
+ #
24
+ include Configs::Standard
25
+
26
+ attr_reader :method_name, :organizer, :kwargs
27
+
28
+ def initialize(method_name:, organizer:, **kwargs)
29
+ @method_name = method_name
30
+ @organizer = organizer
31
+ @kwargs = kwargs
32
+ end
33
+
34
+ def result
35
+ raise Errors::MethodForStepIsNotDefined.new(service_class: organizer.class, method_name: method_name) unless own_method
36
+
37
+ ##
38
+ # NOTE: `kwargs` are intentionally NOT passed, since all the corresponding methods are available inside `own_method` body.
39
+ #
40
+ own_method.call
41
+ end
42
+
43
+ private
44
+
45
+ ##
46
+ # @return [Method, nil]
47
+ #
48
+ # @internal
49
+ # TODO: A possible bottleneck. Should be removed if receives negative feedback.
50
+ #
51
+ # NOTE: `own_method.bind(organizer).call` is logically the same as `own_method.bind_call(organizer)`.
52
+ # - https://ruby-doc.org/core-2.7.1/UnboundMethod.html#method-i-bind_call
53
+ # - https://blog.saeloun.com/2019/10/17/ruby-2-7-adds-unboundmethod-bind_call-method.html
54
+ #
55
+ def own_method
56
+ method = Utils::Module.get_own_instance_method(organizer.class, method_name, private: true)
57
+
58
+ return unless method
59
+
60
+ method.bind(organizer)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "services/method_step_config"
4
3
  require_relative "services/run_own_method_in_organizer"
5
4
  require_relative "services/run_method_in_organizer"
@@ -20,8 +20,12 @@ module ConvenientService
20
20
  # NOTE: This method is NOT likely to be ever changed, that is why inline logic is preferred over command classes in this particular case.
21
21
  #
22
22
  def copy(overrides: {})
23
- overrides[:args] ||= {}
24
- overrides[:kwargs] ||= {}
23
+ defaults = {args: {}, kwargs: {}}
24
+
25
+ ##
26
+ # IMPORTANT: Do not mutate `overrides`.
27
+ #
28
+ overrides = defaults.merge(overrides)
25
29
 
26
30
  ##
27
31
  # TODO: Refactor runtime `respond_to?`. Investigate before refactoring.
@@ -56,7 +56,7 @@ module ConvenientService
56
56
  message = <<~TEXT
57
57
  Module `#{mod}` does NOT export method `#{method_name}` with `#{method_scope}` scope.
58
58
 
59
- Did you forget to export if from `#{mod}`? For example:
59
+ Did you forget to export it from `#{mod}`? For example:
60
60
 
61
61
  module #{mod}
62
62
  export #{method_name}, scope: :#{method_scope} do |*args, **kwargs, &block|
@@ -2,6 +2,8 @@
2
2
 
3
3
  module ConvenientService
4
4
  module Support
5
- NOT_PASSED = ::Object.new
5
+ NOT_PASSED = ::Object.new.tap do |object|
6
+ object.define_singleton_method(:inspect) { "not_passed" }
7
+ end
6
8
  end
7
9
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ UNDEFINED = ::Object.new.tap do |object|
6
+ object.define_singleton_method(:inspect) { "undefined" }
7
+ end
8
+ end
9
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "support/not_passed"
4
+ require_relative "support/undefined"
5
+
4
6
  require_relative "support/concern"
5
7
 
6
8
  require_relative "support/abstract_method"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConvenientService
4
- VERSION = "0.6.0"
4
+ VERSION = "0.8.0"
5
5
  end
@@ -30,6 +30,12 @@ require_relative "convenient_service/common"
30
30
  require_relative "convenient_service/service"
31
31
  require_relative "convenient_service/configs"
32
32
 
33
+ ##
34
+ # @internal
35
+ # Convenient Service Predefined Services.
36
+ #
37
+ require_relative "convenient_service/services"
38
+
33
39
  ##
34
40
  # @internal
35
41
  # Convenient Service Aliases.
data/logo.png CHANGED
Binary file