cuprum 1.1.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 838e5d593a57d931c54b2ce0831699999bdddf6de2599fb2d074e0d31a0f80e0
4
- data.tar.gz: 721a9e2add543ec5e5f704bc6ed6b8e0f353ef7f6a1da0c3e67b4139d81f9d89
3
+ metadata.gz: dab108b9d6630d5ebb692b2a918654cc9a6a2cb6256a5aad7331b40bd2ea7f1d
4
+ data.tar.gz: 55e8f74553df29e8b244e69257c9178b3b8ea0b931c4f8b7a58ae4f587d17e27
5
5
  SHA512:
6
- metadata.gz: e7d4073ddf03b470f65e123bd196a778af7eb04ed0cdd3ec032d2ba06eb701ba19bf3b8fd92f3cfdc89fcaaed066f892e75b200a9040ff64261cbcb2b7d7c5d4
7
- data.tar.gz: aa8c21b9b7ad0dc0757304bfb580faa836b02b1ab152806324f3526a67bfcedab3ca70c141a6de55cf5f48cc47e29da8fffad7301dd10dadeaa279619f7bffab
6
+ metadata.gz: 3f520bbc963d86937b02b8f7849bb064afafa32e43d47e8e5b394f5ff598e05665ba366bf2bffc44f7a3964982c28dba90d7621ab11b6b5a981217a092b62e15
7
+ data.tar.gz: b3b1b63fa90af7d8f2edfd1d7db6ce5b14e2f10397907fd9dcd07138d4c0d5f70a0d1b538c5c7bd2cb1c9b0a10dfd94997c673ba293dbedfcb0855dda413c43d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.0
4
+
5
+ The "Straight On Till Morning" Update
6
+
7
+ As of version 1.2.0, Cuprum will no longer support Ruby 2.7.
8
+
9
+ ### Results
10
+
11
+ Added the `#properties` method (aliased as `#to_h`) that wraps the result's `#value`, `#status`, and `#error`. Collecting the properties in one method supports defining results with additional or custom properties, such as a checksum or metadata.
12
+
13
+ The `Cuprum::Result#==` method was updated to compare the result's properties.
14
+
15
+ - A result can now be matched against a hash with `:value`, `:status`, and `:error` keys.
16
+ - Matching a result against a non-result object with its own `#value`, `#status`, and `#error` methods is now deprecated. Define a `#to_h` method on that object to wrap its properties for comparison with a result.
17
+
18
+ ### RSpec
19
+
20
+ Updated the `Cuprum::RSpec::BeAResultMatcher` to accept an optional result class.
21
+
3
22
  ## 1.1.0
4
23
 
5
24
  The "Second Star To The Right" Update
