convenient_service 0.3.1 → 0.5.0

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.dev/.tmuxinator.yml +17 -0
  3. data/CHANGELOG.md +22 -0
  4. data/README.md +8 -8
  5. data/Taskfile.yml +30 -0
  6. data/convenient_service.gemspec +1 -1
  7. data/lib/convenient_service/aliases.rb +5 -0
  8. data/lib/convenient_service/rspec/matchers/custom/results/base.rb +245 -0
  9. data/lib/convenient_service/rspec/matchers/custom/results/be_error.rb +3 -139
  10. data/lib/convenient_service/rspec/matchers/custom/results/be_failure.rb +3 -106
  11. data/lib/convenient_service/rspec/matchers/custom/results/be_not_error.rb +20 -0
  12. data/lib/convenient_service/rspec/matchers/custom/results/be_not_failure.rb +20 -0
  13. data/lib/convenient_service/rspec/matchers/custom/results/be_not_success.rb +20 -0
  14. data/lib/convenient_service/rspec/matchers/custom/results/be_success.rb +3 -112
  15. data/lib/convenient_service/rspec/matchers/custom/results.rb +6 -0
  16. data/lib/convenient_service/rspec/matchers/results/be_not_error.rb +15 -0
  17. data/lib/convenient_service/rspec/matchers/results/be_not_failure.rb +15 -0
  18. data/lib/convenient_service/rspec/matchers/results/be_not_success.rb +15 -0
  19. data/lib/convenient_service/rspec/matchers/results.rb +8 -0
  20. data/lib/convenient_service/service/plugins/has_result/constants.rb +1 -4
  21. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/data.rb +8 -0
  22. data/lib/convenient_service/service/plugins/has_result/entities/result/plugins/has_jsend_status_and_attributes/entities/status.rb +4 -0
  23. data/lib/convenient_service/service/plugins/has_result_method_steps/middleware.rb +1 -4
  24. data/lib/convenient_service/service/plugins/has_result_steps/concern.rb +8 -2
  25. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/commands/cast_method_caller.rb +1 -1
  26. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/commands/cast_method_key.rb +1 -1
  27. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/commands/cast_method_name.rb +1 -1
  28. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/commands/define_method_in_container.rb +15 -8
  29. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/entities.rb +0 -1
  30. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/errors.rb +15 -1
  31. data/lib/convenient_service/service/plugins/has_result_steps/entities/step/concern/instance_methods.rb +10 -0
  32. data/lib/convenient_service/support/dependency_container/commands/create_methods_module.rb +27 -0
  33. data/lib/convenient_service/support/dependency_container/commands/import_method.rb +111 -0
  34. data/lib/convenient_service/support/dependency_container/commands.rb +4 -0
  35. data/lib/convenient_service/support/dependency_container/constants.rb +15 -0
  36. data/lib/convenient_service/support/dependency_container/entities/method.rb +121 -0
  37. data/lib/convenient_service/support/dependency_container/entities/method_collection.rb +98 -0
  38. data/lib/convenient_service/support/dependency_container/entities/namespace.rb +69 -0
  39. data/lib/convenient_service/support/dependency_container/entities/namespace_collection.rb +103 -0
  40. data/lib/convenient_service/support/dependency_container/entities.rb +6 -0
  41. data/lib/convenient_service/support/dependency_container/errors.rb +49 -0
  42. data/lib/convenient_service/support/dependency_container/export.rb +30 -0
  43. data/lib/convenient_service/support/dependency_container/import.rb +30 -0
  44. data/lib/convenient_service/support/dependency_container.rb +9 -0
  45. data/lib/convenient_service/support/gems/rspec.rb +9 -2
  46. data/lib/convenient_service/support/not_passed.rb +7 -0
  47. data/lib/convenient_service/support/raw_value.rb +52 -0
  48. data/lib/convenient_service/support.rb +3 -0
  49. data/lib/convenient_service/utils/module/fetch_own_const.rb +88 -0
  50. data/lib/convenient_service/utils/module/include_module.rb +38 -0
  51. data/lib/convenient_service/utils/module.rb +10 -0
  52. data/lib/convenient_service/utils/proc/conjunct.rb +1 -1
  53. data/lib/convenient_service/utils/string/split.rb +41 -0
  54. data/lib/convenient_service/utils/string.rb +5 -0
  55. data/lib/convenient_service/version.rb +1 -1
  56. data/lib/convenient_service.rb +6 -0
  57. metadata +33 -8
  58. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/entities/values/raw.rb +0 -48
  59. data/lib/convenient_service/service/plugins/has_result_steps/entities/method/entities/values.rb +0 -3
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Entities
7
+ class Method
8
+ ##
9
+ # @!attribute [r] full_name
10
+ # @return [String, Symbol]
11
+ #
12
+ attr_reader :full_name
13
+
14
+ ##
15
+ # @!attribute [r] scope
16
+ # @return [:instance, :class]
17
+ #
18
+ attr_reader :scope
19
+
20
+ ##
21
+ # @!attribute [r] body
22
+ # @return [Proc]
23
+ #
24
+ attr_reader :body
25
+
26
+ ##
27
+ # @param full_name [String, Symbol]
28
+ # @param scope [:instance, :class]
29
+ # @param body [Proc]
30
+ # @return [void]
31
+ #
32
+ def initialize(full_name:, scope:, body:)
33
+ @full_name = full_name
34
+ @scope = scope
35
+ @body = body
36
+ end
37
+
38
+ ##
39
+ # @return [String]
40
+ #
41
+ def name
42
+ @name ||= full_name_parts.last
43
+ end
44
+
45
+ ##
46
+ # @return [Array<ConvenientService::Support::DependencyContainer::Entities::Namespace>]
47
+ #
48
+ def namespaces
49
+ @namespaces ||= full_name_parts.slice(0..-2).map { |part| Entities::Namespace.new(name: part) }
50
+ end
51
+
52
+ ##
53
+ # @param mod [Module]
54
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Method]
55
+ #
56
+ def define_in_module!(mod)
57
+ ##
58
+ # NOTE: `innermost_namespace` is just `mod`, when `namespaces` are empty.
59
+ #
60
+ innermost_namespace =
61
+ namespaces.reduce(mod) do |namespace, sub_namespace|
62
+ already_defined_sub_namespace = namespace.namespaces.find_by(name: sub_namespace.name)
63
+
64
+ ##
65
+ # NOTE:
66
+ # - Reuses already defined namespace from previous "imports".
67
+ # - In contrast, same methods are always redefined.
68
+ #
69
+ next already_defined_sub_namespace if already_defined_sub_namespace
70
+
71
+ namespace.namespaces << sub_namespace
72
+
73
+ namespace.define_method(sub_namespace.name) { sub_namespace.body.call }
74
+
75
+ sub_namespace
76
+ end
77
+
78
+ ##
79
+ # NOTE:
80
+ # - Same methods are redefined.
81
+ # - In contrast, same namespaces are always reused.
82
+ #
83
+ innermost_namespace.define_method(method.name, &method.body)
84
+
85
+ method
86
+ end
87
+
88
+ ##
89
+ # @param other [Object] Can be any type.
90
+ # @return [Boolean]
91
+ #
92
+ def ==(other)
93
+ return unless other.instance_of?(self.class)
94
+
95
+ return false if full_name != other.full_name
96
+ return false if scope != other.scope
97
+ return false if body != other.body
98
+
99
+ true
100
+ end
101
+
102
+ private
103
+
104
+ ##
105
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Method]
106
+ #
107
+ def method
108
+ self
109
+ end
110
+
111
+ ##
112
+ # @return [Array<String>]
113
+ #
114
+ def full_name_parts
115
+ @full_name_parts ||= Utils::String.split(full_name, ".", "::").map(&:to_sym)
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Entities
7
+ class MethodCollection
8
+ ##
9
+ # @param methods [Array<ConvenientService::Support::DependencyContainer::Entities::Method>]
10
+ # @return [void]
11
+ #
12
+ def initialize(methods: [])
13
+ @methods = methods
14
+ end
15
+
16
+ ##
17
+ # @param name [String, Symbol]
18
+ # @param full_name [String, Symbol]
19
+ # @param scope [:instance, :class]
20
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Method, nil]
21
+ #
22
+ def find_by(name: Support::NOT_PASSED, full_name: Support::NOT_PASSED, scope: Support::NOT_PASSED)
23
+ rules = []
24
+
25
+ rules << ->(method) { method.name.to_s == name.to_s } if name != Support::NOT_PASSED
26
+ rules << ->(method) { method.full_name.to_s == full_name.to_s } if full_name != Support::NOT_PASSED
27
+ rules << ->(method) { method.scope == scope } if scope != Support::NOT_PASSED
28
+
29
+ condition = Utils::Proc.conjunct(rules)
30
+
31
+ methods.find(&condition)
32
+ end
33
+
34
+ ##
35
+ # @param method [ConvenientService::Support::DependencyContainer::Entities::Method]
36
+ # @return [ConvenientService::Support::DependencyContainer::Entities::MethodCollection]
37
+ #
38
+ def <<(method)
39
+ methods << method
40
+
41
+ self
42
+ end
43
+
44
+ ##
45
+ # @return [Boolean]
46
+ #
47
+ def empty?
48
+ methods.empty?
49
+ end
50
+
51
+ ##
52
+ # @param method [ConvenientService::Support::DependencyContainer::Entities::Method]
53
+ # @return [Boolean]
54
+ #
55
+ def include?(method)
56
+ methods.include?(method)
57
+ end
58
+
59
+ ##
60
+ # @return [ConvenientService::Support::DependencyContainer::Entities::MethodCollection]
61
+ #
62
+ def clear
63
+ methods.clear
64
+
65
+ self
66
+ end
67
+
68
+ ##
69
+ # @return [Array]
70
+ #
71
+ def to_a
72
+ methods.to_a
73
+ end
74
+
75
+ ##
76
+ # @param other [Object] Can be any type.
77
+ # @return [Boolean]
78
+ #
79
+ def ==(other)
80
+ return unless other.instance_of?(self.class)
81
+
82
+ return false if methods != other.methods
83
+
84
+ true
85
+ end
86
+
87
+ protected
88
+
89
+ ##
90
+ # @!attribute [r] methods
91
+ # @return [Array<ConvenientService::Support::DependencyContainer::Entities::Method>]
92
+ #
93
+ attr_reader :methods
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Entities
7
+ class Namespace
8
+ ##
9
+ # @!attribute [r] name
10
+ # @return [String, Symbol]
11
+ #
12
+ attr_reader :name
13
+
14
+ ##
15
+ # @param name [String, Symbol]
16
+ # @return [void]
17
+ #
18
+ def initialize(name:)
19
+ @name = name
20
+ end
21
+
22
+ ##
23
+ # @return [Proc]
24
+ #
25
+ def body
26
+ @body ||= -> { namespace }
27
+ end
28
+
29
+ ##
30
+ # @return [ConvenientService::Support::DependencyContainer::Entities::NamespaceCollection]
31
+ #
32
+ def namespaces
33
+ @namespaces ||= Entities::NamespaceCollection.new
34
+ end
35
+
36
+ ##
37
+ # @param name [String, Symbol]
38
+ # @param body [Proc]
39
+ # @return [Symbol]
40
+ #
41
+ def define_method(name, &body)
42
+ define_singleton_method(name, &body)
43
+ end
44
+
45
+ ##
46
+ # @param other [Object] Can be any type.
47
+ # @return [Boolean]
48
+ #
49
+ def ==(other)
50
+ return unless other.instance_of?(self.class)
51
+
52
+ return false if name != other.name
53
+
54
+ true
55
+ end
56
+
57
+ private
58
+
59
+ ##
60
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Namespace]
61
+ #
62
+ def namespace
63
+ self
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Entities
7
+ class NamespaceCollection
8
+ ##
9
+ # @param namespaces [Array<ConvenientService::Support::DependencyContainer::Entities::Namespace>]
10
+ # @return [void]
11
+ #
12
+ def initialize(namespaces: [])
13
+ @namespaces = namespaces
14
+ end
15
+
16
+ ##
17
+ #
18
+ #
19
+ def define_namespace(namespace)
20
+ namespaces << namespace
21
+
22
+ define_singleton_method(namespace.name) { namespace.body.call }
23
+ end
24
+
25
+ ##
26
+ # @param name [String, Symbol]
27
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Namespace, nil]
28
+ #
29
+ def find_by(name: Support::NOT_PASSED)
30
+ rules = []
31
+
32
+ rules << ->(namespace) { namespace.name.to_s == name.to_s } if name != Support::NOT_PASSED
33
+
34
+ condition = Utils::Proc.conjunct(rules)
35
+
36
+ namespaces.find(&condition)
37
+ end
38
+
39
+ ##
40
+ # @param namespace [ConvenientService::Support::DependencyContainer::Entities::Namespace]
41
+ # @return [ConvenientService::Support::DependencyContainer::Entities::NamespaceCollection]
42
+ #
43
+ def <<(namespace)
44
+ namespaces << namespace
45
+
46
+ self
47
+ end
48
+
49
+ ##
50
+ # @return [Boolean]
51
+ #
52
+ def empty?
53
+ namespaces.empty?
54
+ end
55
+
56
+ ##
57
+ # @param namespace [ConvenientService::Support::DependencyContainer::Entities::Namespace]
58
+ # @return [Boolean]
59
+ #
60
+ def include?(namespace)
61
+ namespaces.include?(namespace)
62
+ end
63
+
64
+ ##
65
+ # @return [ConvenientService::Support::DependencyContainer::Entities::NamespaceCollection]
66
+ #
67
+ def clear
68
+ namespaces.clear
69
+
70
+ self
71
+ end
72
+
73
+ ##
74
+ # @return [Array]
75
+ #
76
+ def to_a
77
+ namespaces.to_a
78
+ end
79
+
80
+ ##
81
+ # @param other [Object] Can be any type.
82
+ # @return [Boolean]
83
+ #
84
+ def ==(other)
85
+ return unless other.instance_of?(self.class)
86
+
87
+ return false if namespaces != other.namespaces
88
+
89
+ true
90
+ end
91
+
92
+ protected
93
+
94
+ ##
95
+ # @!attribute [r] namespaces
96
+ # @return [Array<ConvenientService::Support::DependencyContainer::Entities::Namespace>]
97
+ #
98
+ attr_reader :namespaces
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "entities/method_collection"
4
+ require_relative "entities/method"
5
+ require_relative "entities/namespace_collection"
6
+ require_relative "entities/namespace"
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Errors
7
+ class NotExportableModule < ConvenientService::Error
8
+ ##
9
+ # @param mod [Module]
10
+ # @return [void]
11
+ #
12
+ def initialize(mod:)
13
+ message = <<~TEXT
14
+ Module `#{mod}` can NOT export methods.
15
+
16
+ Did you forget to include `ConvenientService::Container.export` into it?
17
+ TEXT
18
+
19
+ super(message)
20
+ end
21
+ end
22
+
23
+ class NotExportedMethod < ConvenientService::Error
24
+ ##
25
+ # @param method_name [String]
26
+ # @param method_scope [Symbol]
27
+ # @param mod [Module]
28
+ # @return [void]
29
+ #
30
+ def initialize(method_name:, method_scope:, mod:)
31
+ message = <<~TEXT
32
+ Module `#{mod}` does NOT export method `#{method_name}` with `#{method_scope}` scope.
33
+
34
+ Did you forget to export if from `#{mod}`? For example:
35
+
36
+ module #{mod}
37
+ export #{method_name}, scope: :#{method_scope} do |*args, **kwargs, &block|
38
+ # ...
39
+ end
40
+ end
41
+ TEXT
42
+
43
+ super(message)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Export
7
+ include Support::Concern
8
+
9
+ class_methods do
10
+ ##
11
+ # @param full_name [String, Symbol]
12
+ # @param scope [:instance, :class]
13
+ # @param body [Proc]
14
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Method]
15
+ #
16
+ def export(full_name, scope: Constants::DEFAULT_SCOPE, &body)
17
+ Entities::Method.new(full_name: full_name, scope: scope, body: body).tap { |method| exported_methods << method }
18
+ end
19
+
20
+ ##
21
+ # @return [ConvenientService::Support::DependencyContainer::Entities::MethodCollection]
22
+ #
23
+ def exported_methods
24
+ @exported_methods ||= Entities::MethodCollection.new
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module DependencyContainer
6
+ module Import
7
+ include Support::Concern
8
+
9
+ class_methods do
10
+ ##
11
+ # @param full_name [String, Symbol]
12
+ # @param from [Module]
13
+ # @param scope [:instance, :class]
14
+ # @param prepend [Boolean]
15
+ # @return [ConvenientService::Support::DependencyContainer::Entities::Method]
16
+ #
17
+ def import(full_name, from:, scope: Constants::DEFAULT_SCOPE, prepend: Constants::DEFAULT_PREPEND)
18
+ raise Errors::NotExportableModule.new(mod: from) unless Utils::Module.include_module?(from, DependencyContainer::Export)
19
+
20
+ method = from.exported_methods.find_by(full_name: full_name, scope: scope)
21
+
22
+ raise Errors::NotExportedMethod.new(method_name: full_name, method_scope: scope, mod: from) unless method
23
+
24
+ Commands::ImportMethod.call(importing_module: self, exporting_module: from, method: method, scope: scope, prepend: prepend)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dependency_container/commands"
4
+ require_relative "dependency_container/constants"
5
+ require_relative "dependency_container/entities"
6
+ require_relative "dependency_container/errors"
7
+
8
+ require_relative "dependency_container/export"
9
+ require_relative "dependency_container/import"
@@ -34,7 +34,7 @@ module ConvenientService
34
34
  # @return [RSpec::Core::Example, nil]
