convenient_service 0.2.1 → 0.3.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/ROADMAP.md +2 -0
  4. data/lib/convenient_service/configs/standard.rb +16 -0
  5. data/lib/convenient_service/dependencies/built_in.rb +8 -0
  6. data/lib/convenient_service/dependencies.rb +37 -0
  7. data/lib/convenient_service/examples/rails/gemfile/services/assert_file_exists.rb +1 -1
  8. data/lib/convenient_service/examples/rails/gemfile/services/assert_file_not_empty.rb +1 -1
  9. data/lib/convenient_service/examples/rails/gemfile/services/assert_npm_package_available.rb +1 -1
  10. data/lib/convenient_service/examples/rails/gemfile/services/parse_content.rb +1 -1
  11. data/lib/convenient_service/examples/rails/gemfile/services/print_shell_command.rb +1 -1
  12. data/lib/convenient_service/examples/rails/gemfile/services/read_file_content.rb +1 -1
  13. data/lib/convenient_service/examples/rails/gemfile/services/run_shell.rb +1 -1
  14. data/lib/convenient_service/examples/rails/gemfile/services/strip_comments.rb +1 -1
  15. data/lib/convenient_service/rspec/helpers/custom/stub_service/entities/result_spec.rb +17 -1
  16. data/lib/convenient_service/rspec/helpers/custom/stub_service/entities/stubbed_service.rb +47 -53
  17. data/lib/convenient_service/service/plugins/can_have_stubbed_result/concern.rb +32 -0
  18. data/lib/convenient_service/service/plugins/can_have_stubbed_result/middleware.rb +36 -0
  19. data/lib/convenient_service/service/plugins/can_have_stubbed_result.rb +4 -0
  20. data/lib/convenient_service/service/plugins.rb +1 -0
  21. data/lib/convenient_service/support/cache.rb +26 -0
  22. data/lib/convenient_service/support/gems/active_model.rb +36 -0
  23. data/lib/convenient_service/support/gems/rspec.rb +36 -0
  24. data/lib/convenient_service/support/gems.rb +4 -0
  25. data/lib/convenient_service/support/ruby.rb +22 -0
  26. data/lib/convenient_service/support/version/null_version.rb +78 -0
  27. data/lib/convenient_service/support/version.rb +65 -0
  28. data/lib/convenient_service/support.rb +3 -0
  29. data/lib/convenient_service/utils/object/instance_variable_fetch.rb +53 -0
  30. data/lib/convenient_service/utils/object.rb +9 -0
  31. data/lib/convenient_service/version.rb +1 -1
  32. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70d36eabeb3ea9598979a7158d06e0bfc627398066abc5180411f13fce9142e8
4
- data.tar.gz: 6c6ea0ce690bb915c32f2e810afe70f87e74961f7a3c01b4e3a6389b8f98b1dc
3
+ metadata.gz: b6eab7876ae6995ff2a0f2f4322a972ad05aae9b4ef3beea93daee900779dc39
4
+ data.tar.gz: 11d7e604831dda483449dc2b2513c0fc5817fd6172740836e5e3929b7d6fbcd4
5
5
  SHA512:
