rspec-mocks 3.11.0 → 3.13.2

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: 12ce7d4b5f6accc3fb2b6af82646843de8ffd500352d4ca5367985e28fec0805
4
- data.tar.gz: 9643bc17b54bd21725aa39946eacda8207e327bdb592a9e0be821ee42c59bddb
3
+ metadata.gz: 560dd3294f2da199bebf87e99715e41d9a5859b2a47408121fe449b8614008e1
4
+ data.tar.gz: 0baffd51e9e653573998948c8057918b0e133caa13435026ad30a30848e15094
5
5
  SHA512:
6
- metadata.gz: 1a1fc3ad0ca28d5371d74985f236f9c222fc310743062a5e8746c5f995803bfd5b63de26b673b94460c6ec47eedfbb150425fe3bb59f0e5bdaf4d639ffe0bc0d
7
- data.tar.gz: 352761f69232f8b5afb6ac918bb596c5961017f622f2aec36052f5f69c426449a51ef2af26394951cad5c7706a165446a9d80d6e01adafeb87e4f6e0804a46af
6
+ metadata.gz: 3ec16d16de43a63521e1e89111df5b26b009e1dfa6738e802fb79c485a3894a4bdf60b63f08a5420c7a37934f367c93250f112d630f727a552c9ac86ece41b7a
7
+ data.tar.gz: 7a8e8e5b56dd7e362b7109bce4137c4f77a95eb50d1904fef38c2ff5cb28d605398b653ba9801ca8eab28b6c894d8f787275132d34193f031d7d10c305791e95
checksums.yaml.gz.sig CHANGED
Binary file
data/Changelog.md CHANGED
@@ -1,5 +1,120 @@
1
1
  ### Development
