convenient_service 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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"