6
- metadata.gz: aa610f3ac041e97849914d6bf15e626cceef9d8abd9aed0e5244b112595c89c9ac1eb91438bf67a84065bc8b90e91ebd8fff19cbcfb3c4a77e9b1246100b25fd
7
- data.tar.gz: d704307543e1a14efa13f2701a109ee2748af75ed2260bbf9e4e64bc06b5bbe2a23ac208eabea1ec2d536578c9b9ec97c76541fa79181b9e896f4f844594d743
6
+ metadata.gz: b422032aa3b32d20579fdcc7e540a5d48e6dd18b3b69182b4b7bc456bbd8a381dd1cdab5654662e1074e095a124d15751aa001d15a36c21518dba0a1683f7130
7
+ data.tar.gz: bc8142c8d9df3971efb3476131608aae89477a46ecbe2efe42e31555f52e2425930c122ff93f8d941f4b44aa1c81b291079b5e11ea0249cfea0d0dd47a0846b7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.0](https://github.com/marian13/convenient_service/compare/v0.2.1...v0.3.0) (2022-12-17)
4
+
5
+
6
+ ### Features
7
+
8
+ * **stub_service:** stub_service supports stubbing with different arguments ([766eb5a](https://github.com/marian13/convenient_service/commit/766eb5a25cfbd49b699ca5c0c0ffa5524dc46548))
9
+
3
10
  ## [0.2.1](https://github.com/marian13/convenient_service/compare/v0.2.0...v0.2.1) (2022-12-14)
4
11
 
5
12
 
data/ROADMAP.md CHANGED
@@ -51,5 +51,7 @@
51
51
  | Medium | 🚧 | User friendly `raise` that removes lib backtrace from caller | Should have a `debug` mode. Should work well with examples. Check RSpec `CallerFilter.first_non_rspec_line` |
52
52
  | High | 🚧 | A plugin that catches `StandardError` and returns `failure` | Should be the lowest in the stack |
53
53
  | High | 🚧 | `Support::Command` alias | Should be visible to the end user |
54
+ | Low | 🚧 | Parallel steps | A killer feature |
55
+ | Medium | 🚧 | Add Ruby 3.2 support to Docker and CI | |
54
56
 
55
57
  Search for `TODO`s in the codebase for more tasks.
@@ -81,6 +81,10 @@ module ConvenientService
81
81
  use Plugins::Service::HasResultShortSyntax::Error::Middleware
82
82
  end
83
83
 
84
+ middlewares :result, scope: :class do
85
+ use Plugins::Common::NormalizesEnv::Middleware
86
+ end
87
+
84
88
  middlewares :step, scope: :class do
85
89
  use Plugins::Common::NormalizesEnv::Middleware
86
90
 
@@ -202,6 +206,18 @@ module ConvenientService
202
206
  end
203
207
  end
204
208
  end
209
+
210
+ if Dependencies.rspec.loaded?
211
+ concerns do
212
+ insert_before 0, Plugins::Service::CanHaveStubbedResult::Concern
213
+ end
214
+
215
+ middlewares :result, scope: :class do
216
+ insert_after \
217
+ Plugins::Common::NormalizesEnv::Middleware,
218
+ Plugins::Service::CanHaveStubbedResult::Middleware
219
+ end
220
+ end
205
221
  end
206
222
  # rubocop:enable Lint/ConstantDefinitionInBlock
207
223
  end
@@ -28,6 +28,14 @@ require "logger"
28
28
  #
29
29
  require "ostruct"
30
30
 
31
+ ##
32
+ # @internal
33
+ # - https://ruby-doc.org/stdlib-2.7.0/libdoc/rubygems/rdoc/Gem/Version.html
34
+ # - https://github.com/rubygems/rubygems
35
+ # - https://github.com/rubygems/rubygems/blob/master/lib/rubygems/version.rb
36
+ #
37
+ require "rubygems"
38
+
31
39
  ##
32
40
  # @internal
33
41
  # - https://ruby-doc.org/stdlib-2.7.0/libdoc/singleton/rdoc/Singleton.html
@@ -42,10 +42,23 @@ module ConvenientService
42
42
  require_relative "common/plugins/has_attributes/using_active_model_attributes"
43
43
  end
44
44
 
45
+ ##
46
+ # @return [Boolean]
47
+ # @api private
48
+ #
49
+ def support_has_result_params_validations_using_active_model_validations?
50
+ return false unless active_model.loaded?
51
+ return false if ruby.version >= 3.0 && active_model.version < 6.0
52
+
53
+ true
54
+ end
55
+
45
56
  ##
46
57
  # @return [Boolean]
47
58
  # @note Expected to be called from app entry points like `initializers` in Rails.
48
59
  #
60
+ # @see https://marian13.github.io/convenient_service_docs/troubleshooting/i18n_translate_wrong_number_of_arguments
61
+ #
49
62
  def require_has_result_params_validations_using_active_model_validations
50
63
  require "active_model"
51
64
 
@@ -129,6 +142,30 @@ module ConvenientService
129
142
 
130
143
  require_relative "examples/dry/gemfile"
131
144
  end
145
+
146
+ ##
147
+ # @return [ConvenientService::Support::Ruby]
148
+ # @api private
149
+ #
150
+ def ruby
151
+ Support::Ruby
152
+ end
153
+
154
+ ##
155
+ # @return [ConvenientService::Support::RSpec]
156
+ # @api private
157
+ #
158
+ def rspec
159
+ Support::Gems::RSpec
160
+ end
161
+
162
+ ##
163
+ # @return [ConvenientService::Support::Gems::ActiveModel]
164
+ # @api private
165
+ #
166
+ def active_model
167
+ Support::Gems::ActiveModel
168
+ end
132
169
  end
133
170
  end
134
171
  end
@@ -10,7 +10,7 @@ module ConvenientService
10
10
 
11
11
  attribute :path, :string
12
12
 
13
- validates :path, presence: true
13
+ validates :path, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
14
14
 
15
15
  def result
16
16
  return error("File with path `#{path}` does NOT exist") unless ::File.exist?(path)
@@ -10,7 +10,7 @@ module ConvenientService
10
10
 
11
11
  attribute :path, :string
12
12
 
13
- validates :path, presence: true
13
+ validates :path, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
14
14
 
15
15
  def result
16
16
  return error(message: "File with path `#{path}` is empty") if ::File.zero?(path)
@@ -15,7 +15,7 @@ module ConvenientService
15
15
 
16
16
  attribute :name, :string
17
17
 
18
- validates :name, presence: true
18
+ validates :name, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
19
19
 
20
20
  step Services::AssertNodeAvailable
21
21
 
@@ -42,7 +42,7 @@ module ConvenientService
42
42
 
43
43
  attribute :content, :string
44
44
 
45
- validates :content, presence: true
45
+ validates :content, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
46
46
 
47
47
  step Services::AssertValidRubySyntax, in: :content
48
48
  step :result, in: :content, out: :parsed_content
@@ -16,7 +16,7 @@ module ConvenientService
16
16
  attribute :text, :string
17
17
  attribute :out, default: $stdout
18
18
 
19
- validates :text, presence: true
19
+ validates :text, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
20
20
 
21
21
  def result
22
22
  out.puts
@@ -10,7 +10,7 @@ module ConvenientService
10
10
 
11
11
  attribute :path, :string
12
12
 
13
- validates :path, presence: true
13
+ validates :path, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
14
14
 
15
15
  step Services::AssertFileExists, in: :path
16
16
  step Services::AssertFileNotEmpty, in: :path
@@ -11,7 +11,7 @@ module ConvenientService
11
11
  attribute :command, :string
12
12
  attribute :debug, :boolean, default: false
13
13
 
14
- validates :command, presence: true
14
+ validates :command, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
15
15
 
16
16
  def result
17
17
  Services::PrintShellCommand.result(text: command) if debug
@@ -15,7 +15,7 @@ module ConvenientService
15
15
 
16
16
  attribute :content, :string
17
17
 
18
- validates :content, presence: true
18
+ validates :content, presence: true if ConvenientService::Dependencies.support_has_result_params_validations_using_active_model_validations?
19
19
 
20
20
  alias_method :content_with_comments, :content
21
21
 
@@ -109,7 +109,21 @@ module ConvenientService
109
109
  service_class.__send__(status, **kwargs)
110
110
  end
111
111
 
112
- private
112
+ ##
113
+ # @param other [Object] Can be any type.
114
+ # @return [Boolean, nil]
115
+ #
116
+ def ==(other)
117
+ return unless other.instance_of?(self.class)
118
+
119
+ return false if status != other.status
120
+ return false if service_class != other.service_class
121
+ return false if chain != other.chain
122
+
123
+ true
124
+ end
125
+
126
+ protected
113
127
 
114
128
  ##
115
129
  # @!attribute [r] status
@@ -129,6 +143,8 @@ module ConvenientService
129
143
  #
130
144
  attr_reader :chain
131
145
 
146
+ private
147
+
132
148
  ##
133
149
  # @return [Hash]
134
150
  #
@@ -6,90 +6,84 @@ module ConvenientService
6
6
  module Custom
7
7
  class StubService < Support::Command
8
8
  module Entities
9
- ##
10
- # TODO: Specs.
11
- #
12
9
  class StubbedService < Support::Command
13
10
  ##
14
- # NOTE: `include ::RSpec::Matchers`.
15
- # - https://github.com/rspec/rspec-expectations/blob/v3.11.0/lib/rspec/matchers.rb
16
- # - https://github.com/rspec/rspec-expectations/blob/main/lib/rspec/matchers.rb
11
+ # @param service_class [Class]
12
+ # @return [void]
17
13
  #
18
- include ::RSpec::Matchers
14
+ def initialize(service_class:)
15
+ @service_class = service_class
16
+ @arguments = {args: [], kwargs: {}, block: nil}
17
+ end
19
18
 
20
19
  ##
21
- # NOTE: `include ::RSpec::Mocks::ExampleMethods`.
22
- # - https://github.com/rspec/rspec-mocks/blob/v3.11.1/lib/rspec/mocks/example_methods.rb
23
- # - https://github.com/rspec/rspec-mocks/blob/main/lib/rspec/mocks/example_methods.rb
20
+ # @param args [Array]
21
+ # @param kwargs [Hash]
22
+ # @param block [Block]
23
+ # @return [ConvenientService::RSpec::Helpers::Custom::StubService::Entities::StubService]
24
24
  #
25
- include ::RSpec::Mocks::ExampleMethods
25
+ def with_arguments(*args, **kwargs, &block)
26
+ @arguments = {args: args, kwargs: kwargs, block: block}
26
27
 
27
- def initialize(service_class:)
28
- @service_class = service_class
28
+ self
29
29
  end
30
30
 
31
- def with_arguments(*args, **kwargs, &block)
32
- chain[:with_arguments] = {args: args, kwargs: kwargs, block: block}
31
+ ##
32
+ # @return [ConvenientService::RSpec::Helpers::Custom::StubService::Entities::StubService]
33
+ #
34
+ def without_arguments
35
+ @arguments = {args: [], kwargs: {}, block: nil}
33
36
 
34
37
  self
35
38
  end
36
39
 
40
+ ##
41
+ # @param result_spec [ConvenientService::RSpec::Helpers::Custom::StubService::Entities::ResultSpec]
42
+ # @return [ConvenientService::RSpec::Helpers::Custom::StubService::Entities::StubService]
43
+ #
37
44
  def to(result_spec)
38
45
  @result_spec = result_spec
39
46
 
40
47
  service_class.commit_config!
41
48
 
42
- if used_with_arguments?
43
- allow(service_class).to receive(:result).and_wrap_original do |original, *actual_args, **actual_kwargs, &actual_block|
44
- expect(actual_args).to eq(expected_args)
45
- expect(actual_kwargs).to eq(expected_kwargs)
46
- expect(actual_block).to eq(expected_block)
49
+ service_class.stubbed_results[key] = result_value
47
50
 
48
- result_value
49
- end
50
- else
51
- allow(service_class).to receive(:result).and_return(result_value)
52
- end
51
+ self
53
52
  end
54
53
 
55
54
  private
56
55
 
57
- attr_reader :service_class, :result_spec
58
-
59
- def result_value
60
- @result_value ||= result_spec.for(service_class).calculate_value
61
- end
62
-
63
- def used_with_arguments?
64
- chain.key?(:with_arguments)
65
- end
66
-
67
- def chain
68
- @chain ||= {}
69
- end
56
+ ##
57
+ # @!attribute [r] service_class
58
+ # @return [Class]
59
+ #
60
+ attr_reader :service_class
70
61
 
71
- def args
72
- @args ||= chain.dig(:with_arguments, :args) || []
73
- end
62
+ ##
63
+ # @!attribute [r] arguments
64
+ # @return [Hash]
65
+ #
66
+ attr_reader :arguments
74
67
 
75
- alias_method :expected_args, :args
68
+ ##
69
+ # @!attribute [r] result_spec
70
+ # @return [ConvenientService::RSpec::Helpers::Custom::StubService::Entities::ResultSpec]
71
+ #
72
+ attr_reader :result_spec
76
73
 
77
- def kwargs
78
- @kwargs ||= chain.dig(:with_arguments, :kwargs) || {}
74
+ ##
75
+ # @return [ConvenientService::Service::Plugins::HasResult::Entities::Result]
76
+ #
77
+ def result_value
78
+ @result_value ||= result_spec.for(service_class).calculate_value
79
79
  end
80
80
 
81
- alias_method :expected_kwargs, :kwargs
82
-
83
81
  ##
84
- # NOTE: `if defined?` is used in order to cache `nil` if needed.
82
+ # @return [ConvenientService::Support::Cache::Key]
85
83
  #
86
- def block
87
- return @block if defined? @block
88
-
89
- @block = chain.dig(:with_arguments, :block)
84
+ def key
85
+ @key ||= service_class.stubbed_results.keygen(*arguments[:args], **arguments[:kwargs], &arguments[:block])
90
86
  end
91
-
92
- alias_method :expected_block, :block
93
87
  end
94
88
  end
95
89
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module CanHaveStubbedResult
7
+ module Concern
8
+ include Support::Concern
9
+
10
+ class_methods do
11
+ ##
12
+ # @return [ConvenientService::Support::Cache]
13
+ #
14
+ # @internal
15
+ # `::RSpec.current_example` docs:
16
+ # - https://www.rubydoc.info/github/rspec/rspec-core/RSpec.current_example
17
+ # - https://github.com/rspec/rspec-core/blob/v3.12.0/lib/rspec/core.rb#L122
18
+ # - https://relishapp.com/rspec/rspec-core/docs/metadata/current-example
19
+ #
20
+ # TODO: Mutex for thread-safety when parallel steps will be supported.
21
+ #
22
+ def stubbed_results
23
+ cache = Utils::Object.instance_variable_fetch(::RSpec.current_example, :@__convenient_service_stubbed_results__) { Support::Cache.new }
24
+
25
+ cache.scope(self)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Service
5
+ module Plugins
6
+ module CanHaveStubbedResult
7
+ class Middleware < Core::MethodChainMiddleware
8
+ ##
9
+ # @param args [Array]
10
+ # @param kwargs [Hash]
11
+ # @param block [Proc]
12
+ # @return [Object] Can be any type.
13
+ #
14
+ def next(*args, **kwargs, &block)
15
+ key_with_arguments = cache.keygen(*args, **kwargs, &block)
16
+ key_without_arguments = cache.keygen
17
+
18
+ return cache[key_with_arguments] if cache.exist?(key_with_arguments)
19
+ return cache[key_without_arguments] if cache.exist?(key_without_arguments)
20
+
21
+ chain.next(*args, **kwargs, &block)
22
+ end
23
+
24
+ private
25
+
26
+ ##
27
+ # @return [ConvenientService::Support::Cache]
28
+ #
29
+ def cache
30
+ entity.stubbed_results
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "can_have_stubbed_result/concern"
4
+ require_relative "can_have_stubbed_result/middleware"
@@ -4,6 +4,7 @@
4
4
  # NOTE: Order matters.
5
5
  #
6
6
  require_relative "plugins/can_recalculate_result"
7
+ require_relative "plugins/can_have_stubbed_result"
7
8
  require_relative "plugins/has_inspect"
8
9
  require_relative "plugins/has_result"
9
10
  require_relative "plugins/has_result_method_steps"
@@ -21,6 +21,16 @@ module ConvenientService
21
21
  end
22
22
  end
23
23
 
24
+ ##
25
+ # @return [Boolean]
26
+ #
27
+ # @internal
28
+ # https://ruby-doc.org/core-2.7.0/Hash.html#method-i-empty-3F
29
+ #
30
+ def empty?
31
+ hash.empty?
32
+ end
33
+
24
34
  ##
25
35
  # @return [Boolean]
26
36
  #
@@ -100,6 +110,22 @@ module ConvenientService
100
110
  exist?(key) ? read(key) : write(key, block.call)
101
111
  end
102
112
 
113
+ ##
114
+ # @return [ConvenientService::Support::Cache]
115
+ #
116
+ def clear
117
+ hash.clear
118
+
119
+ self
120
+ end
121
+
122
+ ##
123
+ # @return [ConvenientService::Support::Cache]
124
+ #
125
+ def scope(key)
126
+ fetch(key) { Support::Cache.new }
127
+ end
128
+
103
129
  ##
104
130
  # @return [ConvenientService::Support::Cache::Key]
105
131
  #
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module Gems
6
+ ##
7
+ # @api private
8
+ #
9
+ class ActiveModel
10
+ class << self
11
+ ##
12
+ # @return [Boolean]
13
+ #
14
+ # @internal
15
+ # `Style/TernaryParentheses` is disabled since `defined?` has too low priority without parentheses.
16
+ #
17
+ # rubocop:disable Style/TernaryParentheses
18
+ def loaded?
19
+ (defined? ::ActiveModel) ? true : false
20
+ end
21
+ # rubocop:enable Style/TernaryParentheses
22
+
23
+ ##
24
+ # @return [ConvenientService::Support::Version]
25
+ #
26
+ # @internal
27
+ # https://github.com/rails/rails/blob/main/activemodel/lib/active_model/version.rb
28
+ #
29
+ def version
30
+ loaded? ? Support::Version.new(::ActiveModel.version) : Support::Version.null_version
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ module Gems
6
+ ##
7
+ # @api private
8
+ #
9
+ class RSpec
10
+ class << self
11
+ ##
12
+ # @return [Boolean]
13
+ #
14
+ # @internal
15
+ # `Style/TernaryParentheses` is disabled since `defined?` has too low priority without parentheses.
16
+ #
17
+ # rubocop:disable Style/TernaryParentheses
18
+ def loaded?
19
+ (defined? ::RSpec) ? true : false
20
+ end
21
+ # rubocop:enable Style/TernaryParentheses
22
+
23
+ ##
24
+ # @return [ConvenientService::Support::Version]
25
+ #
26
+ # @internal
27
+ # https://github.com/rspec/rspec-core/blob/main/lib/rspec/core/version.rb
28
+ #
29
+ def version
30
+ loaded? ? Support::Version.new(::RSpec::Core::Version::STRING) : Support::Version.null_version
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "gems/active_model"
4
+ require_relative "gems/rspec"
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ ##
6
+ # @api private
7
+ #
8
+ class Ruby
9
+ class << self
10
+ ##
11
+ # @return [ConvenientService::Support::Version]
12
+ #
13
+ # @internal
14
+ # https://ruby-doc.org/core-2.7.2/doc/globals_rdoc.html
15
+ #
16
+ def version
17
+ @version ||= Support::Version.new(::RUBY_VERSION)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConvenientService
4
+ module Support
5
+ class Version
6
+ ##
7
+ # @api private
8
+ #
9
+ # @internal
10
+ # - https://thoughtbot.com/blog/rails-refactoring-example-introduce-null-object
11
+ # - https://avdi.codes/null-objects-and-falsiness/
12
+ #
13
+ class NullVersion
14
+ ##
15
+ # @return [nil]
16
+ #
17
+ def gem_version
18
+ nil
19
+ end
20
+
21
+ ##
22
+ # @param other [Object] Can be any type.
23
+ # @return [nil]
24
+ #
25
+ def <=>(other)
26
+ nil
27
+ end
28
+
29
+ ##
30
+ # @param other [Object] Can be any type.
31
+ # @return [nil]
32
+ #
33
+ def <(other)
34
+ nil
35
+ end
36
+
37
+ ##
38
+ # @param other [Object] Can be any type.
39
+ # @return [nil]
40
+ #
41
+ def <=(other)
42
+ nil
43
+ end
44
+
45
+ ##
46
+ # @param other [Object] Can be any type.
47
+ # @return [nil]
48
+ #
49
+ def ==(other)
50
+ nil
51
+ end
52
+
53
+ ##
54
+ # @param other [Object] Can be any type.
55
+ # @return [nil]
56
+ #
57
+ def >(other)
58
+ nil
59
+ end
60
+
61
+ ##
62
+ # @param other [Object] Can be any type.
63
+ # @return [nil]
64
+ #
65
+ def >=(other)
66
+ nil
67
+ end
68
+
69
+ ##
70
+ # @return [String]
71
+ #
72
+ def to_s
73
+ ""
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "version/null_version"
4
+
5
+ module ConvenientService
6
+ module Support
7
+ class Version
8
+ include ::Comparable
9
+
10
+ undef_method :between?
11
+
12
+ undef_method :clamp
13
+
14
+ ##
15
+ # @param value [String]
16
+ # @return [void]
17
+ #
18
+ def initialize(value)
19
+ @value = value
20
+ end
21
+
22
+ class << self
23
+ ##
24
+ # @return [ConvenientService::Support::Version::NullVersion]
25
+ #
26
+ def null_version
27
+ @null_version ||= NullVersion.new
28
+ end
29
+ end
30
+
31
+ ##
32
+ # @return [Gem::Version, nil]
33
+ #
34
+ def gem_version
35
+ cast_gem_version(value)
36
+ end
37
+
38
+ ##
39
+ # @param other [Object] Can be any type.
40
+ # @return [Boolean, nil]
41
+ #
42
+ def <=>(other)
43
+ gem_version <=> cast_gem_version(other)
44
+ end
45
+
46
+ ##
47
+ # @return [String]
48
+ #
49
+ def to_s
50
+ gem_version.to_s
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :value
56
+
57
+ ##
58
+ # @return [Gem::Version, nil]
59
+ #
60
+ def cast_gem_version(value)
61
+ ::Gem::Version.create(value.to_s) if ::Gem::Version.correct?(value.to_s)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -9,4 +9,7 @@ require_relative "support/command"
9
9
  require_relative "support/copyable"
10
10
  require_relative "support/delegate"
11
11
  require_relative "support/finite_loop"
12
+ require_relative "support/gems"
12
13
  require_relative "support/middleware"
14
+ require_relative "support/ruby"
15
+ require_relative "support/version"
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ #
5
+ #
6
+ module ConvenientService
7
+ module Utils
8
+ module Object
9
+ class InstanceVariableFetch < Support::Command
10
+ ##
11
+ # @!attribute [r] object
12
+ # @return [Object]
13
+ #
14
+ attr_reader :object
15
+
16
+ ##
17
+ # @!attribute [r] ivar_name
18
+ # @return [Symbol, String]
19
+ #
20
+ attr_reader :ivar_name
21
+
22
+ ##
23
+ # @!attribute [r] fallback_block
24
+ # @return [Proc, nil]
25
+ #
26
+ attr_reader :fallback_block
27
+
28
+ ##
29
+ # @param object [Object]
30
+ # @param ivar_name [Symbol]
31
+ # @param fallback_block [Proc]
32
+ # @return [void]
33
+ #
34
+ def initialize(object, ivar_name, &fallback_block)
35
+ @object = object
36
+ @ivar_name = ivar_name
37
+ @fallback_block = fallback_block
38
+ end
39
+
40
+ ##
41
+ # @return [Object] Value of ivar. Can be any type.
42
+ #
43
+ def call
44
+ return object.instance_variable_get(ivar_name) if object.instance_variable_defined?(ivar_name)
45
+
46
+ return object.instance_variable_set(ivar_name, fallback_block.call) if fallback_block
47
+
48
+ nil
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,11 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "object/instance_variable_fetch"
3
4
  require_relative "object/resolve_type"
4
5
 
5
6
  module ConvenientService
6
7
  module Utils
7
8
  module Object
8
9
  class << self
10
+ ##
11
+ # @example
12
+ # ConvenientService::Utils::Object.instance_variable_fetch("abc", :@foo) { :bar }
13
+ #
14
+ def instance_variable_fetch(...)
15
+ InstanceVariableFetch.call(...)
16
+ end
17
+
9
18
  ##
10
19
  # @example
11
20
  # ConvenientService::Utils::Object.resolve_type("foo")
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConvenientService
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
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.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marian Kostyk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-14 00:00:00.000000000 Z
11
+ date: 2022-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -548,6 +548,9 @@ files:
548
548
  - lib/convenient_service/service.rb
549
549
  - lib/convenient_service/service/plugins.rb
550
550
  - lib/convenient_service/service/plugins/aliases.rb
551
+ - lib/convenient_service/service/plugins/can_have_stubbed_result.rb
552
+ - lib/convenient_service/service/plugins/can_have_stubbed_result/concern.rb
553
+ - lib/convenient_service/service/plugins/can_have_stubbed_result/middleware.rb
551
554
  - lib/convenient_service/service/plugins/can_recalculate_result.rb
552
555
  - lib/convenient_service/service/plugins/can_recalculate_result/concern.rb
553
556
  - lib/convenient_service/service/plugins/has_inspect.rb
@@ -694,8 +697,14 @@ files:
694
697
  - lib/convenient_service/support/copyable.rb
695
698
  - lib/convenient_service/support/delegate.rb
696
699
  - lib/convenient_service/support/finite_loop.rb
700
+ - lib/convenient_service/support/gems.rb
701
+ - lib/convenient_service/support/gems/active_model.rb
702
+ - lib/convenient_service/support/gems/rspec.rb
697
703
  - lib/convenient_service/support/middleware.rb
698
704
  - lib/convenient_service/support/middleware/stack_builder.rb
705
+ - lib/convenient_service/support/ruby.rb
706
+ - lib/convenient_service/support/version.rb
707
+ - lib/convenient_service/support/version/null_version.rb
699
708
  - lib/convenient_service/utils.rb
700
709
  - lib/convenient_service/utils/array.rb
701
710
  - lib/convenient_service/utils/array/contain_exactly.rb
@@ -720,6 +729,7 @@ files:
720
729
  - lib/convenient_service/utils/module/has_own_instance_method.rb
721
730
  - lib/convenient_service/utils/module/instance_method_defined.rb
722
731
  - lib/convenient_service/utils/object.rb
732
+ - lib/convenient_service/utils/object/instance_variable_fetch.rb
723
733
  - lib/convenient_service/utils/object/resolve_type.rb
724
734
  - lib/convenient_service/utils/proc.rb
725
735
  - lib/convenient_service/utils/proc/conjunct.rb