convenient_service 0.9.0 → 0.10.1

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/lib/convenient_service/common/plugins/can_have_user_provided_entity/commands/find_or_create_entity.rb +117 -0
  4. data/lib/convenient_service/common/plugins/can_have_user_provided_entity/commands.rb +3 -0
  5. data/lib/convenient_service/common/plugins/can_have_user_provided_entity/container.rb +17 -0
  6. data/lib/convenient_service/common/plugins/can_have_user_provided_entity/errors.rb +42 -0
  7. data/lib/convenient_service/common/plugins/can_have_user_provided_entity.rb +5 -0
  8. data/lib/convenient_service/common/plugins/has_internals/commands/create_internals_class.rb +21 -36
  9. data/lib/convenient_service/common/plugins/has_internals/concern.rb +1 -4
  10. data/lib/convenient_service/common/plugins.rb +1 -0
  11. data/lib/convenient_service/rspec/matchers/custom/results/base.rb +5 -0
  12. data/lib/convenient_service/service/plugins/can_have_stubbed_result/concern.rb +18 -1
  13. data/lib/convenient_service/service/plugins/has_result/commands/create_result_class.rb +13 -38
  14. data/lib/convenient_service/service/plugins/has_result/concern/class_methods.rb +4 -0
  15. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/concern/class_methods.rb +22 -0
  16. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/concern.rb +3 -0
  17. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/concern/class_methods.rb +40 -0
  18. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/concern/instance_methods.rb +65 -0
  19. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/{class_methods.rb → concern.rb} +12 -10
  20. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code.rb +2 -26
  21. data/lib/convenient_service/service/plugins/has_result_steps/commands/create_step_class.rb +19 -38
  22. data/lib/convenient_service/service/plugins/has_result_steps/entities/step_collection.rb +4 -1
  23. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_cause.rb +16 -2
  24. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_class.rb +40 -0
  25. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_exception.rb +31 -6
  26. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_message.rb +48 -0
  27. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands.rb +2 -0
  28. data/lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/constants.rb +8 -0
  29. data/lib/convenient_service/utils/string/camelize.rb +6 -2
  30. data/lib/convenient_service/utils/string/demodulize.rb +50 -0
  31. data/lib/convenient_service/utils/string/split.rb +4 -4
  32. data/lib/convenient_service/utils/string.rb +5 -0
  33. data/lib/convenient_service/version.rb +1 -1
  34. metadata +14 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bd38d2c3dd6b1e8a5d8baf78b0b017a2e8f46bf17a1099221da1c3f1b6da0cc
4
- data.tar.gz: eb5f1bad44ec8d80c6e4b661e354c59c170bb0174dcf9f8c9ee9902f434a3cc4
3
+ metadata.gz: bea27107b54aa89400ea5c24d48db2087287b5c4d4aea9bdf3f860dc626bf864
4
+ data.tar.gz: 58a10038fa38887094a47b7dc00745244ac9ce0ef8daa5d1afa1fc8fd6ef893f
5
5
  SHA512:
