convenient_service 0.2.1 → 0.3.0

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