35
35
  #
36
36
  # @internal
37
- # NOTE: Returns `nil` in a non-test environment
37
+ # NOTE: Returns `nil` in environments where RSpec is NOT fully loaded, e.g: irb, rails console, etc.
38
38
  #
39
39
  # `::RSpec.current_example` docs:
40
40
  # - https://www.rubydoc.info/github/rspec/rspec-core/RSpec.current_example
@@ -42,7 +42,14 @@ module ConvenientService
42
42
  # - https://relishapp.com/rspec/rspec-core/docs/metadata/current-example
43
43
  #
44
44
  def current_example
45
- ::RSpec.current_example if loaded?
45
+ return unless loaded?
46
+
47
+ ##
48
+ # NOTE: This happens in Ruby-only projects where RSpec is loaded by `Bundler.require`, not by `bundle exec rspec`.
49
+ #
50
+ return unless ::RSpec.respond_to?(:current_example)
51
+
52
+ ::RSpec.current_example
46
53
  end
47
54
  end
48
55
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ NOT_PASSED = ::Object.new
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ class RawValue
6
+ ##
7
+ # @param object [Object] Can be any type.
8
+ # @return [void]
9
+ #
10
+ def initialize(object)
11
+ @object = object
12
+ end
13
+
14
+ class << self
15
+ ##
16
+ # @param object [Object] Can be any type.
17
+ # @return [ConvenientService::Support::RawValue]
18
+ #
19
+ def wrap(object)
20
+ new(object)
21
+ end
22
+
23
+ private :new
24
+ end
25
+
26
+ ##
27
+ # @return [Object] Can be any type.
28
+ #
29
+ def unwrap
30
+ object
31
+ end
32
+
33
+ ##
34
+ # @return [Boolean, nil]
35
+ #
36
+ def ==(other)
37
+ return unless other.instance_of?(self.class)
38
+
39
+ return false if object != other.object
40
+
41
+ true
42
+ end
43
+
44
+ protected
45
+
46
+ ##
47
+ # @return [Object] Can be any type.
48
+ #
49
+ attr_reader :object
50
+ end
51
+ end
52
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "support/not_passed"
3
4
  require_relative "support/concern"
4
5
 
5
6
  require_relative "support/abstract_method"
@@ -8,8 +9,10 @@ require_relative "support/castable"
8
9
  require_relative "support/command"
9
10
  require_relative "support/copyable"
10
11
  require_relative "support/delegate"
12
+ require_relative "support/dependency_container"
11
13
  require_relative "support/finite_loop"
12
14
  require_relative "support/gems"
13
15
  require_relative "support/middleware"
16
+ require_relative "support/raw_value"
14
17
  require_relative "support/ruby"
15
18
  require_relative "support/version"