data/README.md CHANGED
@@ -30,7 +30,7 @@ On the opposite end of the scale, frameworks such as [Dry::Monads](https://dry-r
30
30
 
31
31
  ## Compatibility
32
32
 
33
- Cuprum is tested against Ruby (MRI) 2.7 through 3.2.
33
+ Cuprum is tested against Ruby (MRI) 3.0 through 3.2.
34
34
 
35
35
  ## Documentation
36
36
 
data/lib/cuprum/result.rb CHANGED
@@ -8,10 +8,10 @@ module Cuprum
8
8
  # Enumerates the default permitted values for a Result#status.
9
9
  STATUSES = %i[success failure].freeze
10
10
 
11
- # @param value [Object] The value returned by calling the command.
12
- # @param error [Object] The error (if any) generated when the command was
11
+ # @param value [Object] the value returned by calling the command.
12
+ # @param error [Object] the error (if any) generated when the command was
13
13
  # called. Can be a Cuprum::Error, a model errors object, etc.
14
- # @param status [String, Symbol] The status of the result. Must be :success,
14
+ # @param status [String, Symbol] the status of the result. Must be :success,
15
15
  # :failure, or nil.
16
16
  def initialize(value: nil, error: nil, status: nil)
17
17
  @value = value
@@ -31,18 +31,19 @@ module Cuprum
31
31
 
32
32
  # Compares the other object to the result.
33
33
  #
34
- # @param other [#value, #success?] An object responding to, at minimum,
35
- # #value and #success?. If present, the #failure? and #error values
36
- # will also be compared.
34
+ # In order to match the result, the object must respond to the #to_h method,
35
+ # and the value of object.to_h must be equal to the value of
36
+ # result.properties.
37
37
  #
38
- # @return [Boolean] True if all present values match the result, otherwise
39
- # false.
38
+ # @param other [#to_h] the result or object to compare.
39
+ #
40
+ # @return [Boolean] true if all values match the result, otherwise false.
40
41
  def ==(other)
41
- return false unless other.respond_to?(:value) && other.value == value
42
- return false unless other.respond_to?(:status) && other.status == status
43
- return false unless other.respond_to?(:error) && other.error == error
42
+ other = other.to_cuprum_result if other.respond_to?(:to_cuprum_result)
43
+
44
+ return properties == other.to_h if other.respond_to?(:to_h)
44
45
 
45
- true
46
+ deprecated_compare(other)
46
47
  end
47
48
 
48
49
  # @return [Boolean] true if the result status is :failure, otherwise false.
@@ -50,6 +51,16 @@ module Cuprum
50
51
  @status == :failure
51
52
  end
52
53
 
54
+ # @return [Hash{Symbol => Object}] a Hash representation of the result.
55
+ def properties
56
+ {
57
+ error: error,
58
+ status: status,
59
+ value: value
60
+ }
61
+ end
62
+ alias_method :to_h, :properties
63
+
53
64
  # @return [Boolean] true if the result status is :success, otherwise false.
54
65
  def success?
55
66
  @status == :success
@@ -66,6 +77,19 @@ module Cuprum
66
77
  self.class::STATUSES
67
78
  end
68
79
 
80
+ def deprecated_compare(other)
81
+ unless %i[value status error].all? { |sym| other.respond_to?(sym) }
82
+ return false
83
+ end
84
+
85
+ tools.core_tools.deprecate 'Cuprum::Result#==',
86
+ message: 'The compared object must respond to #to_h.'
87
+
88
+ other.value == value &&
89
+ other.status == status &&
90
+ other.error == error
91
+ end
92
+
69
93
  def normalize_status(status)
70
94
  return status unless status.is_a?(String) || status.is_a?(Symbol)
71
95
 
@@ -4,25 +4,31 @@ require 'cuprum/rspec/be_a_result_matcher'
4
4
 
5
5
  module Cuprum::RSpec
6
6
  module Matchers # rubocop:disable Style/Documentation
7
- # Asserts that the object is a Cuprum::Result with status: :failure.
7
+ # Asserts that the object is a result with status: :failure.
8
+ #
9
+ # @param expected_class [Class] the expected class of result. Defaults to
10
+ # Cuprum::Result.
8
11
  #
9
12
  # @return [Cuprum::RSpec::BeAResultMatcher] the generated matcher.
10
- def be_a_failing_result
11
- be_a_result.with_status(:failure)
13
+ def be_a_failing_result(expected_class = nil)
14
+ be_a_result(expected_class).with_status(:failure)
12
15
  end
13
16
 
14
17
  # Asserts that the object is a Cuprum::Result with status: :success.
15
18
  #
19
+ # @param expected_class [Class] the expected class of result. Defaults to
20
+ # Cuprum::Result.
21
+ #
16
22
  # @return [Cuprum::RSpec::BeAResultMatcher] the generated matcher.
17
- def be_a_passing_result
18
- be_a_result.with_status(:success).and_error(nil)
23
+ def be_a_passing_result(expected_class = nil)
24
+ be_a_result(expected_class).with_status(:success).and_error(nil)
19
25
  end
20
26
 
21
27
  # Asserts that the object is a Cuprum::Result.
22
28
  #
23
29
  # @return [Cuprum::RSpec::BeAResultMatcher] the generated matcher.
24
- def be_a_result
25
- Cuprum::RSpec::BeAResultMatcher.new
30
+ def be_a_result(expected_class = nil)
31
+ Cuprum::RSpec::BeAResultMatcher.new(expected_class)
26
32
  end
27
33
  end
28
34
  end
@@ -4,8 +4,68 @@ require 'cuprum/errors/operation_not_called'
4
4
  require 'cuprum/rspec'
5
5
 
6
6
  module Cuprum::RSpec
7
- # Custom matcher that asserts the actual object is a Cuprum result object with
8
- # the specified properties.
7
+ # Asserts the actual object is a result object with the specified properties.
8
+ #
9
+ # If initialized with a class, the matcher will assert that the actual object
10
+ # is an instance of that class. This can be useful for asserting that the
11
+ # result is an instance of a result subclass. If no class is given, the
12
+ # matcher asserts that the result is an object responding to
13
+ # #to_cuprum_result.
14
+ #
15
+ # The matcher also defines fluent methods for asserting on the result's
16
+ # properties:
17
+ #
18
+ # - The #with_value method asserts that the result has the specified value.
19
+ # Also aliased as #and_value.
20
+ # - The #with_error method asserts that the result has the specified error.
21
+ # Also aliased as #and_error.
22
+ # - The #with_status method asserts that the result has the specified status.
23
+ # Also aliased as #and_status.
24
+ #
25
+ # Generally speaking, you should use the #be_a_result, #be_a_passing_result,
26
+ # and #be_a_failing_result macros, rather than instantiating a
27
+ # BeAResultMatcher directly.
28
+ #
29
+ # @example Matching Any Result
30
+ # # Or use expect().to be_a_result
31
+ # matcher = Cuprum::RSpec::BeAResultMatcher.new
32
+ #
33
+ # matcher.matches?(nil) #=> false
34
+ # matcher.matches?(Cuprum::Result.new) #=> true
35
+ #
36
+ # @example Matching A Result Status
37
+ # # Or use expect().to be_a_passing_result
38
+ # matcher = Cuprum::RSpec::BeAResultMatcher.new.with_status(:success)
39
+ #
40
+ # matcher.matches?(Cuprum::Result.new(status: :failure)) #=> false
41
+ # matcher.matches?(Cuprum::Result.new(status: :success)) #=> false
42
+ #
43
+ # @example Matching A Result Value
44
+ # matcher = Cuprum::RSpec::BeAResultMatcher.new.with_value({ ok: true })
45
+ #
46
+ # matcher.matches?(Cuprum::Result.new(value: { ok: false })) #=> false
47
+ # matcher.matches?(Cuprum::Result.new(value: { ok: true })) #=> true
48
+ #
49
+ # @example Matching A Result Error
50
+ # error = Cuprum::Error.new(message: 'Something went wrong')
51
+ # matcher = Cuprum::RSpec::BeAResultMatcher.new.with_error(error)
52
+ #
53
+ # other_error = Cuprum::Error.new(message: 'Oh no')
54
+ # matcher.matches?(Cuprum::Result.new(error: other_error) #=> false
55
+ # matcher.matches?(Cuprum::Result.new(error: error) #=> true
56
+ #
57
+ # @example Matching A Result Class
58
+ # matcher = Cuprum::RSpec::BeAResultMatcher.new(CustomResult)
59
+ #
60
+ # matcher.matches?(Cuprum::Result.new) #=> false
61
+ # matcher.matches?(CustomResult.new) #=> true
62
+ #
63
+ # @example Matching Multiple Properties
64
+ # matcher =
65
+ # Cuprum::RSpec::BeAResultMatcher
66
+ # .with_status(:failure)
67
+ # .and_value({ ok: false })
68
+ # .and_error(Cuprum::Error.new(message: 'Something went wrong'))
9
69
  class BeAResultMatcher # rubocop:disable Metrics/ClassLength
10
70
  DEFAULT_VALUE = Object.new.freeze
11
71
  private_constant :DEFAULT_VALUE
@@ -13,15 +73,26 @@ module Cuprum::RSpec
13
73
  RSPEC_MATCHER_METHODS = %i[description failure_message matches?].freeze
14
74
  private_constant :RSPEC_MATCHER_METHODS
15
75
 
16
- def initialize
76
+ # @param expected_class [Class] the expected class of result. Defaults to
77
+ # Cuprum::Result.
78
+ def initialize(expected_class = nil)
79
+ @expected_class = expected_class
17
80
  @expected_error = DEFAULT_VALUE
18
81
  @expected_value = DEFAULT_VALUE
19
82
  end
20
83
 
84
+ # @return [Class] the expected class of result.
85
+ attr_reader :expected_class
86
+
21
87
  # @return [String] a short description of the matcher and expected
22
88
  # properties.
23
89
  def description
24
- message = 'be a Cuprum result'
90
+ message =
91
+ if expected_class
92
+ "be an instance of #{expected_class}"
93
+ else
94
+ 'be a Cuprum result'
95
+ end
25
96
 
26
97
  return message unless expected_properties?
27
98
 
@@ -30,7 +101,7 @@ module Cuprum::RSpec
30
101
 
31
102
  # Checks that the given actual object is not a Cuprum result.
32
103
  #
33
- # @param actual [Object] The actual object to match.
104
+ # @param actual [Object] the actual object to match.
34
105
  #
35
106
  # @return [Boolean] false if the actual object is a result; otherwise true.
36
107
  def does_not_match?(actual)
@@ -42,10 +113,12 @@ module Cuprum::RSpec
42
113
  end
43
114
 
44
115
  # @return [String] a summary message describing a failed expectation.
45
- def failure_message
116
+ def failure_message # rubocop:disable Metrics/MethodLength
46
117
  message = "expected #{actual.inspect} to #{description}"
47
118
 
48
- if !actual_is_result?
119
+ if !actual_is_result? && expected_class
120
+ "#{message}, but the object is not an instance of #{expected_class}"
121
+ elsif !actual_is_result?
49
122
  "#{message}, but the object is not a result"
50
123
  elsif actual_is_uncalled_operation?
51
124
  "#{message}, but the object is an uncalled operation"
@@ -72,7 +145,8 @@ module Cuprum::RSpec
72
145
  # @return [Boolean] true if the actual object is a result with the expected
73
146
  # properties; otherwise false.
74
147
  def matches?(actual)
75
- @actual = actual
148
+ @actual = actual
149
+ @non_matching_properties = nil
76
150
 
77
151
  actual_is_result? && !actual_is_uncalled_operation? && properties_match?
78
152
  end
@@ -125,7 +199,11 @@ module Cuprum::RSpec
125
199
  :expected_value
126
200
 
127
201
  def actual_is_result?
128
- actual.respond_to?(:to_cuprum_result)
202
+ return false unless actual.respond_to?(:to_cuprum_result)
203
+
204
+ return true unless expected_class
205
+
206
+ actual.to_cuprum_result.is_a?(expected_class)
129
207
  end
130
208
 
131
209
  def actual_is_uncalled_operation?
@@ -141,8 +219,8 @@ module Cuprum::RSpec
141
219
  def error_failure_message
142
220
  return '' if error_matches?
143
221
 
144
- "\n expected error: #{inspect_expected(expected_error)}" \
145
- "\n actual error: #{result.error.inspect}"
222
+ "#{pad_key('expected error')}#{inspect_expected(expected_error)}" \
223
+ "#{pad_key('actual error')}#{result.error.inspect}"
146
224
  end
147
225
 
148
226
  def error_matches?
@@ -153,10 +231,18 @@ module Cuprum::RSpec
153
231
  @error_matches = compare_items(expected_error, result.error)
154
232
  end
155
233
 
234
+ def expected_properties
235
+ expected = {}
236
+
237
+ expected['error'] = expected_error if expected_error?
238
+ expected['status'] = expected_status if expected_status?
239
+ expected['value'] = expected_value if expected_value?
240
+
241
+ expected
242
+ end
243
+
156
244
  def expected_properties?
157
- (expected_error? && !expected_error.nil?) ||
158
- expected_status? ||
159
- expected_value?
245
+ expected_properties.any?
160
246
  end
161
247
 
162
248
  def expected_error?
@@ -182,12 +268,23 @@ module Cuprum::RSpec
182
268
  ' positives, since any other result will match.'
183
269
  end
184
270
 
185
- # rubocop:disable Metrics/AbcSize
271
+ def non_matching_properties
272
+ @non_matching_properties ||=
273
+ expected_properties
274
+ .each_key
275
+ .reject { |key| send(:"#{key}_matches?") }
276
+ end
277
+
278
+ def pad_key(str)
279
+ # Value 11 from " expected " prefix.
280
+ len = 11 + non_matching_properties.map(&:length).max
281
+
282
+ "\n#{format("%#{len}s", str)}: "
283
+ end
284
+
186
285
  def properties_description
187
286
  msg = ''
188
- ary = []
189
- ary << 'value' if expected_value?
190
- ary << 'error' if expected_error? && !expected_error.nil?
287
+ ary = expected_properties.except('status').keys
191
288
 
192
289
  unless ary.empty?
193
290
  msg = "with the expected #{tools.array_tools.humanize_list(ary)}"
@@ -199,34 +296,28 @@ module Cuprum::RSpec
199
296
 
200
297
  msg + " and status: #{expected_status.inspect}"
201
298
  end
202
- # rubocop:enable Metrics/AbcSize
203
299
 
204
300
  def properties_failure_message
205
- properties_short_message +
206
- status_failure_message +
207
- value_failure_message +
208
- error_failure_message
301
+ expected_properties
302
+ .each_key
303
+ .map { |key| send(:"#{key}_failure_message") }
304
+ .unshift(properties_short_message)
305
+ .join
209
306
  end
210
307
 
211
308
  def properties_match?
212
- error_matches? && status_matches? && value_matches?
309
+ non_matching_properties.empty?
213
310
  end
214
311
 
215
312
  def properties_short_message
216
- ary = []
217
- ary << 'status' unless status_matches?
218
- ary << 'value' unless value_matches?
219
- ary << 'error' unless error_matches?
313
+ ary = non_matching_properties
220
314
 
221
315
  ", but the #{tools.array_tools.humanize_list(ary)}" \
222
316
  " #{tools.integer_tools.pluralize(ary.size, 'does', 'do')} not match:"
223
317
  end
224
318
 
225
319
  def properties_warning
226
- ary = []
227
- ary << 'value' if expected_value?
228
- ary << 'status' if expected_status?
229
- ary << 'error' if expected_error?
320
+ ary = expected_properties.keys
230
321
 
231
322
  return '' if ary.empty?
232
323
 
@@ -250,8 +341,8 @@ module Cuprum::RSpec
250
341
  def status_failure_message
251
342
  return '' if status_matches?
252
343
 
253
- "\n expected status: #{expected_status.inspect}" \
254
- "\n actual status: #{result.status.inspect}"
344
+ "#{pad_key('expected status')}#{expected_status.inspect}" \
345
+ "#{pad_key('actual status')}#{result.status.inspect}"
255
346
  end
256
347
 
257
348
  def status_matches?
@@ -269,8 +360,8 @@ module Cuprum::RSpec
269
360
  def value_failure_message
270
361
  return '' if value_matches?
271
362
 
272
- "\n expected value: #{inspect_expected(expected_value)}" \
273
- "\n actual value: #{result.value.inspect}"
363
+ "#{pad_key('expected value')}#{inspect_expected(expected_value)}" \
364
+ "#{pad_key('actual value')}#{result.value.inspect}"
274
365
  end
275
366
 
276
367
  def value_matches?
@@ -10,7 +10,7 @@ module Cuprum
10
10
  # Major version.
11
11
  MAJOR = 1
12
12
  # Minor version.
13
- MINOR = 1
13
+ MINOR = 2
14
14
  # Patch version.
15
15
  PATCH = 0
16
16
  # Prerelease version.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cuprum
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob "Merlin" Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-01 00:00:00.000000000 Z
11
+ date: 2023-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sleeping_king_studios-tools
@@ -24,76 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.0'
27
- - !ruby/object:Gem::Dependency
28
- name: rspec
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '3.10'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '3.10'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec-sleeping_king_studios
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2.7'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '2.7'
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.31'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.31'
69
- - !ruby/object:Gem::Dependency
70
- name: rubocop-rspec
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '2.11'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '2.11'
83
- - !ruby/object:Gem::Dependency
84
- name: simplecov
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '0.21'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '0.21'
97
27
  description: |-
98
28
  An opinionated implementation of the Command pattern for Ruby applications.
99
29
  Cuprum wraps your business logic in a consistent, object-oriented interface
@@ -159,16 +89,16 @@ require_paths:
159
89
  - lib
160
90
  required_ruby_version: !ruby/object:Gem::Requirement
161
91
  requirements:
162
- - - ">="
92
+ - - "~>"
163
93
  - !ruby/object:Gem::Version
164
- version: 2.6.0
94
+ version: '3.0'
165
95
  required_rubygems_version: !ruby/object:Gem::Requirement
166
96
  requirements:
167
97
  - - ">="
168
98
  - !ruby/object:Gem::Version
169
99
  version: '0'
170
100
  requirements: []
171
- rubygems_version: 3.4.1
101
+ rubygems_version: 3.4.14
172
102
  signing_key:
173
103
  specification_version: 4
174
104
  summary: An opinionated implementation of the Command pattern.