rspec-mocks 3.9.1 → 3.12.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2e1e958a5a621513f9854bca7ac6fc45048e3ef493a78ed1a748bd8f4e87430
4
- data.tar.gz: 56c6a88292c8fb57b3fd4de0adbc8ce7c5cabc4ec22a3374e1c13aae846db988
3
+ metadata.gz: efbe21159038369570929be696cd7f44b23c367f6e96d3c037aa791454a1c739
4
+ data.tar.gz: 7a5d840187a13422c78b8ac6fffc205bd6b4841b8ba2c150be8026a5e650d381
5
5
  SHA512:
6
- metadata.gz: '098987b9f29a04beb11feb2a154fc8bb7b243e3a9db8fa163441118ac8eb04d48ce6e777b8c1046bc264f4aa1160a228379fd6eecf5f17abeaf574503a720991'
7
- data.tar.gz: 1f0c7da266382660d474e09ca064b0fc8252d5ed5be4c1b9569e3ee92767931a16e70ebc82f8784cb3c468c9c98b1674ca79f526fa179e9c06b6c29d481dd914
6
+ metadata.gz: 325a0c1eee0cd639bb0645f927bae2a41793eaaaadc9871046906f1101d515585dbea17320d56840ff9741d19a61c9203c2fa9073297a10f9ba90fb21b068c5a
7
+ data.tar.gz: 3334fed9cb0f29768ed9faa0d335572a42484f4930f7bc2f837045a6d7f250d9357059468fed3eb67aa67464cddb4a728e95beb21e0c8c282a75f731167c4fcb
checksums.yaml.gz.sig CHANGED
Binary file
data/Changelog.md CHANGED
@@ -1,3 +1,124 @@
1
+ ### Development
2
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.4...3-12-maintenance)
3
+
4
+ ### 3.12.4 / 2023-01-17
5
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.3...v3.12.4)
6
+
7
+ Bug Fixes:
8
+
9
+ * Fix an issue with asserting that Array#reverse is never called. (Brad Trick, #1533)
10
+ * Fix compatibility issue with Rails where active_support monkey patches `with`.
11
+ (Jean Boussier, #1531, #1534)
12
+
13
+ ### 3.12.3 / 2023-01-17
14
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.2...v3.12.3)
15
+
16
+ Bug Fixes:
17
+
18
+ * Fix keyword delegation in `send` for verifying doubles on Ruby 3.
19
+ (Charlie Honig, #1485)
20
+
21
+ ### 3.12.2 / 2023-01-07
22
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.1...v3.12.2)
23
+
24
+ Bug Fixes:
25
+
26
+ * Fix implementation blocks for mocks using keyword arguments on Ruby 3.2.0.
27
+ (Adam Steel, #1508)
28
+ * Fix keyword argument assertions when mocking using `with` on Ruby 3.2.0.
29
+ (Slava Kardakov, Benoit Tigeot, Phil Pirozhkov, Benoit Daloze, #1514)
30
+
31
+ ### 3.12.1 / 2022-12-10
32
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.12.0...v3.12.1)
33
+
34
+ Bug Fixes:
35
+
36
+ * Remove empty diff marker when a diff only contains console codes. (Jon Rowe, #1506)
37
+ * Show keyword vs hash diff marker when arguments are not `==` (Jon Rowe, #1506)
38
+ * Change check to detect frozen objects to rescue errors rather than
39
+ pre-empting by checking `frozen?` due to some objects mis-behaving.
40
+ (Keegan Roth, #1401)
41
+ * Prevent unfulfilled expectations using `expect_any_instance_of` across a class
42
+ inheritance boundary from raising rather than failing. (Jon Rowe, #1496)
43
+ * Prevent a misleading error message when using `allow(...).not_to` with
44
+ unsupported matchers. (Phil Pirozhkov, #1503)
45
+
46
+ ### 3.12.0 / 2022-10-26
47
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.2...v3.12.0)
48
+
49
+ Enhancements:
50
+
51
+ * Improve diff output when diffing keyword arguments against hashes.
52
+ (Jean Boussier, #1461)
53
+
54
+ ### 3.11.2 / 2022-10-25
55
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.1...v3.11.2)
56
+
57
+ Bug Fixes:
58
+
59
+ * Use the original implementation of `Class.new` to detect overridden definitions
60
+ of `new` rather than the owner, fixing detection of "double aliased" methods
61
+ in Ruby 3 and above. (Benoit Daloze, #1470, #1476)
62
+ * Support keyword argument semantics when constraining argument expectations using
63
+ `with` on Ruby 3.0+ with `instance_double` (Andrii Malyshko, #1473)
64
+
65
+ ### 3.11.1 / 2022-03-31
66
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.11.0...v3.11.1)
67
+
68
+ Bug Fixes:
69
+
70
+ * Add extra `ruby2_keywords` calls to properly designate methods using
71
+ `*args` to pass keyword around, fixes an issue with TruffleRuby.
72
+ (Benoit Daloze, #1464)
73
+
74
+ ### 3.11.0 / 2022-02-09
75
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.3...v3.11.0)
76
+
77
+ Enhancements:
78
+
79
+ * Add `and_invoke` implementation for configuring responses to `receive`
80
+ (and `receive_messages`) with multiple callable objects. (Kyle Smith, #1411)
81
+
82
+ ### 3.10.3 / 2022-01-28
83
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.2...v3.10.3)
84
+
85
+ Bug Fixes:
86
+
87
+ * Suppress warning by setting `$VERBOSE` to nil. (Nobuyoshi Nakada, #1414)
88
+ * Support keyword argument semantics when constraining argument expectations using
89
+ `with` on Ruby 3.0+ (Yusuke Endoh, #1394)
90
+
91
+ ### 3.10.2 / 2021-01-27
92
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.1...v3.10.2)
93
+
94
+ Bug Fixes:
95
+
96
+ * Support keyword arguments with `and_call_original` on Ruby 3.0.
97
+ (Bryan Powell, #1385)
98
+ * `RSpec::Mocks::Constant#previously_defined?` is now always a boolean.
99
+ (Phil Pirozhkov, #1397)
100
+ * Support keyword arguments on Ruby 3.0 when used with `expect_any_instance_of`
101
+ or `allow_any_instance_of` with `and_call_original`.
102
+ (Jess Hottenstein, #1407)
103
+
104
+ ### 3.10.1 / 2020-12-27
105
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.10.0...v3.10.1)
106
+
107
+ Bug Fixes:
108
+
109
+ * Issue `ArgumentError` rather than `TypeError` when unsupported methods on
110
+ unsupported objects are attempted to be stubbed. (@zhisme, #1357)
111
+
112
+ ### 3.10.0 / 2020-10-30
113
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.1...v3.10.0)
114
+
115
+ Enhancements:
116
+ * Add the ability to set a custom error generator in `MessageExpectation`.
117
+ This will allow rspec-expectations to inject a custom failure message.
118
+ (Benoit Tigeot and Nicolas Zermati, #1312)
119
+ * Return the result of the block passed to `RSpec::Mocks.with_temporary_scope`
120
+ when block run. (@expeehaa, #1329)
121
+
1
122
  ### 3.9.1 / 2019-12-31
2
123
  [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.0...v3.9.1)
3
124
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RSpec Mocks [![Build Status](https://secure.travis-ci.org/rspec/rspec-mocks.svg?branch=master)](http://travis-ci.org/rspec/rspec-mocks) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks)
1
+ # RSpec Mocks [![Build Status](https://github.com/rspec/rspec-mocks/workflows/RSpec%20CI/badge.svg)](https://github.com/rspec/rspec-mocks/actions) [![Code Climate](https://codeclimate.com/github/rspec/rspec-mocks.svg)](https://codeclimate.com/github/rspec/rspec-mocks)
2
2
  rspec-mocks is a test-double framework for rspec with support for method stubs,
3
3
  fakes, and message expectations on generated test-doubles and real objects
4
4
  alike.
@@ -8,12 +8,12 @@ alike.
8
8
  gem install rspec # for rspec-core, rspec-expectations, rspec-mocks
9
9
  gem install rspec-mocks # for rspec-mocks only
10
10
 
11
- Want to run against the `master` branch? You'll need to include the dependent
11
+ Want to run against the `main` branch? You'll need to include the dependent
12
12
  RSpec repos as well. Add the following to your `Gemfile`:
13
13
 
14
14
  ```ruby
15
15
  %w[rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib|
16
- gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'master'
16
+ gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main'
17
17
  end
18
18
  ```
19
19
  ## Contributing
@@ -53,7 +53,7 @@ book = instance_double("Book", :pages => 250)
53
53
  Verifying doubles have some clever tricks to enable you to both test in
54
54
  isolation without your dependencies loaded while still being able to validate
55
55
  them against real objects. More detail is available in [their
56
- documentation](https://github.com/rspec/rspec-mocks/blob/master/features/verifying_doubles).
56
+ documentation](https://github.com/rspec/rspec-mocks/blob/main/features/verifying_doubles).
57
57
 
58
58
  Verifying doubles can also accept custom identifiers, just like double(), e.g.:
59
59
 
@@ -403,7 +403,7 @@ your code.
403
403
  ## Stubbing and Hiding Constants
404
404
 
405
405
  See the [mutating constants
406
- README](https://github.com/rspec/rspec-mocks/blob/master/features/mutating_constants/README.md)
406
+ README](https://github.com/rspec/rspec-mocks/blob/main/features/mutating_constants/README.md)
407
407
  for info on this feature.
408
408
 
409
409
  ## Use `before(:example)`, not `before(:context)`
@@ -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`.
@@ -261,6 +261,7 @@ module RSpec
261
261
  recorder.playback!(self, method_name)
262
262
  __send__(method_name, *args, &blk)
263
263
  end
264
+ @klass.__send__(:ruby2_keywords, method_name) if @klass.respond_to?(:ruby2_keywords, true)
264
265
  end
265
266
 
266
267
  def mark_invoked!(method_name)
@@ -46,17 +46,34 @@ module RSpec
46
46
  @expected_args = expected_args
47
47
  ensure_expected_args_valid!
48
48
  end
49
+ ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
49
50
 
50
51
  # @api public
51
- # @param [Array] args
52
+ # @param [Array] actual_args
52
53
  #
53
54
  # Matches each element in the `expected_args` against the element in the same
54
55
  # position of the arguments passed to `new`.
55
56
  #
56
57
  # @see #initialize
57
- def args_match?(*args)
58
- Support::FuzzyMatcher.values_match?(resolve_expected_args_based_on(args), args)
58
+ def args_match?(*actual_args)
59
+ expected_args = resolve_expected_args_based_on(actual_args)
60
+
61
+ return false if expected_args.size != actual_args.size
62
+
63
+ if RUBY_VERSION >= "3"
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).
67
+ if Hash === expected_args.last && Hash === actual_args.last
68
+ if !Hash.ruby2_keywords_hash?(actual_args.last) && Hash.ruby2_keywords_hash?(expected_args.last)
69
+ return false
70
+ end
71
+ end
72
+ end
73
+
74
+ Support::FuzzyMatcher.values_match?(expected_args, actual_args)
59
75
  end
76
+ ruby2_keywords :args_match? if respond_to?(:ruby2_keywords, true)
60
77
 
61
78
  # @private
62
79
  # Resolves abstract arg placeholders like `no_args` and `any_args` into
@@ -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,13 +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
66
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
65
67
  end
66
68
 
67
69
  private
@@ -13,14 +13,14 @@ module RSpec
13
13
  @recorded_customizations = []
14
14
  end
15
15
 
16
- [:with, :and_return, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg|
16
+ [:with, :and_return, :and_invoke, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg|
17
17
  define_method(msg) do |*args, &block|
18
18
  @recorded_customizations << ExpectationCustomization.new(msg, args, block)
19
19
  self
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
 
@@ -53,7 +53,7 @@ module RSpec
53
53
  # etc.
54
54
  #
55
55
  # If the message is received more times than there are values, the last
56
- # value is received for every subsequent call.
56
+ # value is returned for every subsequent call.
57
57
  #
58
58
  # @return [nil] No further chaining is supported after this.
59
59
  # @example
@@ -85,6 +85,48 @@ module RSpec
85
85
  nil
86
86
  end
87
87
 
88
+ # Tells the object to invoke a Proc when it receives the message. Given
89
+ # more than one value, the result of the first Proc is returned the first
90
+ # time the message is received, the result of the second Proc is returned
91
+ # the next time, etc, etc.
92
+ #
93
+ # If the message is received more times than there are Procs, the result of
94
+ # the last Proc is returned for every subsequent call.
95
+ #
96
+ # @return [nil] No further chaining is supported after this.
97
+ # @example
98
+ # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout })
99
+ # api.get_foo # => raises ApiTimeout
100
+ # api.get_foo # => raises ApiTimeout
101
+ #
102
+ # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo })
103
+ # api.get_foo # => raises ApiTimeout
104
+ # api.get_foo # => rasies ApiTimeout
105
+ # api.get_foo # => :a_foo
106
+ # api.get_foo # => :a_foo
107
+ # api.get_foo # => :a_foo
108
+ # # etc
109
+ def and_invoke(first_proc, *procs)
110
+ raise_already_invoked_error_if_necessary(__method__)
111
+ if negative?
112
+ raise "`and_invoke` is not supported with negative message expectations"
113
+ end
114
+
115
+ if block_given?
116
+ raise ArgumentError, "Implementation blocks aren't supported with `and_invoke`"
117
+ end
118
+
119
+ procs.unshift(first_proc)
120
+ if procs.any? { |p| !p.respond_to?(:call) }
121
+ raise ArgumentError, "Arguments to `and_invoke` must be callable."
122
+ end
123
+
124
+ @expected_received_count = [@expected_received_count, procs.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
125
+ self.terminal_implementation_action = AndInvokeImplementation.new(procs)
126
+
127
+ nil
128
+ end
129
+
88
130
  # Tells the object to delegate to the original unmodified method
89
131
  # when it receives the message.
90
132
  #
@@ -97,9 +139,12 @@ module RSpec
97
139
  # counter.increment
98
140
  # expect(counter.count).to eq(original_count + 1)
99
141
  def and_call_original
100
- wrap_original(__method__) do |original, *args, &block|
101
- original.call(*args, &block)
142
+ block = lambda do |original, *args, &b|
143
+ original.call(*args, &b)
102
144
  end
145
+ block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
146
+
147
+ wrap_original(__method__, &block)
103
148
  end
104
149
 
105
150
  # Decorates the stubbed method with the supplied block. The original
@@ -322,6 +367,7 @@ module RSpec
322
367
  @argument_list_matcher = ArgumentListMatcher.new(*args)
323
368
  self
324
369
  end
370
+ ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
325
371
 
326
372
  # Expect messages to be received in a specific order.
327
373
  #
@@ -360,13 +406,12 @@ module RSpec
360
406
  # some collaborators it delegates to for this stuff but for now this was
361
407
  # the simplest way to split the public from private stuff to make it
362
408
  # easier to publish the docs for the APIs we want published.
363
- # rubocop:disable Metrics/ModuleLength
364
409
  module ImplementationDetails
365
410
  attr_accessor :error_generator, :implementation
366
411
  attr_reader :message
367
412
  attr_reader :orig_object
368
413
  attr_writer :expected_received_count, :expected_from, :argument_list_matcher
369
- protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
414
+ protected :expected_received_count=, :expected_from=, :error_generator=, :implementation=
370
415
 
371
416
  # @private
372
417
  attr_reader :type
@@ -376,7 +421,7 @@ module RSpec
376
421
  type=:expectation, opts={}, &implementation_block)
377
422
  @type = type
378
423
  @error_generator = error_generator
379
- @error_generator.opts = opts
424
+ @error_generator.opts = error_generator.opts.merge(opts)
380
425
  @expected_from = expected_from
381
426
  @method_double = method_double
382
427
  @orig_object = @method_double.object
@@ -418,18 +463,22 @@ module RSpec
418
463
  def matches?(message, *args)
419
464
  @message == message && @argument_list_matcher.args_match?(*args)
420
465
  end
466
+ ruby2_keywords :matches? if respond_to?(:ruby2_keywords, true)
421
467
 
422
468
  def safe_invoke(parent_stub, *args, &block)
423
469
  invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
424
470
  end
471
+ ruby2_keywords :safe_invoke if respond_to?(:ruby2_keywords, true)
425
472
 
426
473
  def invoke(parent_stub, *args, &block)
427
474
  invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
428
475
  end
476
+ ruby2_keywords :invoke if respond_to?(:ruby2_keywords, true)
429
477
 
430
478
  def invoke_without_incrementing_received_count(parent_stub, *args, &block)
431
479
  invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
432
480
  end
481
+ ruby2_keywords :invoke_without_incrementing_received_count if respond_to?(:ruby2_keywords, true)
433
482
 
434
483
  def negative?
435
484
  @expected_received_count == 0 && !@at_least
@@ -578,6 +627,7 @@ module RSpec
578
627
  @actual_received_count += increment
579
628
  end
580
629
  end
630
+ ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true)
581
631
 
582
632
  def has_been_invoked?
583
633
  @actual_received_count > 0
@@ -635,7 +685,6 @@ module RSpec
635
685
  nil
636
686
  end
637
687
  end
638
- # rubocop:enable Metrics/ModuleLength
639
688
 
640
689
  include ImplementationDetails
641
690
  end
@@ -683,6 +732,24 @@ module RSpec
683
732
  end
684
733
  end
685
734
 
735
+ # Handles the implementation of an `and_invoke` implementation.
736
+ # @private
737
+ class AndInvokeImplementation
738
+ def initialize(procs_to_invoke)
739
+ @procs_to_invoke = procs_to_invoke
740
+ end
741
+
742
+ def call(*args, &block)
743
+ proc = if @procs_to_invoke.size > 1
744
+ @procs_to_invoke.shift
745
+ else
746
+ @procs_to_invoke.first
747
+ end
748
+
749
+ proc.call(*args, &block)
750
+ end
751
+ end
752
+
686
753
  # Represents a configured implementation. Takes into account
687
754
  # any number of sub-implementations.
688
755
  # @private
@@ -694,6 +761,7 @@ module RSpec
694
761
  action.call(*args, &block)
695
762
  end.last
696
763
  end
764
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
697
765
 
698
766
  def present?
699
767
  actions.any?
@@ -739,6 +807,7 @@ module RSpec
739
807
  def call(*args, &block)
740
808
  @block.call(@method, *args, &block)
741
809
  end
810
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
742
811
 
743
812
  private
744
813
 
@@ -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
 
@@ -63,10 +66,21 @@ module RSpec
63
66
  define_method(method_name) do |*args, &block|
64
67
  method_double.proxy_method_invoked(self, *args, &block)
65
68
  end
69
+ # This can't be `if respond_to?(:ruby2_keywords, true)`,
70
+ # see https://github.com/rspec/rspec-mocks/pull/1385#issuecomment-755340298
71
+ ruby2_keywords(method_name) if Module.private_method_defined?(:ruby2_keywords)
66
72
  __send__(visibility, method_name)
67
73
  end
68
74
 
69
75
  @method_is_proxied = true
76
+ rescue RuntimeError, TypeError => e
77
+ # TODO: drop in favor of FrozenError in ruby 2.5+
78
+ # RuntimeError (and FrozenError) for ruby 2.x
79
+ # TypeError for ruby 1.x
80
+ if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message
81
+ raise ArgumentError, "Cannot proxy frozen objects, rspec-mocks relies on proxies for method stubbing and expectations."
82
+ end
83
+ raise
70
84
  end
71
85
 
72
86
  # The implementation of the proxied method. Subclasses may override this
@@ -76,10 +90,10 @@ module RSpec
76
90
  def proxy_method_invoked(_obj, *args, &block)
77
91
  @proxy.message_received method_name, *args, &block
78
92
  end
93
+ ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
79
94
 
80
95
  # @private
81
96
  def restore_original_method
82
- return show_frozen_warning if object_singleton_class.frozen?
83
97
  return unless @method_is_proxied
84
98
 
85
99
  remove_method_from_definition_target
@@ -87,6 +101,14 @@ module RSpec
87
101
  restore_original_visibility
88
102
 
89
103
  @method_is_proxied = false
104
+ rescue RuntimeError, TypeError => e
105
+ # TODO: drop in favor of FrozenError in ruby 2.5+
106
+ # RuntimeError (and FrozenError) for ruby 2.x
107
+ # TypeError for ruby 1.x
108
+ if (defined?(FrozenError) && e.is_a?(FrozenError)) || FROZEN_ERROR_MSG === e.message
109
+ return show_frozen_warning
110
+ end
111
+ raise
90
112
  end
91
113
 
92
114
  # @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
 
@@ -66,7 +66,7 @@ module RSpec
66
66
 
67
67
  # @private
68
68
  def self.unmutated(name)
69
- previously_defined = recursive_const_defined?(name)
69
+ previously_defined = !!recursive_const_defined?(name)
70
70
  rescue NameError
71
71
  new(name) do |c|
72
72
  c.valid_name = false
@@ -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)
@@ -21,6 +21,8 @@ module RSpec
21
21
 
22
22
  # @private
23
23
  def initialize(object, order_group, options={})
24
+ ensure_can_be_proxied!(object)
25
+
24
26
  @object = object
25
27
  @order_group = order_group
26
28
  @error_generator = ErrorGenerator.new(object)
@@ -31,6 +33,14 @@ module RSpec
31
33
  @method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
32
34
  end
33
35
 
36
+ # @private
37
+ def ensure_can_be_proxied!(object)
38
+ return unless object.is_a?(Symbol)
39
+
40
+ msg = "Cannot proxy frozen objects. Symbols such as #{object} cannot be mocked or stubbed."
41
+ raise ArgumentError, msg
42
+ end
43
+
34
44
  # @private
35
45
  attr_reader :object
36
46
 
@@ -182,6 +192,7 @@ module RSpec
182
192
  @messages_received << [message, args, block]
183
193
  end
184
194
  end
195
+ ruby2_keywords :record_message_received if respond_to?(:ruby2_keywords, true)
185
196
 
186
197
  # @private
187
198
  def message_received(message, *args, &block)
@@ -214,6 +225,7 @@ module RSpec
214
225
  @object.__send__(:method_missing, message, *args, &block)
215
226
  end
216
227
  end
228
+ ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
217
229
 
218
230
  # @private
219
231
  def raise_unexpected_message_error(method_name, args)
@@ -263,12 +275,14 @@ module RSpec
263
275
  expectation.matches?(method_name, *args)
264
276
  end
265
277
  end
278
+ ruby2_keywords :find_matching_expectation if respond_to?(:ruby2_keywords, true)
266
279
 
267
280
  def find_almost_matching_expectation(method_name, *args)
268
281
  find_best_matching_expectation_for(method_name) do |expectation|
269
282
  expectation.matches_name_but_not_args(method_name, *args)
270
283
  end
271
284
  end
285
+ ruby2_keywords :find_almost_matching_expectation if respond_to?(:ruby2_keywords, true)
272
286
 
273
287
  def find_best_matching_expectation_for(method_name)
274
288
  first_match = nil
@@ -285,10 +299,12 @@ module RSpec
285
299
  def find_matching_method_stub(method_name, *args)
286
300
  method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
287
301
  end
302
+ ruby2_keywords :find_matching_method_stub if respond_to?(:ruby2_keywords, true)
288
303
 
289
304
  def find_almost_matching_stub(method_name, *args)
290
305
  method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
291
306
  end
307
+ ruby2_keywords :find_almost_matching_stub if respond_to?(:ruby2_keywords, true)
292
308
  end
293
309
 
294
310
  # @private
@@ -344,6 +360,7 @@ module RSpec
344
360
  end
345
361
  super
346
362
  end
363
+ ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
347
364
 
348
365
  private
349
366
 
@@ -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)
@@ -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
@@ -34,27 +34,21 @@ module RSpec
34
34
  super
35
35
  end
36
36
 
37
- # @private
38
- module SilentIO
39
- def self.method_missing(*); end
40
- def self.respond_to?(*)
41
- true
42
- end
43
- end
44
-
45
37
  # Redefining `__send__` causes ruby to issue a warning.
46
- old, $stderr = $stderr, SilentIO
38
+ old, $VERBOSE = $VERBOSE, nil
47
39
  def __send__(name, *args, &block)
48
40
  @__sending_message = name
49
41
  super
50
42
  ensure
51
43
  @__sending_message = nil
52
44
  end
53
- $stderr = old
45
+ ruby2_keywords :__send__ if respond_to?(:ruby2_keywords, true)
46
+ $VERBOSE = old
54
47
 
55
48
  def send(name, *args, &block)
56
49
  __send__(name, *args, &block)
57
50
  end
51
+ ruby2_keywords :send if respond_to?(:ruby2_keywords, true)
58
52
 
59
53
  def initialize(doubled_module, *args)
60
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.9.1'
6
+ STRING = '3.12.4'
7
7
  end
8
8
  end
9
9
  end
data/lib/rspec/mocks.rb CHANGED
@@ -87,12 +87,15 @@ module RSpec
87
87
 
88
88
  # Call the passed block and verify mocks after it has executed. This allows
89
89
  # mock usage in arbitrary places, such as a `before(:all)` hook.
90
+ #
91
+ # @return [Object] the return value from the block
90
92
  def self.with_temporary_scope
91
93
  setup
92
94
 
93
95
  begin
94
- yield
96
+ result = yield
95
97
  verify
98
+ result
96
99
  ensure
97
100
  teardown
98
101
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-mocks
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.1
4
+ version: 3.12.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Baker
8
8
  - David Chelimsky
9
9
  - Myron Marston
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain:
13
13
  - |
@@ -45,7 +45,7 @@ cert_chain:
45
45
  ZsVDj6a7lH3cNqtWXZxrb2wO38qV5AkYj8SQK7Hj3/Yui9myUX3crr+PdetazSqQ
46
46
  F3MdtaDehhjC
47
47
  -----END CERTIFICATE-----
48
- date: 2019-12-31 00:00:00.000000000 Z
48
+ date: 2023-03-12 00:00:00.000000000 Z
49
49
  dependencies:
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: rspec-support
@@ -53,14 +53,14 @@ dependencies:
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: 3.9.0
56
+ version: 3.12.0
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 3.9.0
63
+ version: 3.12.0
64
64
  - !ruby/object:Gem::Dependency
65
65
  name: diff-lcs
66
66
  requirement: !ruby/object:Gem::Requirement
@@ -85,44 +85,44 @@ dependencies:
85
85
  name: rake
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - "~>"
88
+ - - ">"
89
89
  - !ruby/object:Gem::Version
90
90
  version: 10.0.0
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - "~>"
95
+ - - ">"
96
96
  - !ruby/object:Gem::Version
97
97
  version: 10.0.0
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: cucumber
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - "~>"
102
+ - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: 1.3.15
104
+ version: '1.3'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - "~>"
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
- version: 1.3.15
111
+ version: '1.3'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: aruba
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
116
  - - "~>"
117
117
  - !ruby/object:Gem::Version
118
- version: 0.14.10
118
+ version: '1.1'
119
119
  type: :development
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
123
  - - "~>"
124
124
  - !ruby/object:Gem::Version
125
- version: 0.14.10
125
+ version: '1.1'
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: minitest
128
128
  requirement: !ruby/object:Gem::Requirement
@@ -194,11 +194,11 @@ licenses:
194
194
  - MIT
195
195
  metadata:
196
196
  bug_tracker_uri: https://github.com/rspec/rspec-mocks/issues
197
- changelog_uri: https://github.com/rspec/rspec-mocks/blob/v3.9.1/Changelog.md
197
+ changelog_uri: https://github.com/rspec/rspec-mocks/blob/v3.12.4/Changelog.md
198
198
  documentation_uri: https://rspec.info/documentation/
199
199
  mailing_list_uri: https://groups.google.com/forum/#!forum/rspec
200
200
  source_code_uri: https://github.com/rspec/rspec-mocks
201
- post_install_message:
201
+ post_install_message:
202
202
  rdoc_options:
203
203
  - "--charset=UTF-8"
204
204
  require_paths:
@@ -214,8 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
214
  - !ruby/object:Gem::Version
215
215
  version: '0'
216
216
  requirements: []
217
- rubygems_version: 3.1.1
218
- signing_key:
217
+ rubygems_version: 3.4.1
218
+ signing_key:
219
219
  specification_version: 4
220
- summary: rspec-mocks-3.9.1
220
+ summary: rspec-mocks-3.12.4
221
221
  test_files: []
metadata.gz.sig CHANGED
Binary file