2
- [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.0...3-11-maintenance)
2
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.13.2...3-13-maintennace)
3
+
4
+ ### 3.13.2 / 2024-10-02
5
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.13.1...v3.13.2)
6
+
7
+ Bug Fixes:
8
+
9
+ * Support keyword arguments in callables passed to `and_invoke`. (Jon Rowe, #1595)
10
+
11
+ ### 3.13.1 / 2024-05-08
12
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.13.0...v3.13.1)
13
+
14
+ Bug Fixes:
15
+
16
+ * Use `RSpec::Support::Mutex` in `RSpec::Mocks::Proxy` to avoid issues from
17
+ stubbing `::Mutex#new`. (Eric Mueller, #1575)
18
+
19
+ ### 3.13.0 / 2024-02-04
20
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.7...v3.13.0)
21
+
22
+ Enhancements:
23
+
24
+ * Add an `array_excluding` matcher for arguments. (Zane Wolfgang Pickett, #1528)
25
+
26
+ ### 3.12.7 / 2024-02-04
27
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.6...v3.12.7)
28
+
29
+ Bug Fixes:
30
+
31
+ * Reduce allocations from "any_instance" style mocks. (Carlos Palhares, #1479)
32
+
33
+ ### 3.12.6 / 2023-07-11
34
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.5...v3.12.6)
35
+
36
+ Bug Fixes:
37
+
38
+ * Fix an issue with `and_call_original` when using the `method_missing` fallback
39
+ with keyword arguments. (Igor Drozdov, #1552)
40
+
41
+ ### 3.12.5 / 2023-03-30
42
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.4...v3.12.5)
43
+
44
+ Bug Fixes:
45
+
46
+ * Fix compatibility issue with Rails where active_support monkey patches `with`
47
+ when using any instance. (Lachlan Sylvester, #1540)
48
+
49
+ ### 3.12.4 / 2023-03-12
50
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.3...v3.12.4)
51
+
52
+ Bug Fixes:
53
+
54
+ * Fix an issue with asserting that Array#reverse is never called. (Brad Trick, #1533)
55
+ * Fix compatibility issue with Rails where active_support monkey patches `with`.
56
+ (Jean Boussier, #1531, #1534)
57
+
58
+ ### 3.12.3 / 2023-01-17
59
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.2...v3.12.3)
60
+
61
+ Bug Fixes:
62
+
63
+ * Fix keyword delegation in `send` for verifying doubles on Ruby 3.
64
+ (Charlie Honig, #1485)
65
+
66
+ ### 3.12.2 / 2023-01-07
67
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.1...v3.12.2)
68
+
69
+ Bug Fixes:
70
+
71
+ * Fix implementation blocks for mocks using keyword arguments on Ruby 3.2.0.
72
+ (Adam Steel, #1508)
73
+ * Fix keyword argument assertions when mocking using `with` on Ruby 3.2.0.
74
+ (Slava Kardakov, Benoit Tigeot, Phil Pirozhkov, Benoit Daloze, #1514)
75
+
76
+ ### 3.12.1 / 2022-12-10
77
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.0...v3.12.1)
78
+
79
+ Bug Fixes:
80
+
81
+ * Remove empty diff marker when a diff only contains console codes. (Jon Rowe, #1506)
82
+ * Show keyword vs hash diff marker when arguments are not `==` (Jon Rowe, #1506)
83
+ * Change check to detect frozen objects to rescue errors rather than
84
+ pre-empting by checking `frozen?` due to some objects mis-behaving.
85
+ (Keegan Roth, #1401)
86
+ * Prevent unfulfilled expectations using `expect_any_instance_of` across a class
87
+ inheritance boundary from raising rather than failing. (Jon Rowe, #1496)
88
+ * Prevent a misleading error message when using `allow(...).not_to` with
89
+ unsupported matchers. (Phil Pirozhkov, #1503)
90
+
91
+ ### 3.12.0 / 2022-10-26
92
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.2...v3.12.0)
93
+
94
+ Enhancements:
95
+
96
+ * Improve diff output when diffing keyword arguments against hashes.
97
+ (Jean Boussier, #1461)
98
+
99
+ ### 3.11.2 / 2022-10-25
100
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.1...v3.11.2)
101
+
102
+ Bug Fixes:
103
+
104
+ * Use the original implementation of `Class.new` to detect overridden definitions
105
+ of `new` rather than the owner, fixing detection of "double aliased" methods
106
+ in Ruby 3 and above. (Benoit Daloze, #1470, #1476)
107
+ * Support keyword argument semantics when constraining argument expectations using
108
+ `with` on Ruby 3.0+ with `instance_double` (Andrii Malyshko, #1473)
109
+
110
+ ### 3.11.1 / 2022-03-31
111
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.0...v3.11.1)
112
+
113
+ Bug Fixes:
114
+
115
+ * Add extra `ruby2_keywords` calls to properly designate methods using
116
+ `*args` to pass keyword around, fixes an issue with TruffleRuby.
117
+ (Benoit Daloze, #1464)
3
118
 
4
119
  ### 3.11.0 / 2022-02-09
5
120
  [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.3...v3.11.0)
data/README.md CHANGED
@@ -278,6 +278,8 @@ expect(double).to receive(:msg).with(1, duck_type(:abs, :div), "b") #2nd argumen
278
278
  expect(double).to receive(:msg).with(hash_including(:a => 5)) # first arg is a hash with a: 5 as one of the key-values
279
279
  expect(double).to receive(:msg).with(array_including(5)) # first arg is an array with 5 as one of the key-values
280
280
  expect(double).to receive(:msg).with(hash_excluding(:a => 5)) # first arg is a hash without a: 5 as one of the key-values
281
+ expect(double).to receive(:msg).with(start_with('a')) # any matcher, custom or from rspec-expectations
282
+ expect(double).to receive(:msg).with(satisfy { |data| data.dig(:a, :b, :c) == 5 }) # assert anything you want
281
283
  ```
282
284
 
283
285
  ## Receive Counts
@@ -49,7 +49,7 @@ module RSpec
49
49
  # @private
50
50
  def unfulfilled_expectations
51
51
  @chains_by_method_name.map do |method_name, chains|
52
- method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled?
52
+ method_name.to_s if ExpectationChain === chains.last && !chains.last.expectation_fulfilled?
53
53
  end.compact
54
54
  end
55
55
 
@@ -10,7 +10,7 @@ module RSpec
10
10
  #
11
11
  # This proxy sits in front of the recorder and delegates both to it
12
12
  # and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed
13
- # instance of the class, in order to propogates changes to the instances.
13
+ # instance of the class, in order to propagates changes to the instances.
14
14
  #
15
15
  # Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless
16
16
  # and is not persisted in `RSpec::Mocks.space`.
@@ -83,6 +83,15 @@ module RSpec
83
83
  end
84
84
  end
85
85
 
86
+ unless defined?(BasicObject)
87
+ class BasicObject
88
+ # Remove all methods except those expected to be defined on BasicObject
89
+ (instance_methods.map(&:to_sym) - [:__send__, :"!", :instance_eval, :==, :instance_exec, :"!=", :equal?, :__id__, :__binding__, :object_id]).each do |method|
90
+ undef_method method
91
+ end
92
+ end
93
+ end
94
+
86
95
  # @private
87
96
  # Delegates messages to each of the given targets in order to
88
97
  # provide the fluent interface that is available off of message
@@ -91,12 +100,12 @@ module RSpec
91
100
  # `targets` will typically contain 1 of the `AnyInstance::Recorder`
92
101
  # return values and N `MessageExpectation` instances (one per instance
93
102
  # of the `any_instance` klass).
94
- class FluentInterfaceProxy
103
+ class FluentInterfaceProxy < BasicObject
95
104
  def initialize(targets)
96
105
  @targets = targets
97
106
  end
98
107
 
99
- if RUBY_VERSION.to_f > 1.8
108
+ if ::RUBY_VERSION.to_f > 1.8
100
109
  def respond_to_missing?(method_name, include_private=false)
101
110
  super || @targets.first.respond_to?(method_name, include_private)
102
111
  end
@@ -156,21 +156,23 @@ module RSpec
156
156
 
157
157
  private
158
158
 
159
- def ancestor_is_an_observer?(method_name)
160
- lambda do |ancestor|
161
- unless ancestor == @klass
162
- ::RSpec::Mocks.space.
163
- any_instance_recorder_for(ancestor).already_observing?(method_name)
164
- end
165
- end
159
+ def ancestor_is_an_observer?(ancestor, method_name)
160
+ return if ancestor == @klass
161
+
162
+ ::RSpec::Mocks.space.
163
+ any_instance_recorder_for(ancestor).already_observing?(method_name)
166
164
  end
167
165
 
168
166
  def super_class_observers_for(method_name)
169
- @klass.ancestors.select(&ancestor_is_an_observer?(method_name))
167
+ @klass.ancestors.select do |ancestor|
168
+ ancestor_is_an_observer?(ancestor, method_name)
169
+ end
170
170
  end
171
171
 
172
172
  def super_class_observing?(method_name)
173
- @klass.ancestors.any?(&ancestor_is_an_observer?(method_name))
173
+ @klass.ancestors.any? do |ancestor|
174
+ ancestor_is_an_observer?(ancestor, method_name)
175
+ end
174
176
  end
175
177
 
176
178
  def normalize_chain(*args)
@@ -46,7 +46,7 @@ module RSpec
46
46
  @expected_args = expected_args
47
47
  ensure_expected_args_valid!
48
48
  end
49
- ruby2_keywords :initialize if Module.private_method_defined?(:ruby2_keywords)
49
+ ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
50
50
 
51
51
  # @api public
52
52
  # @param [Array] actual_args
@@ -61,7 +61,9 @@ module RSpec
61
61
  return false if expected_args.size != actual_args.size
62
62
 
63
63
  if RUBY_VERSION >= "3"
64
- # if both arguments end with Hashes, and if one is a keyword hash and the other is not, they don't match
64
+ # If the expectation was set with keywords, while the actual method was called with a positional hash argument, they don't match.
65
+ # If the expectation was set without keywords, e.g., with({a: 1}), then it fine to call it with either foo(a: 1) or foo({a: 1}).
66
+ # This corresponds to Ruby semantics, as if the method was def foo(options).
65
67
  if Hash === expected_args.last && Hash === actual_args.last
66
68
  if !Hash.ruby2_keywords_hash?(actual_args.last) && Hash.ruby2_keywords_hash?(expected_args.last)
67
69
  return false
@@ -71,6 +73,7 @@ module RSpec
71
73
 
72
74
  Support::FuzzyMatcher.values_match?(expected_args, actual_args)
73
75
  end
76
+ ruby2_keywords :args_match? if respond_to?(:ruby2_keywords, true)
74
77
 
75
78
  # @private
76
79
  # Resolves abstract arg placeholders like `no_args` and `any_args` into
@@ -71,6 +71,16 @@ module RSpec
71
71
  HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
72
72
  end
73
73
 
74
+ # Matches a hash that doesn't include the specified key(s) or key/value.
75
+ #
76
+ # @example
77
+ # expect(object).to receive(:message).with(hash_excluding(:key => val))
78
+ # expect(object).to receive(:message).with(hash_excluding(:key))
79
+ # expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2))
80
+ def hash_excluding(*args)
81
+ HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
82
+ end
83
+
74
84
  # Matches an array that includes the specified items at least once.
75
85
  # Ignores duplicates and additional values
76
86
  #
@@ -82,14 +92,14 @@ module RSpec
82
92
  ArrayIncludingMatcher.new(actually_an_array)
83
93
  end
84
94
 
85
- # Matches a hash that doesn't include the specified key(s) or key/value.
95
+ # Matches an array that excludes the specified items.
86
96
  #
87
97
  # @example
88
- # expect(object).to receive(:message).with(hash_excluding(:key => val))
89
- # expect(object).to receive(:message).with(hash_excluding(:key))
90
- # expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2))
91
- def hash_excluding(*args)
92
- HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
98
+ # expect(object).to receive(:message).with(array_excluding(1,2,3))
99
+ # expect(object).to receive(:message).with(array_excluding([1,2,3]))
100
+ def array_excluding(*args)
101
+ actually_an_array = Array === args.first && args.count == 1 ? args.first : args
102
+ ArrayExcludingMatcher.new(actually_an_array)
93
103
  end
94
104
 
95
105
  alias_method :hash_not_including, :hash_excluding
@@ -236,6 +246,8 @@ module RSpec
236
246
 
237
247
  def ===(actual)
238
248
  actual = actual.uniq
249
+ return true if (actual & @expected).count >= @expected.count
250
+
239
251
  @expected.uniq.all? do |expected_element|
240
252
  actual.any? do |actual_element|
241
253
  RSpec::Support::FuzzyMatcher.values_match?(expected_element, actual_element)
@@ -258,6 +270,38 @@ module RSpec
258
270
  end
259
271
  end
260
272
 
273
+ # @private
274
+ class ArrayExcludingMatcher
275
+ def initialize(unexpected)
276
+ @unexpected = unexpected.uniq
277
+ end
278
+
279
+ def ===(actual)
280
+ actual = actual.uniq
281
+ return false unless (actual & @unexpected).empty?
282
+
283
+ actual.none? do |actual_element|
284
+ @unexpected.any? do |unexpected_element|
285
+ RSpec::Support::FuzzyMatcher.values_match?(unexpected_element, actual_element)
286
+ end
287
+ end
288
+ rescue NoMethodError
289
+ false
290
+ end
291
+
292
+ def description
293
+ "array_excluding(#{formatted_unexpected_values})"
294
+ end
295
+
296
+ private
297
+
298
+ def formatted_unexpected_values
299
+ @unexpected.map do |x|
300
+ RSpec::Support.rspec_description_for_object(x)
301
+ end.join(", ")
302
+ end
303
+ end
304
+
261
305
  # @private
262
306
  class DuckTypeMatcher
263
307
  def initialize(*methods_to_respond_to)
@@ -312,7 +356,7 @@ module RSpec
312
356
  begin
313
357
  object.class.name.include?(matcher_namespace)
314
358
  rescue NoMethodError
315
- # Some objects, like BasicObject, don't implemented standard
359
+ # Some objects, like BasicObject, don't implement standard
316
360
  # reflection methods.
317
361
  false
318
362
  end
@@ -156,7 +156,7 @@ module RSpec
156
156
  end
157
157
 
158
158
  # @private
159
- # Used to track wether we are temporarily suppressing verifying partial
159
+ # Used to track whether we are temporarily suppressing verifying partial
160
160
  # doubles with `without_partial_double_verification { ... }`
161
161
  attr_accessor :temporarily_suppress_partial_double_verification
162
162
 
@@ -268,11 +268,32 @@ module RSpec
268
268
  def error_message(expectation, args_for_multiple_calls)
269
269
  expected_args = format_args(expectation.expected_args)
270
270
  actual_args = format_received_args(args_for_multiple_calls)
271
+
272
+ if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?
273
+ expected_hash = expectation.expected_args.last
274
+ actual_hash = args_for_multiple_calls.last.last
275
+ if Hash === expected_hash && Hash === actual_hash &&
276
+ (Hash.ruby2_keywords_hash?(expected_hash) != Hash.ruby2_keywords_hash?(actual_hash))
277
+
278
+ actual_description = Hash.ruby2_keywords_hash?(actual_hash) ? " (keyword arguments)" : " (options hash)"
279
+ expected_description = Hash.ruby2_keywords_hash?(expected_hash) ? " (keyword arguments)" : " (options hash)"
280
+
281
+ if actual_description != expected_description
282
+ actual_args += actual_description
283
+ expected_args += expected_description
284
+ end
285
+ end
286
+ end
287
+
271
288
  message = default_error_message(expectation, expected_args, actual_args)
272
289
 
273
290
  if args_for_multiple_calls.one?
274
291
  diff = diff_message(expectation.expected_args, args_for_multiple_calls.first)
275
- message << "\nDiff:#{diff}" unless diff.strip.empty?
292
+ if RSpec::Mocks.configuration.color?
293
+ message << "\nDiff:#{diff}" unless diff.gsub(/\e\[\d+m/, '').strip.empty?
294
+ else
295
+ message << "\nDiff:#{diff}" unless diff.strip.empty?
296
+ end
276
297
  end
277
298
 
278
299
  message
@@ -16,7 +16,7 @@ module RSpec
16
16
  @subject = nil
17
17
  end
18
18
 
19
- def name
19
+ def matcher_name
20
20
  "have_received"
21
21
  end
22
22
 
@@ -13,7 +13,7 @@ module RSpec
13
13
  @recorded_customizations = []
14
14
  end
15
15
 
16
- def name
16
+ def matcher_name
17
17
  "receive"
18
18
  end
19
19
 
@@ -55,14 +55,15 @@ module RSpec
55
55
  setup_any_instance_method_substitute(subject, :stub, block)
56
56
  end
57
57
 
58
+ own_methods = (instance_methods - superclass.instance_methods)
58
59
  MessageExpectation.public_instance_methods(false).each do |method|
59
- next if method_defined?(method)
60
+ next if own_methods.include?(method)
60
61
 
61
62
  define_method(method) do |*args, &block|
62
63
  @recorded_customizations << ExpectationCustomization.new(method, args, block)
63
64
  self
64
65
  end
65
- ruby2_keywords(method) if Module.private_method_defined?(:ruby2_keywords)
66
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
66
67
  end
67
68
 
68
69
  private
@@ -20,7 +20,7 @@ module RSpec
20
20
  end
21
21
  end
22
22
 
23
- def name
23
+ def matcher_name
24
24
  "receive_message_chain"
25
25
  end
26
26
 
@@ -10,7 +10,7 @@ module RSpec
10
10
  @backtrace_line = CallerFilter.first_non_rspec_line
11
11
  end
12
12
 
13
- def name
13
+ def matcher_name
14
14
  "receive_messages"
15
15
  end
16
16
 
@@ -68,7 +68,7 @@ module RSpec
68
68
  # counter.count # => 3
69
69
  # counter.count # => 3
70
70
  # # etc
71
- def and_return(first_value, *values)
71
+ def and_return(first_value, *values, &_block)
72
72
  raise_already_invoked_error_if_necessary(__method__)
73
73
  if negative?
74
74
  raise "`and_return` is not supported with negative message expectations"
@@ -101,12 +101,12 @@ module RSpec
101
101
  #
102
102
  # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo })
103
103
  # api.get_foo # => raises ApiTimeout
104
- # api.get_foo # => rasies ApiTimeout
104
+ # api.get_foo # => raises ApiTimeout
105
105
  # api.get_foo # => :a_foo
106
106
  # api.get_foo # => :a_foo
107
107
  # api.get_foo # => :a_foo
108
108
  # # etc
109
- def and_invoke(first_proc, *procs)
109
+ def and_invoke(first_proc, *procs, &_block)
110
110
  raise_already_invoked_error_if_necessary(__method__)
111
111
  if negative?
112
112
  raise "`and_invoke` is not supported with negative message expectations"
@@ -139,9 +139,12 @@ module RSpec
139
139
  # counter.increment
140
140
  # expect(counter.count).to eq(original_count + 1)
141
141
  def and_call_original
142
- wrap_original(__method__) do |original, *args, &block|
143
- original.call(*args, &block)
142
+ block = lambda do |original, *args, &b|
143
+ original.call(*args, &b)
144
144
  end
145
+ block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
146
+
147
+ wrap_original(__method__, &block)
145
148
  end
146
149
 
147
150
  # Decorates the stubbed method with the supplied block. The original
@@ -364,7 +367,7 @@ module RSpec
364
367
  @argument_list_matcher = ArgumentListMatcher.new(*args)
365
368
  self
366
369
  end
367
- ruby2_keywords(:with) if Module.private_method_defined?(:ruby2_keywords)
370
+ ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
368
371
 
369
372
  # Expect messages to be received in a specific order.
370
373
  #
@@ -403,7 +406,6 @@ module RSpec
403
406
  # some collaborators it delegates to for this stuff but for now this was
404
407
  # the simplest way to split the public from private stuff to make it
405
408
  # easier to publish the docs for the APIs we want published.
406
- # rubocop:disable Metrics/ModuleLength
407
409
  module ImplementationDetails
408
410
  attr_accessor :error_generator, :implementation
409
411
  attr_reader :message
@@ -461,18 +463,22 @@ module RSpec
461
463
  def matches?(message, *args)
462
464
  @message == message && @argument_list_matcher.args_match?(*args)
463
465
  end
466
+ ruby2_keywords :matches? if respond_to?(:ruby2_keywords, true)
464
467
 
465
468
  def safe_invoke(parent_stub, *args, &block)
466
469
  invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
467
470
  end
471
+ ruby2_keywords :safe_invoke if respond_to?(:ruby2_keywords, true)
468
472
 
469
473
  def invoke(parent_stub, *args, &block)
470
474
  invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
471
475
  end
476
+ ruby2_keywords :invoke if respond_to?(:ruby2_keywords, true)
472
477
 
473
478
  def invoke_without_incrementing_received_count(parent_stub, *args, &block)
474
479
  invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
475
480
  end
481
+ ruby2_keywords :invoke_without_incrementing_received_count if respond_to?(:ruby2_keywords, true)
476
482
 
477
483
  def negative?
478
484
  @expected_received_count == 0 && !@at_least
@@ -621,6 +627,7 @@ module RSpec
621
627
  @actual_received_count += increment
622
628
  end
623
629
  end
630
+ ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true)
624
631
 
625
632
  def has_been_invoked?
626
633
  @actual_received_count > 0
@@ -678,7 +685,6 @@ module RSpec
678
685
  nil
679
686
  end
680
687
  end
681
- # rubocop:enable Metrics/ModuleLength
682
688
 
683
689
  include ImplementationDetails
684
690
  end
@@ -742,6 +748,7 @@ module RSpec
742
748
 
743
749
  proc.call(*args, &block)
744
750
  end
751
+ ruby2_keywords(:call) if respond_to?(:ruby2_keywords, true)
745
752
  end
746
753
 
747
754
  # Represents a configured implementation. Takes into account
@@ -755,6 +762,7 @@ module RSpec
755
762
  action.call(*args, &block)
756
763
  end.last
757
764
  end
765
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
758
766
 
759
767
  def present?
760
768
  actions.any?
@@ -800,6 +808,7 @@ module RSpec
800
808
  def call(*args, &block)
801
809
  @block.call(@method, *args, &block)
802
810
  end
811
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
803
812
 
804
813
  private
805
814
 
@@ -2,6 +2,9 @@ module RSpec
2
2
  module Mocks
3
3
  # @private
4
4
  class MethodDouble
5
+ # @private TODO: drop in favor of FrozenError in ruby 2.5+
6
+ FROZEN_ERROR_MSG = /can't modify frozen/
7
+
5
8
  # @private
6
9
  attr_reader :method_name, :object, :expectations, :stubs, :method_stasher
7
10
 
@@ -23,10 +26,7 @@ module RSpec
23
26
  # handler of the object. This accounts for cases where the user has not
24
27
  # correctly defined `respond_to?`, and also 1.8 which does not provide
25
28
  # method handles for missing methods even if `respond_to?` is correct.
26
- @original_implementation_callable ||= original_method ||
27
- Proc.new do |*args, &block|
28
- @object.__send__(:method_missing, @method_name, *args, &block)
29
- end
29
+ @original_implementation_callable ||= original_method || method_missing_block
30
30
  end
31
31
 
32
32
  alias_method :save_original_implementation_callable!, :original_implementation_callable
@@ -37,6 +37,16 @@ module RSpec
37
37
  @proxy.original_method_handle_for(method_name)
38
38
  end
39
39
 
40
+ # @private
41
+ def method_missing_block
42
+ block = Proc.new do |*args, &b|
43
+ @object.__send__(:method_missing, @method_name, *args, &b)
44
+ end
45
+ block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
46
+
47
+ block
48
+ end
49
+
40
50
  # @private
41
51
  def visibility
42
52
  @proxy.visibility_for(@method_name)
@@ -63,11 +73,21 @@ module RSpec
63
73
  define_method(method_name) do |*args, &block|
64
74
  method_double.proxy_method_invoked(self, *args, &block)
65
75
  end
76
+ # This can't be `if respond_to?(:ruby2_keywords, true)`,
77
+ # see https://github.com/rspec/rspec-mocks/pull/1385#issuecomment-755340298
66
78
  ruby2_keywords(method_name) if Module.private_method_defined?(:ruby2_keywords)
67
79
  __send__(visibility, method_name)
68
80
  end
69
81
 
70
82
  @method_is_proxied = true
83
+ rescue RuntimeError, TypeError => e
84
+ # TODO: drop in favor of FrozenError in ruby 2.5+
85
+ # RuntimeError (and FrozenError) for ruby 2.x
86
+ # TypeError for ruby 1.x
87
+ if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message
88
+ raise ArgumentError, "Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations."
89
+ end
90
+ raise
71
91
  end
72
92
 
73
93
  # The implementation of the proxied method. Subclasses may override this
@@ -77,10 +97,10 @@ module RSpec
77
97
  def proxy_method_invoked(_obj, *args, &block)
78
98
  @proxy.message_received method_name, *args, &block
79
99
  end
100
+ ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
80
101
 
81
102
  # @private
82
103
  def restore_original_method
83
- return show_frozen_warning if object_singleton_class.frozen?
84
104
  return unless @method_is_proxied
85
105
 
86
106
  remove_method_from_definition_target
@@ -88,6 +108,14 @@ module RSpec
88
108
  restore_original_visibility
89
109
 
90
110
  @method_is_proxied = false
111
+ rescue RuntimeError, TypeError => e
112
+ # TODO: drop in favor of FrozenError in ruby 2.5+
113
+ # RuntimeError (and FrozenError) for ruby 2.x
114
+ # TypeError for ruby 1.x
115
+ if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message
116
+ return show_frozen_warning
117
+ end
118
+ raise
91
119
  end
92
120
 
93
121
  # @private
@@ -185,11 +185,23 @@ module RSpec
185
185
  def self.applies_to?(method_name)
186
186
  return false unless method_name == :new
187
187
  klass = yield
188
- return false unless klass.respond_to?(:new, true)
188
+ return false unless ::Class === klass && klass.respond_to?(:new, true)
189
189
 
190
190
  # We only want to apply our special logic to normal `new` methods.
191
191
  # Methods that the user has monkeyed with should be left as-is.
192
- ::RSpec::Support.method_handle_for(klass, :new).owner == ::Class
192
+ uses_class_new?(klass)
193
+ end
194
+
195
+ if RUBY_VERSION.to_i >= 3
196
+ CLASS_NEW = ::Class.instance_method(:new)
197
+
198
+ def self.uses_class_new?(klass)
199
+ ::RSpec::Support.method_handle_for(klass, :new) == CLASS_NEW.bind(klass)
200
+ end
201
+ else # Ruby 2's Method#== is too strict
202
+ def self.uses_class_new?(klass)
203
+ ::RSpec::Support.method_handle_for(klass, :new).owner == ::Class
204
+ end
193
205
  end
194
206
 
195
207
  def with_signature
@@ -37,7 +37,7 @@ if defined?(::Minitest::Expectation)
37
37
  # not want to here (or else we would interfere with rspec-expectations' definition).
38
38
  else
39
39
  # ...otherwise, define those methods now. If `rspec/expectations/minitest_integration`
40
- # is loaded after this file, it'll overide the defintion here.
40
+ # is loaded after this file, it'll override the definition here.
41
41
  Minitest::Expectation.class_eval do
42
42
  include RSpec::Mocks::ExpectationTargetMethods
43
43
 
@@ -81,7 +81,7 @@ module RSpec
81
81
  # Queries rspec-mocks to find out information about the named constant.
82
82
  #
83
83
  # @param [String] name the name of the constant
84
- # @return [Constant] an object contaning information about the named
84
+ # @return [Constant] an object containing information about the named
85
85
  # constant.
86
86
  def self.original(name)
87
87
  mutator = ::RSpec::Mocks.space.constant_mutator_for(name)
@@ -1,3 +1,5 @@
1
+ RSpec::Support.require_rspec_support 'mutex'
2
+
1
3
  module RSpec
2
4
  module Mocks
3
5
  # @private
@@ -9,11 +11,6 @@ module RSpec
9
11
  end
10
12
  end
11
13
 
12
- unless defined?(Mutex)
13
- Support.require_rspec_support 'mutex'
14
- Mutex = Support::Mutex
15
- end
16
-
17
14
  # @private
18
15
  def ensure_implemented(*_args)
19
16
  # noop for basic proxies, see VerifyingProxy for behaviour.
@@ -27,7 +24,7 @@ module RSpec
27
24
  @order_group = order_group
28
25
  @error_generator = ErrorGenerator.new(object)
29
26
  @messages_received = []
30
- @messages_received_mutex = Mutex.new
27
+ @messages_received_mutex = Support::Mutex.new
31
28
  @options = options
32
29
  @null_object = false
33
30
  @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
@@ -35,15 +32,9 @@ module RSpec
35
32
 
36
33
  # @private
37
34
  def ensure_can_be_proxied!(object)
38
- return unless object.is_a?(Symbol) || object.frozen?
39
- return if object.nil?
35
+ return unless object.is_a?(Symbol)
40
36
 
41
- msg = "Cannot proxy frozen objects"
42
- if Symbol === object
43
- msg << ". Symbols such as #{object} cannot be mocked or stubbed."
44
- else
45
- msg << ", rspec-mocks relies on proxies for method stubbing and expectations."
46
- end
37
+ msg = "Cannot proxy frozen objects. Symbols such as #{object} cannot be mocked or stubbed."
47
38
  raise ArgumentError, msg
48
39
  end
49
40
 
@@ -198,6 +189,7 @@ module RSpec
198
189
  @messages_received << [message, args, block]
199
190
  end
200
191
  end
192
+ ruby2_keywords :record_message_received if respond_to?(:ruby2_keywords, true)
201
193
 
202
194
  # @private
203
195
  def message_received(message, *args, &block)
@@ -230,6 +222,7 @@ module RSpec
230
222
  @object.__send__(:method_missing, message, *args, &block)
231
223
  end
232
224
  end
225
+ ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
233
226
 
234
227
  # @private
235
228
  def raise_unexpected_message_error(method_name, args)
@@ -279,12 +272,14 @@ module RSpec
279
272
  expectation.matches?(method_name, *args)
280
273
  end
281
274
  end
275
+ ruby2_keywords :find_matching_expectation if respond_to?(:ruby2_keywords, true)
282
276
 
283
277
  def find_almost_matching_expectation(method_name, *args)
284
278
  find_best_matching_expectation_for(method_name) do |expectation|
285
279
  expectation.matches_name_but_not_args(method_name, *args)
286
280
  end
287
281
  end
282
+ ruby2_keywords :find_almost_matching_expectation if respond_to?(:ruby2_keywords, true)
288
283
 
289
284
  def find_best_matching_expectation_for(method_name)
290
285
  first_match = nil
@@ -301,10 +296,12 @@ module RSpec
301
296
  def find_matching_method_stub(method_name, *args)
302
297
  method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
303
298
  end
299
+ ruby2_keywords :find_matching_method_stub if respond_to?(:ruby2_keywords, true)
304
300
 
305
301
  def find_almost_matching_stub(method_name, *args)
306
302
  method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
307
303
  end
304
+ ruby2_keywords :find_almost_matching_stub if respond_to?(:ruby2_keywords, true)
308
305
  end
309
306
 
310
307
  # @private
@@ -360,6 +357,7 @@ module RSpec
360
357
  end
361
358
  super
362
359
  end
360
+ ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
363
361
 
364
362
  private
365
363
 
@@ -77,9 +77,9 @@ module RSpec
77
77
 
78
78
  def reset_all
79
79
  proxies.each_value { |proxy| proxy.reset }
80
- @constant_mutators.reverse.each { |mut| mut.idempotently_reset }
81
80
  any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! }
82
81
  any_instance_recorders.clear
82
+ @constant_mutators.reverse.each { |mut| mut.idempotently_reset }
83
83
  end
84
84
 
85
85
  def register_constant_mutator(mutator)
@@ -115,7 +115,7 @@ module RSpec
115
115
  Matchers::Receive.new(method_name, block)
116
116
  end
117
117
 
118
- def receive_messages(message_return_value_hash)
118
+ def receive_messages(message_return_value_hash, &_block)
119
119
  matcher = Matchers::ReceiveMessages.new(message_return_value_hash)
120
120
  matcher.warn_about_block if block_given?
121
121
  matcher
@@ -54,7 +54,7 @@ module RSpec
54
54
 
55
55
  def raise_negation_unsupported(method_name, matcher)
56
56
  raise NegationUnsupportedError,
57
- "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \
57
+ "`#{expression}(...).#{method_name} #{matcher.matcher_name}` is not supported since it " \
58
58
  "doesn't really make sense. What would it even mean?"
59
59
  end
60
60
  end
@@ -42,11 +42,13 @@ module RSpec
42
42
  ensure
43
43
  @__sending_message = nil
44
44
  end
45
+ ruby2_keywords :__send__ if respond_to?(:ruby2_keywords, true)
45
46
  $VERBOSE = old
46
47
 
47
48
  def send(name, *args, &block)
48
49
  __send__(name, *args, &block)
49
50
  end
51
+ ruby2_keywords :send if respond_to?(:ruby2_keywords, true)
50
52
 
51
53
  def initialize(doubled_module, *args)
52
54
  @doubled_module = doubled_module
@@ -31,6 +31,7 @@ module RSpec
31
31
  end
32
32
  end
33
33
  end
34
+ ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
34
35
 
35
36
  private
36
37
 
@@ -57,7 +57,7 @@ module RSpec
57
57
  # A verifying proxy mostly acts like a normal proxy, except that it
58
58
  # contains extra logic to try and determine the validity of any expectation
59
59
  # set on it. This includes whether or not methods have been defined and the
60
- # validatiy of arguments on method calls.
60
+ # validity of arguments on method calls.
61
61
  #
62
62
  # In all other ways this behaves like a normal proxy. It only adds the
63
63
  # verification behaviour to specific methods then delegates to the parent
@@ -147,12 +147,12 @@ module RSpec
147
147
  end
148
148
 
149
149
  def add_expectation(*args, &block)
150
- # explict params necessary for 1.8.7 see #626
150
+ # explicit params necessary for 1.8.7 see #626
151
151
  super(*args, &block).tap { |x| x.method_reference = @method_reference }
152
152
  end
153
153
 
154
154
  def add_stub(*args, &block)
155
- # explict params necessary for 1.8.7 see #626
155
+ # explicit params necessary for 1.8.7 see #626
156
156
  super(*args, &block).tap { |x| x.method_reference = @method_reference }
157
157
  end
158
158
 
@@ -160,6 +160,7 @@ module RSpec
160
160
  validate_arguments!(args)
161
161
  super
162
162
  end
163
+ ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
163
164
 
164
165
  def validate_arguments!(actual_args)
165
166
  @method_reference.with_signature do |signature|
@@ -3,7 +3,7 @@ module RSpec
3
3
  # Version information for RSpec mocks.
4
4
  module Version
5
5
  # Version of RSpec mocks currently in use in SemVer format.
6
- STRING = '3.11.0'
6
+ STRING = '3.13.2'
7
7
  end
8
8
  end
9
9
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-mocks
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.11.0
4
+ version: 3.13.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Baker
8
8
  - David Chelimsky
9
9
  - Myron Marston
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain:
13
12
  - |
@@ -45,7 +44,7 @@ cert_chain:
45
44
  ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
46
45
  F3MdtaDehhjC
47
46
  -----END CERTIFICATE-----
48
- date: 2022-02-09 00:00:00.000000000 Z
47
+ date: 2024-10-02 00:00:00.000000000 Z
49
48
  dependencies:
50
49
  - !ruby/object:Gem::Dependency
51
50
  name: rspec-support
@@ -53,14 +52,14 @@ dependencies:
53
52
  requirements:
54
53
  - - "~>"
55
54
  - !ruby/object:Gem::Version
56
- version: 3.11.0
55
+ version: 3.13.0
57
56
  type: :runtime
58
57
  prerelease: false
59
58
  version_requirements: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - "~>"
62
61
  - !ruby/object:Gem::Version
63
- version: 3.11.0
62
+ version: 3.13.0
64
63
  - !ruby/object:Gem::Dependency
65
64
  name: diff-lcs
66
65
  requirement: !ruby/object:Gem::Requirement
@@ -113,16 +112,22 @@ dependencies:
113
112
  name: aruba
114
113
  requirement: !ruby/object:Gem::Requirement
115
114
  requirements:
116
- - - "~>"
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 1.1.0
118
+ - - "<"
117
119
  - !ruby/object:Gem::Version
118
- version: 0.14.10
120
+ version: 3.0.0
119
121
  type: :development
120
122
  prerelease: false
121
123
  version_requirements: !ruby/object:Gem::Requirement
122
124
  requirements:
123
- - - "~>"
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: 1.1.0
128
+ - - "<"
124
129
  - !ruby/object:Gem::Version
125
- version: 0.14.10
130
+ version: 3.0.0
126
131
  - !ruby/object:Gem::Dependency
127
132
  name: minitest
128
133
  requirement: !ruby/object:Gem::Requirement
@@ -194,11 +199,10 @@ licenses:
194
199
  - MIT
195
200
  metadata:
196
201
  bug_tracker_uri: https://github.com/rspec/rspec-mocks/issues
197
- changelog_uri: https://github.com/rspec/rspec-mocks/blob/v3.11.0/Changelog.md
202
+ changelog_uri: https://github.com/rspec/rspec-mocks/blob/v3.13.2/Changelog.md
198
203
  documentation_uri: https://rspec.info/documentation/
199
204
  mailing_list_uri: https://groups.google.com/forum/#!forum/rspec
200
205
  source_code_uri: https://github.com/rspec/rspec-mocks
201
- post_install_message:
202
206
  rdoc_options:
203
207
  - "--charset=UTF-8"
204
208
  require_paths:
@@ -214,8 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
218
  - !ruby/object:Gem::Version
215
219
  version: '0'
216
220
  requirements: []
217
- rubygems_version: 3.3.3
218
- signing_key:
221
+ rubygems_version: 3.6.0.dev
219
222
  specification_version: 4
220
- summary: rspec-mocks-3.11.0
223
+ summary: rspec-mocks-3.13.2
221
224
  test_files: []
metadata.gz.sig CHANGED
Binary file