6
- metadata.gz: fef2d2dda95a3386ebc25494c003053fd72746fac690a4d7b4e1acd4b2eaadf1f858e85ec7340f0a451dc1e658dd288570a1c424671fd47112576870b18935b3
7
- data.tar.gz: 055ec4a62139d303a21c058088fb92cea5eae7d520e4f112445a410049564e3c16c23917ac60884b349673e6174a2a16208b58748eb1eb1df6b241a84f327673
6
+ metadata.gz: faade4af5c03737c1954bfba9cbd9eaa5e31b13856e527cfcfb6e05db3f645729b81b65be8ed22e5f678845b1c3024224b9fe42eccb0bc3b27ed24ae5d8a2dec
7
+ data.tar.gz: 55fef97517812e04cf89eed571570728fd9a9f1ba2504422e3399600d5d71e27cc2723ced664b1eed147f0289b1cf0d0963805c8ffed45c0dfc173f0b4646505
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.1](https://github.com/marian13/convenient_service/compare/v0.10.0...v0.10.1) (2023-03-02)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **can_have_stubbed_result:** add thread-safety ([1962dcc](https://github.com/marian13/convenient_service/commit/1962dccdea29fb36c1b581e312329f41bb23c179))
9
+
10
+ ## [0.10.0](https://github.com/marian13/convenient_service/compare/v0.9.0...v0.10.0) (2023-03-01)
11
+
12
+
13
+ ### Features
14
+
15
+ * **utils:** introduce Utils::String.demodulize ([87a145a](https://github.com/marian13/convenient_service/commit/87a145a7742e1fce04fa40917dae6ee61e811c71))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **be_result:** commit config manually ([658e314](https://github.com/marian13/convenient_service/commit/658e3147cad50cb7226c8530227e704bb4c30945))
21
+ * **can_have_user_provided_entity:** use demodulized proto entity name ([f25e63c](https://github.com/marian13/convenient_service/commit/f25e63c23a9407ec8cc513311de506781e3cb5ca))
22
+ * **has_result_steps:** no validate ([6c14a57](https://github.com/marian13/convenient_service/commit/6c14a572b8fa2dfcb3afef26e64859bb67ac9729))
23
+ * **rescues_result_unhandled_exceptions:** add indentation for all message lines ([6458103](https://github.com/marian13/convenient_service/commit/6458103c362c66da42587b1a0bec4935b42606ac))
24
+ * **rescues_result_unhandled_exceptions:** use formatted message and class for cause ([c75c389](https://github.com/marian13/convenient_service/commit/c75c3891158ea58d28d8de3718a6296565a1cd84))
25
+
3
26
  ## [0.9.0](https://github.com/marian13/convenient_service/compare/v0.8.0...v0.9.0) (2023-02-22)
4
27
 
5
28
 
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Common
5
+ module Plugins
6
+ module CanHaveUserProvidedEntity
7
+ module Commands
8
+ class FindOrCreateEntity < Support::Command
9
+ ##
10
+ # @!attribute [r] namespace
11
+ # @return [Class]
12
+ #
13
+ attr_reader :namespace
14
+
15
+ ##
16
+ # @!attribute [r] proto_entity
17
+ # @return [Class]
18
+ #
19
+ attr_reader :proto_entity
20
+
21
+ ##
22
+ # @param namespace [Class]
23
+ # @param proto_entity [Class]
24
+ # @return [void]
25
+ #
26
+ def initialize(namespace:, proto_entity:)
27
+ @namespace = namespace
28
+ @proto_entity = proto_entity
29
+ end
30
+
31
+ ##
32
+ # @return [void]
33
+ #
34
+ def call
35
+ raise Errors::ProtoEntityHasNoName.new(proto_entity: proto_entity) unless proto_entity_name
36
+ raise Errors::ProtoEntityHasNoConcern.new(proto_entity: proto_entity) unless proto_entity_concern
37
+
38
+ entity.include proto_entity_concern
39
+
40
+ ##
41
+ # @example Result for service.
42
+ #
43
+ # klass = ConvenientService::Common::Plugins::CanHaveUserProvidedEntity::Commands::FindOrCreateEntity.call(
44
+ # namespace: SomeService,
45
+ # proto_entity: ConvenientService::Service::Plugins::HasResult::Entities::Result
46
+ # )
47
+ #
48
+ # ##
49
+ # # `klass` is something like:
50
+ # #
51
+ # # class Result < ConvenientService::Service::Plugins::HasResult::Entities::Result # or just `class Result` if service (namespace) class defines its own.
52
+ # # include ConvenientService::Service::Plugins::HasResult::Entities::Result::Concern # (concern)
53
+ # #
54
+ # # class << self
55
+ # # def proto_entity
56
+ # # ##
57
+ # # # NOTE: Returns `proto_entity` passed to `FindOrCreateEntity`.
58
+ # # #
59
+ # # proto_entity
60
+ # # end
61
+ # #
62
+ # # def ==(other)
63
+ # # return unless other.respond_to?(:proto_entity)
64
+ # #
65
+ # # self.proto_entity == other.proto_entity
66
+ # # end
67
+ # # end
68
+ # # end
69
+ #
70
+ entity.class_exec(proto_entity) do |proto_entity|
71
+ define_singleton_method(:proto_entity) { proto_entity }
72
+ define_singleton_method(:==) { |other| self.proto_entity == other.proto_entity if other.respond_to?(:proto_entity) }
73
+
74
+ ##
75
+ # TODO: `inspect`.
76
+ #
77
+ # define_singleton_method(:inspect) { "#{entity}(Prototyped by #{proto_entity})" }
78
+ end
79
+
80
+ entity
81
+ end
82
+
83
+ private
84
+
85
+ ##
86
+ # @return [Class]
87
+ #
88
+ def entity
89
+ @entity ||= Utils::Module.get_own_const(namespace, proto_entity_demodulized_name) || ::Class.new(proto_entity)
90
+ end
91
+
92
+ ##
93
+ # @return [String, nil]
94
+ #
95
+ def proto_entity_name
96
+ Utils::Object.memoize_including_falsy_values(self, :@proto_entity_name) { proto_entity.name }
97
+ end
98
+
99
+ ##
100
+ # @return [String]
101
+ #
102
+ def proto_entity_demodulized_name
103
+ @proto_entity_demodulized_name ||= Utils::String.demodulize(proto_entity_name)
104
+ end
105
+
106
+ ##
107
+ # @return [Module]
108
+ #
109
+ def proto_entity_concern
110
+ Utils::Object.memoize_including_falsy_values(self, :@proto_entity_concern) { Utils::Module.get_own_const(proto_entity, :Concern) }
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "commands/find_or_create_entity"
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Common
5
+ module Plugins
6
+ module CanHaveUserProvidedEntity
7
+ module Container
8
+ include Support::DependencyContainer::Export
9
+
10
+ export :"commands.FindOrCreateEntity" do
11
+ Commands::FindOrCreateEntity
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Common
5
+ module Plugins
6
+ module CanHaveUserProvidedEntity
7
+ module Errors
8
+ class ProtoEntityHasNoName < ConvenientService::Error
9
+ def initialize(proto_entity:)
10
+ message = <<~TEXT
11
+ Proto entity `#{proto_entity}` has no name.
12
+
13
+ In other words:
14
+
15
+ proto_entity.name
16
+ # => nil
17
+
18
+ NOTE: Anonymous classes do NOT have names. Are you passing an anonymous class?
19
+ TEXT
20
+
21
+ super(message)
22
+ end
23
+ end
24
+
25
+ class ProtoEntityHasNoConcern < ConvenientService::Error
26
+ def initialize(proto_entity:)
27
+ message = <<~TEXT
28
+ Proto entity `#{proto_entity}` has no concern.
29
+
30
+ Have a look at `ConvenientService::Service::Plugins::HasResult::Entities::Result`.
31
+
32
+ It is an example of a valid proto entity.
33
+ TEXT
34
+
35
+ super(message)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "can_have_user_provided_entity/commands"
4
+ require_relative "can_have_user_provided_entity/container"
5
+ require_relative "can_have_user_provided_entity/errors"
@@ -6,47 +6,32 @@ module ConvenientService
6
6
  module HasInternals
7
7
  module Commands
8
8
  class CreateInternalsClass < Support::Command
9
- attr_reader :service_class
9
+ include Support::DependencyContainer::Import
10
10
 
11
- def initialize(service_class:)
12
- @service_class = service_class
13
- end
14
-
15
- def call
16
- internals_class.include Entities::Internals::Concern
11
+ ##
12
+ # @!attribute [r] entity_class
13
+ # @return Class
14
+ #
15
+ attr_reader :entity_class
17
16
 
18
- ##
19
- # class Internals < ConvenientService::Common::Plugins::HasInternals::Entities::Internals # or just `class Internals` if service class defines its own.
20
- # include ConvenientService::Common::Plugins::HasInternals::Entities::Internals::Concern
21
- #
22
- # class << self
23
- # def service_class
24
- # ##
25
- # # NOTE: Returns `service_class` passed to `CreateInternalsClass`.
26
- # #
27
- # service_class
28
- # end
29
- #
30
- # def ==(other)
31
- # return unless other.instance_of?(self.class)
32
- #
33
- # self.service_class == other.service_class
34
- # end
35
- # end
36
- # end
37
- #
38
- internals_class.class_exec(service_class) do |service_class|
39
- define_singleton_method(:service_class) { service_class }
40
- define_singleton_method(:==) { |other| self.service_class == other.service_class if other.instance_of?(self.class) }
41
- end
17
+ ##
18
+ # @return Class
19
+ #
20
+ import :"commands.FindOrCreateEntity", from: Common::Plugins::CanHaveUserProvidedEntity::Container
42
21
 
43
- internals_class
22
+ ##
23
+ # @param entity_class [Class]
24
+ # @return [void]
25
+ #
26
+ def initialize(entity_class:)
27
+ @entity_class = entity_class
44
28
  end
45
29
 
46
- private
47
-
48
- def internals_class
49
- @internals_class ||= Utils::Module.get_own_const(service_class, :Internals) || ::Class.new(Entities::Internals)
30
+ ##
31
+ # @return [Class]
32
+ #
33
+ def call
34
+ commands.FindOrCreateEntity.call(namespace: entity_class, proto_entity: Entities::Internals)
50
35
  end
51
36
  end
52
37
  end
@@ -15,10 +15,7 @@ module ConvenientService
15
15
 
16
16
  class_methods do
17
17
  def internals_class
18
- ##
19
- # TODO: Generic `CreateInternalsClass`.
20
- #
21
- @internals_class ||= Commands::CreateInternalsClass.call(service_class: self)
18
+ @internals_class ||= Commands::CreateInternalsClass.call(entity_class: self)
22
19
  end
23
20
  end
24
21
  end
@@ -8,6 +8,7 @@ require_relative "plugins/normalizes_env"
8
8
  require_relative "plugins/caches_constructor_params"
9
9
  require_relative "plugins/caches_return_value"
10
10
  require_relative "plugins/can_be_copied"
11
+ require_relative "plugins/can_have_user_provided_entity"
11
12
  require_relative "plugins/has_callbacks"
12
13
  require_relative "plugins/has_around_callbacks"
13
14
  require_relative "plugins/has_constructor"
@@ -25,6 +25,11 @@ module ConvenientService
25
25
 
26
26
  rules = []
27
27
 
28
+ ##
29
+ # IMPORTANT: Makes `result.class.include?` from the following line idempotent.
30
+ #
31
+ result.commit_config! if result.respond_to?(:commit_config!)
32
+
28
33
  rules << ->(result) { result.class.include?(Service::Plugins::HasResult::Entities::Result::Concern) }
29
34
 
30
35
  ##
@@ -22,7 +22,24 @@ module ConvenientService
22
22
  def stubbed_results
23
23
  return Support::Cache.new unless Support::Gems::RSpec.current_example
24
24
 
25
- cache = Utils::Object.instance_variable_fetch(::RSpec.current_example, :@__convenient_service_stubbed_results__) { Support::Cache.new }
25
+ ##
26
+ # IMPORTANT: ivar name with class and current thread enforces thread safety since there is no resource to share.
27
+ #
28
+ # TODO: Thread safety specs.
29
+ #
30
+ # NOTE: `self` is a service class in the current context. For example:
31
+ #
32
+ # before do
33
+ # stub_service(ConvenientService::Examples::Standard::Gemfile::Services::RunShell)
34
+ # .with_arguments(command: node_available_command)
35
+ # .to return_result(node_available_status)
36
+ # end
37
+ #
38
+ # # Then `self` is `ConvenientService::Examples::Standard::Gemfile::Services::RunShell`.
39
+ #
40
+ ivar_name = "@__convenient_service_stubbed_results__#{object_id}__#{Thread.current.object_id}__"
41
+
42
+ cache = Utils::Object.instance_variable_fetch(::RSpec.current_example, ivar_name) { Support::Cache.new }
26
43
 
27
44
  cache.scope(self)
28
45
  end
@@ -6,57 +6,32 @@ module ConvenientService
6
6
  module HasResult
7
7
  module Commands
8
8
  class CreateResultClass < Support::Command
9
+ include Support::DependencyContainer::Import
10
+
11
+ ##
12
+ # @!attribute [r] service_class
13
+ # @return Class
14
+ #
9
15
  attr_reader :service_class
10
16
 
11
17
  ##
12
- # @param service_class [Class]
13
- # @return [void]
18
+ # @return Class
14
19
  #
15
- def initialize(service_class:)
16
- @service_class = service_class
17
- end
20
+ import :"commands.FindOrCreateEntity", from: Common::Plugins::CanHaveUserProvidedEntity::Container
18
21
 
19
22
  ##
23
+ # @param service_class [Class]
20
24
  # @return [void]
21
25
  #
22
- def call
23
- result_class.include Entities::Result::Concern
24
-
25
- ##
26
- # class Result < ConvenientService::Service::Plugins::HasResult::Entities::Result # or just `class Result` if service class defines its own.
27
- # include ConvenientService::Service::Plugins::HasResult::Entities::Result::Concern
28
- #
29
- # class << self
30
- # def service_class
31
- # ##
32
- # # NOTE: Returns `service_class` passed to `CreateResultClass`.
33
- # #
34
- # service_class
35
- # end
36
- #
37
- # def ==(other)
38
- # return unless other.instance_of?(self.class)
39
- #
40
- # self.service_class == other.service_class
41
- # end
42
- # end
43
- # end
44
- #
45
- result_class.class_exec(service_class) do |service_class|
46
- define_singleton_method(:service_class) { service_class }
47
- define_singleton_method(:==) { |other| self.service_class == other.service_class if other.instance_of?(self.class) }
48
- end
49
-
50
- result_class
26
+ def initialize(service_class:)
27
+ @service_class = service_class
51
28
  end
52
29
 
53
- private
54
-
55
30
  ##
56
31
  # @return [Class]
57
32
  #
58
- def result_class
59
- @result_class ||= Utils::Module.get_own_const(service_class, :Result) || ::Class.new(Entities::Result)
33
+ def call
34
+ commands.FindOrCreateEntity.call(namespace: service_class, proto_entity: Entities::Result)
60
35
  end
61
36
  end
62
37
  end
@@ -80,6 +80,10 @@ module ConvenientService
80
80
  # @api private
81
81
  # @return [Class]
82
82
  #
83
+ # @internal
84
+ # NOTE: A command instead of `import` is used in order to NOT pollute the public interface.
85
+ # TODO: Specs that prevent public interface accidental pollution.
86
+ #
83
87
  def result_class
84
88
  @result_class ||= Commands::CreateResultClass.call(service_class: self)
85
89
  end
@@ -0,0 +1,22 @@
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 HasJsendStatusAndAttributes
11
+ module Concern
12
+ module ClassMethods
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "concern/class_methods"
3
4
  require_relative "concern/instance_methods"
4
5
 
5
6
  module ConvenientService
@@ -15,6 +16,8 @@ module ConvenientService
15
16
 
16
17
  included do |result_class|
17
18
  result_class.include InstanceMethods
19
+
20
+ result_class.extend ClassMethods
18
21
  end
19
22
  end
20
23
  end
@@ -0,0 +1,40 @@
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 HasJsendStatusAndAttributes
11
+ module Entities
12
+ class Code
13
+ module Concern
14
+ module ClassMethods
15
+ ##
16
+ # @param other [Object] Can be any type.
17
+ # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result::Plugins::HasJsendStatusAndAttributes::Entities::Code, nil]
18
+ #
19
+ def cast(other)
20
+ case other
21
+ when ::String
22
+ new(value: other.to_sym)
23
+ when ::Symbol
24
+ new(value: other)
25
+ when Code
26
+ new(value: other.value)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,65 @@
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 HasJsendStatusAndAttributes
11
+ module Entities
12
+ class Code
13
+ module Concern
14
+ module InstanceMethods
15
+ ##
16
+ # @!attribute value [r]
17
+ # @return [Symbol]
18
+ #
19
+ attr_reader :value
20
+
21
+ ##
22
+ # @param value [Symbol]
23
+ # @return [void]
24
+ #
25
+ def initialize(value:)
26
+ @value = value
27
+ end
28
+
29
+ ##
30
+ # @param other [Object] Can be any type.
31
+ # @return [Boolean]
32
+ #
33
+ def ==(other)
34
+ casted = cast(other)
35
+
36
+ return unless casted
37
+
38
+ value == casted.value
39
+ end
40
+
41
+ ##
42
+ # @return [String]
43
+ #
44
+ def to_s
45
+ @to_s ||= value.to_s
46
+ end
47
+
48
+ ##
49
+ # @return [Symbol]
50
+ #
51
+ def to_sym
52
+ @to_sym ||= value.to_sym
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "concern/class_methods"
4
+ require_relative "concern/instance_methods"
5
+
3
6
  module ConvenientService
4
7
  module Service
5
8
  module Plugins
@@ -10,16 +13,15 @@ module ConvenientService
10
13
  module HasJsendStatusAndAttributes
11
14
  module Entities
12
15
  class Code
13
- module ClassMethods
14
- def cast(other)
15
- case other
16
- when ::String
17
- new(value: other.to_sym)
18
- when ::Symbol
19
- new(value: other)
20
- when Code
21
- new(value: other.value)
22
- end
16
+ module Concern
17
+ include Support::Concern
18
+
19
+ included do |code_class|
20
+ code_class.include Support::Castable
21
+
22
+ code_class.include InstanceMethods
23
+
24
+ code_class.extend ClassMethods
23
25
  end
24
26
  end
25
27
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "code/class_methods"
3
+ require_relative "code/concern"
4
4
 
5
5
  module ConvenientService
6
6
  module Service
@@ -12,31 +12,7 @@ module ConvenientService
12
12
  module HasJsendStatusAndAttributes
13
13
  module Entities
14
14
  class Code
15
- include Support::Castable
16
-
17
- extend ClassMethods
18
-
19
- attr_reader :value
20
-
21
- def initialize(value:)
22
- @value = value
23
- end
24
-
25
- def ==(other)
26
- casted = cast(other)
27
-
28
- return unless casted
29
-
30
- value == casted.value
31
- end
32
-
33
- def to_s
34
- @to_s ||= value.to_s
35
- end
36
-
37
- def to_sym
38
- @to_sym ||= value.to_sym
39
- end
15
+ include Concern
40
16
  end
41
17
  end
42
18
  end
@@ -6,51 +6,32 @@ module ConvenientService
6
6
  module HasResultSteps
7
7
  module Commands
8
8
  class CreateStepClass < Support::Command
9
+ include Support::DependencyContainer::Import
10
+
11
+ ##
12
+ # @!attribute [r] service_class
13
+ # @return Class
14
+ #
9
15
  attr_reader :service_class
10
16
 
17
+ ##
18
+ # @return Class
19
+ #
20
+ import :"commands.FindOrCreateEntity", from: Common::Plugins::CanHaveUserProvidedEntity::Container
21
+
22
+ ##
23
+ # @param service_class [Class]
24
+ # @return [void]
25
+ #
11
26
  def initialize(service_class:)
12
27
  @service_class = service_class
13
28
  end
14
29
 
30
+ ##
31
+ # @return [Class]
32
+ #
15
33
  def call
16
- step_class.include Entities::Step::Concern
17
-
18
- ##
19
- # class Step < ConvenientService::Service::Plugins::HasResultSteps::Entities::Step # or just `class Step` if service class defines its own.
20
- # include ConvenientService::Service::Plugins::HasResultSteps::Entities::Step::Concern
21
- #
22
- # class << self
23
- # def service_class
24
- # ##
25
- # # NOTE: Returns `service_class` passed to `CreateResultClass`.
26
- # #
27
- # service_class
28
- # end
29
- #
30
- # def ==(other)
31
- # return unless other.instance_of?(self.class)
32
- #
33
- # self.service_class == other.service_class
34
- # end
35
- # end
36
- # end
37
- #
38
- step_class.class_exec(service_class) do |service_class|
39
- define_singleton_method(:service_class) { service_class }
40
-
41
- ##
42
- # TODO: Fix a bug with `ap`.
43
- #
44
- define_singleton_method(:==) { |other| self.service_class == other.service_class if other.instance_of?(self.class) }
45
- end
46
-
47
- step_class
48
- end
49
-
50
- private
51
-
52
- def step_class
53
- @step_class ||= Utils::Module.get_own_const(service_class, :Step) || ::Class.new(Entities::Step)
34
+ commands.FindOrCreateEntity.call(namespace: service_class, proto_entity: Entities::Step)
54
35
  end
55
36
  end
56
37
  end
@@ -20,7 +20,10 @@ module ConvenientService
20
20
  def commit!
21
21
  return false if committed?
22
22
 
23
- steps.each { |step| step.validate! && step.define! }.freeze
23
+ ##
24
+ # IMPORTANT: Temporarily removed `step.validate!` since it is neither thread-safe nor idempotent.
25
+ #
26
+ steps.each { |step| step.define! }.freeze
24
27
 
25
28
  true
26
29
  end
@@ -39,14 +39,28 @@ module ConvenientService
39
39
  <<~MESSAGE.chomp
40
40
  ------------------
41
41
  --- Caused by: ---
42
- #{cause.class}:
43
- #{cause.message}
42
+ #{formatted_cause_class}
43
+ #{formatted_cause_message}
44
44
  #{formatted_cause_first_line}
45
45
  MESSAGE
46
46
  end
47
47
 
48
48
  private
49
49
 
50
+ ##
51
+ # @return [String]
52
+ #
53
+ def formatted_cause_class
54
+ Commands::FormatClass.call(klass: cause.class)
55
+ end
56
+
57
+ ##
58
+ # @return [String]
59
+ #
60
+ def formatted_cause_message
61
+ Commands::FormatMessage.call(message: cause.message)
62
+ end
63
+
50
64
  ##
51
65
  # @return [String, nil]
52
66
  #
@@ -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 FormatClass < Support::Command
9
+ ##
10
+ # @!attribute [r] klass
11
+ # @return [Class]
12
+ #
13
+ attr_reader :klass
14
+
15
+ ##
16
+ # @param klass [Class]
17
+ # @return [void]
18
+ #
19
+ def initialize(klass:)
20
+ @klass = klass
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
+ # StandardError:
31
+ #
32
+ def call
33
+ "#{klass}:"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -64,6 +64,23 @@ module ConvenientService
64
64
  # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
65
65
  # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
66
66
  #
67
+ # @example Exception with multiline message.
68
+ #
69
+ # StandardError:
70
+ # exception message first line
71
+ # exception message second line
72
+ # exception message third line
73
+ # # /gem/lib/convenient_service/factories/services.rb:120:in `result'
74
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `call'
75
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/caller/commands/define_method_middlewares_caller.rb:116:in `block in result'
76
+ # # /gem/lib/convenient_service/dependencies/extractions/ruby_middleware/middleware/runner.rb:67:in `block (2 levels) in build_call_chain'
77
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
78
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `block in next'
79
+ # # /gem/lib/convenient_service/support/cache.rb:110:in `fetch'
80
+ # # /gem/lib/convenient_service/common/plugins/caches_return_value/middleware.rb:17:in `next'
81
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/middleware.rb:73:in `call'
82
+ # # /gem/lib/convenient_service/core/entities/config/entities/method_middlewares/entities/chain.rb:35:in `next'
83
+ #
67
84
  # @example Exception with backtrace with more than 10 lines.
68
85
  #
69
86
  # StandardError:
@@ -107,7 +124,8 @@ module ConvenientService
107
124
  #
108
125
  def call
109
126
  <<~MESSAGE.rstrip
110
- #{formatted_exception}
127
+ #{formatted_exception_class}
128
+ #{formatted_exception_message}
111
129
  #{formatted_exception_backtrace}
112
130
  #{formatted_exception_cause}
113
131
  MESSAGE
@@ -118,11 +136,15 @@ module ConvenientService
118
136
  ##
119
137
  # @return [String]
120
138
  #
121
- def formatted_exception
122
- <<~MESSAGE.chomp
123
- #{exception.class}:
124
- #{exception.message}
125
- MESSAGE
139
+ def formatted_exception_class
140
+ Commands::FormatClass.call(klass: exception.class)
141
+ end
142
+
143
+ ##
144
+ # @return [String]
145
+ #
146
+ def formatted_exception_message
147
+ Commands::FormatMessage.call(message: exception.message)
126
148
  end
127
149
 
128
150
  ##
@@ -151,6 +173,9 @@ module ConvenientService
151
173
  ##
152
174
  # @return [String]
153
175
  #
176
+ # @note `exception.cause` may be `$!`.
177
+ # @see https://ruby-doc.org/core-2.7.0/Exception.html#method-i-cause
178
+ #
154
179
  def formatted_exception_cause
155
180
  Commands::FormatCause.call(cause: exception.cause)
156
181
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module RescuesResultUnhandledExceptions
7
+ module Commands
8
+ class FormatMessage < Support::Command
9
+ ##
10
+ # @!attribute [r] message
11
+ # @return [String, nil]
12
+ #
13
+ attr_reader :message
14
+
15
+ ##
16
+ # @param message [String, nil]
17
+ # @return [void]
18
+ #
19
+ def initialize(message:)
20
+ @message = message
21
+ end
22
+
23
+ ##
24
+ # @return [String]
25
+ #
26
+ # @note Message formatting is inspired by RSpec. It has almost the same output (at least for RSpec 3).
27
+ #
28
+ # @note Underscores are used to highlight spaces in docs, they are NOT included in the resulting message, check `FormatException` for a full example.
29
+ #
30
+ # @example Message.
31
+ # __exception message
32
+ #
33
+ # @example Multiline message.
34
+ # __exception message first line
35
+ # __exception message second line
36
+ # __exception message third line
37
+ #
38
+ # @see ConvenientService::Service::Plugins::RescuesResultUnhandledExceptions::Commands::FormatException
39
+ #
40
+ def call
41
+ message.to_s.chomp.split("\n").map { |line| line.prepend(Constants::MESSAGE_LINE_PREFIX) }.join("\n")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative "commands/format_backtrace"
4
4
  require_relative "commands/format_cause"
5
+ require_relative "commands/format_class"
5
6
  require_relative "commands/format_line"
7
+ require_relative "commands/format_message"
6
8
 
7
9
  require_relative "commands/format_exception"
@@ -5,7 +5,15 @@ module ConvenientService
5
5
  module Plugins
6
6
  module RescuesResultUnhandledExceptions
7
7
  module Constants
8
+ ##
9
+ # @return [Integer]
10
+ #
8
11
  DEFAULT_MAX_BACKTRACE_SIZE = 10
12
+
13
+ ##
14
+ # @return [String]
15
+ #
16
+ MESSAGE_LINE_PREFIX = " " * 2
9
17
  end
10
18
  end
11
19
  end
@@ -13,7 +13,7 @@ module ConvenientService
13
13
  class Camelize < Support::Command
14
14
  ##
15
15
  # @!attribute [r] string
16
- # @return [#to_s]
16
+ # @return [String]
17
17
  #
18
18
  attr_reader :string
19
19
 
@@ -24,7 +24,7 @@ module ConvenientService
24
24
  attr_reader :capitalize_first_letter
25
25
 
26
26
  ##
27
- # @param string [Symbol, String]
27
+ # @param string [#to_s]
28
28
  # @return [void]
29
29
  #
30
30
  def initialize(string, capitalize_first_letter: true)
@@ -45,6 +45,10 @@ module ConvenientService
45
45
 
46
46
  private
47
47
 
48
+ ##
49
+ # @param part [String]
50
+ # @return [String]
51
+ #
48
52
  def upcase_first_char(part)
49
53
  return part if part.empty?
50
54
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Utils
5
+ module String
6
+ ##
7
+ # @example
8
+ # demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
9
+ # demodulize('Inflections') # => "Inflections"
10
+ # demodulize('::Inflections') # => "Inflections"
11
+ # demodulize('') # => ""
12
+ #
13
+ # @see https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-demodulize
14
+ #
15
+ class Demodulize < Support::Command
16
+ ##
17
+ # @!attribute [r] string
18
+ # @return [String]
19
+ #
20
+ attr_reader :string
21
+
22
+ ##
23
+ # @param string [#to_s]
24
+ # @return [void]
25
+ #
26
+ def initialize(string)
27
+ @string = string.to_s
28
+ end
29
+
30
+ ##
31
+ # @return [String]
32
+ #
33
+ # @internal
34
+ # NOTE: Copied with cosmetic modifications from:
35
+ # - https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-demodulize
36
+ #
37
+ # NOTE: Modifications.
38
+ # - `string` is `path.to_s` is the original implementation.
39
+ # - Fixed Rubocop complaint about `Style/SlicingWithRange`.
40
+ # - Fixed Rubocop complaint about `Lint/AssignmentInCondition`.
41
+ #
42
+ def call
43
+ i = string.rindex("::")
44
+
45
+ i ? string[(i + 2)..] : string
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -6,7 +6,7 @@ module ConvenientService
6
6
  class Split < Support::Command
7
7
  ##
8
8
  # @!attribute [r] string
9
- # @return [#to_s]
9
+ # @return [String]
10
10
  #
11
11
  attr_reader :string
12
12
 
@@ -17,12 +17,12 @@ module ConvenientService
17
17
  attr_reader :delimiters
18
18
 
19
19
  ##
20
- # @param string [Symbol, String]
20
+ # @param string [#to_s]
21
21
  # @param delimiters [Array<String>]
22
22
  # @return [void]
23
23
  #
24
24
  def initialize(string, *delimiters)
25
- @string = string
25
+ @string = string.to_s
26
26
  @delimiters = delimiters
27
27
  end
28
28
 
@@ -33,7 +33,7 @@ module ConvenientService
33
33
  # https://stackoverflow.com/a/51380514/12201472
34
34
  #
35
35
  def call
36
- string.to_s.split(::Regexp.union(delimiters))
36
+ string.split(::Regexp.union(delimiters))
37
37
  end
38
38
  end
39
39
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "string/camelize"
4
+ require_relative "string/demodulize"
4
5
  require_relative "string/split"
5
6
 
6
7
  module ConvenientService
@@ -11,6 +12,10 @@ module ConvenientService
11
12
  Camelize.call(...)
12
13
  end
13
14
 
15
+ def demodulize(...)
16
+ Demodulize.call(...)
17
+ end
18
+
14
19
  def split(...)
15
20
  Split.call(...)
16
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConvenientService
4
- VERSION = "0.9.0"
4
+ VERSION = "0.10.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: convenient_service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marian Kostyk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-23 00:00:00.000000000 Z
11
+ date: 2023-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -391,6 +391,11 @@ files:
391
391
  - lib/convenient_service/common/plugins/caches_return_value/middleware.rb
392
392
  - lib/convenient_service/common/plugins/can_be_copied.rb
393
393
  - lib/convenient_service/common/plugins/can_be_copied/concern.rb
394
+ - lib/convenient_service/common/plugins/can_have_user_provided_entity.rb
395
+ - lib/convenient_service/common/plugins/can_have_user_provided_entity/commands.rb
396
+ - lib/convenient_service/common/plugins/can_have_user_provided_entity/commands/find_or_create_entity.rb
397
+ - lib/convenient_service/common/plugins/can_have_user_provided_entity/container.rb
398
+ - lib/convenient_service/common/plugins/can_have_user_provided_entity/errors.rb
394
399
  - lib/convenient_service/common/plugins/has_around_callbacks.rb
395
400
  - lib/convenient_service/common/plugins/has_around_callbacks/concern.rb
396
401
  - lib/convenient_service/common/plugins/has_around_callbacks/errors.rb
@@ -712,10 +717,13 @@ files:
712
717
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/commands.rb
713
718
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/commands/cast_jsend_attributes.rb
714
719
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/concern.rb
720
+ - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/concern/class_methods.rb
715
721
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/concern/instance_methods.rb
716
722
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities.rb
717
723
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code.rb
718
- - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/class_methods.rb
724
+ - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/concern.rb
725
+ - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/concern/class_methods.rb
726
+ - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/code/concern/instance_methods.rb
719
727
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/data.rb
720
728
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/data/class_methods.rb
721
729
  - lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/message.rb
@@ -829,8 +837,10 @@ files:
829
837
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands.rb
830
838
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_backtrace.rb
831
839
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_cause.rb
840
+ - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_class.rb
832
841
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_exception.rb
833
842
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_line.rb
843
+ - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/commands/format_message.rb
834
844
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/constants.rb
835
845
  - lib/convenient_service/service/plugins/rescues_result_unhandled_exceptions/middleware.rb
836
846
  - lib/convenient_service/service/plugins/wraps_result_in_db_transaction.rb
@@ -914,6 +924,7 @@ files:
914
924
  - lib/convenient_service/utils/proc/exec_config.rb
915
925
  - lib/convenient_service/utils/string.rb
916
926
  - lib/convenient_service/utils/string/camelize.rb
927
+ - lib/convenient_service/utils/string/demodulize.rb
917
928
  - lib/convenient_service/utils/string/split.rb
918
929
  - lib/convenient_service/version.rb
919
930
  - logo.png