rspec-mocks 2.14.6 → 2.99.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +8 -8
  2. data/Changelog.md +20 -14
  3. data/lib/rspec/mocks.rb +6 -2
  4. data/lib/rspec/mocks/any_instance/chain.rb +28 -0
  5. data/lib/rspec/mocks/any_instance/expectation_chain.rb +5 -10
  6. data/lib/rspec/mocks/any_instance/recorder.rb +4 -1
  7. data/lib/rspec/mocks/any_instance/stub_chain.rb +5 -5
  8. data/lib/rspec/mocks/caller_filter.rb +55 -0
  9. data/lib/rspec/mocks/configuration.rb +19 -0
  10. data/lib/rspec/mocks/deprecation.rb +9 -1
  11. data/lib/rspec/mocks/extensions/proc.rb +63 -0
  12. data/lib/rspec/mocks/framework.rb +2 -0
  13. data/lib/rspec/mocks/matchers/receive.rb +2 -9
  14. data/lib/rspec/mocks/message_expectation.rb +92 -22
  15. data/lib/rspec/mocks/method_double.rb +2 -2
  16. data/lib/rspec/mocks/proxy_for_nil.rb +2 -2
  17. data/lib/rspec/mocks/space.rb +4 -5
  18. data/lib/rspec/mocks/stub_chain.rb +1 -1
  19. data/lib/rspec/mocks/syntax.rb +3 -3
  20. data/lib/rspec/mocks/targets.rb +1 -1
  21. data/lib/rspec/mocks/test_double.rb +8 -2
  22. data/lib/rspec/mocks/version.rb +1 -1
  23. data/spec/rspec/mocks/and_yield_spec.rb +1 -1
  24. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +3 -3
  25. data/spec/rspec/mocks/any_instance_spec.rb +233 -84
  26. data/spec/rspec/mocks/argument_expectation_spec.rb +4 -4
  27. data/spec/rspec/mocks/block_return_value_spec.rb +49 -11
  28. data/spec/rspec/mocks/bug_report_10263_spec.rb +1 -1
  29. data/spec/rspec/mocks/bug_report_8165_spec.rb +2 -2
  30. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +4 -4
  31. data/spec/rspec/mocks/double_spec.rb +7 -0
  32. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +1 -0
  33. data/spec/rspec/mocks/matchers/receive_spec.rb +10 -8
  34. data/spec/rspec/mocks/mock_space_spec.rb +10 -0
  35. data/spec/rspec/mocks/mock_spec.rb +20 -1
  36. data/spec/rspec/mocks/mutate_const_spec.rb +25 -25
  37. data/spec/rspec/mocks/null_object_mock_spec.rb +7 -0
  38. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +4 -2
  39. data/spec/rspec/mocks/record_messages_spec.rb +4 -4
  40. data/spec/rspec/mocks/space_spec.rb +1 -1
  41. data/spec/rspec/mocks/stub_chain_spec.rb +8 -12
  42. data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +20 -0
  43. data/spec/rspec/mocks/test_double_spec.rb +0 -5
  44. data/spec/rspec/mocks_spec.rb +14 -0
  45. data/spec/spec_helper.rb +27 -0
  46. metadata +7 -5
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZWYzOGM0ZDZlNWI1YjhiOGY5N2MxMGEzODNlNzA4NzJiYmRiOWIwMg==
4
+ OThjNzA2ZTdkZmU2MWE5OWVhMzIzMjI4NjZkNGNlY2QwZTY1ZDMyYQ==
5
5
  data.tar.gz: !binary |-
6
- MjhlMWMxOWI0MTMxYjQzYmExMWVmODkwMGNhYzczYmY1MWU1ZTliMw==
6
+ OGY4OTQwMGY4NTZhMGEzMmVhY2NlODAzZTYxMWMyNWFmZjVjMWIwYg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- OGM2M2FjZWM0YzJkMjkwOWE5Nzk0NWY3YjNmNGJlN2QxZjBjNmI2ZTU5NTQy
10
- ODA3N2EyODNmNGFiYjA3M2FiZDQ5NTc5NThmYzVlNGJhMTY3ZTg0MTQyNjBj
11
- NDZjY2Q2Y2ZiZTA1NTBlMjBhYTQzMWU1MDgyNzU1OGNhYzZkOTE=
9
+ Njc3ODhhZTYxMzMyNWU3MzIxM2MzMTdjNWQ0N2Q4MWNiNTIyZDYwNGIzZjI4
10
+ NzRmZjk1YjYzNDExMmZkYWNiM2E4NjQ0ZWVkOWM4MGU2NzNiMjNlYjY4ZWEw
11
+ Zjg5ZDNkOTY0NGNkOTUxZjc1NWM3MDcyM2Y1NThjNDgwNDI1YjQ=
12
12
  data.tar.gz: !binary |-
13
- MmNjMDFlNDM0MTZhZGNiZjhjMmQ1NDk4OGQ3YzViOGExOTVlMzUzNzI1MTI0
14
- NjlkMTdkZmQyNjE5ODljNTc5MWE4M2E3NzQzY2Y0ZWEzZDVkYTQzNmM3OGY3
15
- Yjg4ODM5ZmI0NjYyYjEzZGMyZGZjNzA2ZDExOTM3ZDY1OWY2YmQ=
13
+ MGIzMGMxYzg2OTE4NzY1NzA5ZTM4MDIxZmRhZDQ1NjM0OWE0YjIwOWQ3MTFj
14
+ YWFiYzQwNGRhMDEzNTc2YWVlNzYyMDcwNTNiZGU5MWE4NmZiMzc4MGYzNzQ5
15
+ NWM1YjA3OTBmMzg5Njk1MWZkZDFjNmUxNDc4ODA5Y2NiMTAyNGQ=
@@ -1,20 +1,27 @@
1
- ### 2.14.6 development
2
- [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.5...v2.14.6)
1
+ ### 2.99.0.beta1 / 2013-11-07
2
+ [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.99.0.beta1)
3
3
 
4
- Bug Fixes:
5
-
6
- * Ensure `any_instance` method stubs and expectations are torn down regardless of
7
- expectation failures. (Sam Phippen)
4
+ Deprecations
8
5
 
9
- ### 2.14.5 / 2014-02-01
10
- [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.4...v2.14.5)
6
+ * Expecting to use lambdas or other strong arity implementations for stub
7
+ methods with mis-matched arity is deprecated and support for them will be
8
+ removed in 3.0. Either provide the right amount of arguments or use a weak
9
+ arity implementation (methods with splats or procs). (Jon Rowe)
10
+ * Using the same test double instance in multiple examples is deprecated. Test
11
+ doubles are only meant to live for one example. The mocks and stubs have
12
+ always been reset between examples; however, in 2.x the `as_null_object`
13
+ state was not reset and some users relied on this to have a null object
14
+ double that is used for many examples. This behavior will be removed in 3.0.
15
+ (Myron Marston)
16
+ * Print a detailed warning when an `any_instance` implementation block is used
17
+ when the new `yield_receiver_to_any_instance_implementation_blocks` config
18
+ option is not explicitly set, as RSpec 3.0 will default to enabling this new
19
+ feature. (Sam Phippen)
11
20
 
12
- Bug Fixes:
21
+ Enhancements:
13
22
 
14
- * Fix regression that caused block implementations to not receive all
15
- args on 1.8.7 if the block also receives a block, due to Proc#arity
16
- reporting `1` no matter how many args the block receives if it
17
- receives a block, too. (Myron Marston)
23
+ * Add a config option to yield the receiver to `any_instance` implementation
24
+ blocks. (Sam Phippen)
18
25
 
19
26
  ### 2.14.4 / 2013-10-15
20
27
  [full changelog](http://github.com/rspec/rspec-mocks/compare/v2.14.3...v2.14.4)
@@ -119,7 +126,6 @@ Bug fixes
119
126
  (Luke Imhoff, Jon Rowe)
120
127
  * Fix isolation of `allow_message_expectations_on_nil` (Jon Rowe)
121
128
  * Use inspect to format actual arguments on expectations in failure messages (#280, Ben Langfeld)
122
- * Protect against improperly initialised test doubles (#293) (Joseph Shraibman and Jon Rowe)
123
129
 
124
130
  Deprecations
125
131
 
@@ -47,7 +47,9 @@ module RSpec
47
47
  # x = 0
48
48
  # RSpec::Mocks.allow_message(bar, :foo) { x += 1 }
49
49
  def allow_message(subject, message, opts={}, &block)
50
- orig_caller = opts.fetch(:expected_from) { caller(1)[0] }
50
+ orig_caller = opts.fetch(:expected_from) {
51
+ CallerFilter.first_non_rspec_line
52
+ }
51
53
  ::RSpec::Mocks.proxy_for(subject).
52
54
  add_stub(orig_caller, message.to_sym, opts, &block)
53
55
  end
@@ -64,7 +66,9 @@ module RSpec
64
66
  # RSpec::Mocks.expect_message(bar, :foo)
65
67
  # bar.foo
66
68
  def expect_message(subject, message, opts={}, &block)
67
- orig_caller = opts.fetch(:expected_from) { caller(1)[0] }
69
+ orig_caller = opts.fetch(:expected_from) {
70
+ CallerFilter.first_non_rspec_line
71
+ }
68
72
  ::RSpec::Mocks.proxy_for(subject).
69
73
  add_message_expectation(orig_caller, message.to_sym, opts, &block)
70
74
  end
@@ -6,6 +6,8 @@ module RSpec
6
6
  @recorder = recorder
7
7
  @expectation_args = args
8
8
  @expectation_block = block
9
+ @source_line = CallerFilter.first_non_rspec_line
10
+ ensure_expectation_block_has_source_location
9
11
  end
10
12
 
11
13
  module Customizations
@@ -71,6 +73,19 @@ module RSpec
71
73
 
72
74
  private
73
75
 
76
+ def create_message_expectation_on(instance)
77
+ me = yield(::RSpec::Mocks.proxy_for(instance), IGNORED_BACKTRACE_LINE)
78
+
79
+ if RSpec::Mocks.configuration.should_warn_about_any_instance_blocks?
80
+ me.warn_about_receiver_passing(@source_line)
81
+ me.display_any_instance_deprecation_warning_if_necessary(@expectation_block)
82
+ elsif RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
83
+ me.and_yield_receiver_to_implementation
84
+ end
85
+
86
+ me
87
+ end
88
+
74
89
  def negated?
75
90
  messages.any? { |(message, *_), _| message.to_sym == :never }
76
91
  end
@@ -88,6 +103,19 @@ module RSpec
88
103
  messages << [args.unshift(rspec_method_name), block]
89
104
  self
90
105
  end
106
+
107
+ if Proc.method_defined?(:source_location)
108
+ def ensure_expectation_block_has_source_location; end
109
+ else
110
+ def ensure_expectation_block_has_source_location
111
+ return unless @expectation_block
112
+ source_location = CallerFilter.first_non_rspec_line.split(':')
113
+
114
+ @expectation_block.extend Module.new {
115
+ define_method(:source_location) { source_location }
116
+ }
117
+ end
118
+ end
91
119
  end
92
120
  end
93
121
  end
@@ -12,20 +12,15 @@ module RSpec
12
12
  super
13
13
  end
14
14
 
15
- private
15
+ private
16
+
16
17
  def verify_invocation_order(rspec_method_name, *args, &block)
17
18
  end
18
- end
19
-
20
- # @api private
21
- class PositiveExpectationChain < ExpectationChain
22
-
23
- private
24
19
 
25
20
  def create_message_expectation_on(instance)
26
- proxy = ::RSpec::Mocks.proxy_for(instance)
27
- expected_from = IGNORED_BACKTRACE_LINE
28
- proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
21
+ super do |proxy, expected_from|
22
+ proxy.add_message_expectation(expected_from, *@expectation_args, &@expectation_block)
23
+ end
29
24
  end
30
25
 
31
26
  def invocation_order
@@ -57,7 +57,7 @@ module RSpec
57
57
  def should_receive(method_name, &block)
58
58
  @expectation_set = true
59
59
  observe!(method_name)
60
- message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block))
60
+ message_chains.add(method_name, ExpectationChain.new(self, method_name, &block))
61
61
  end
62
62
 
63
63
  def should_not_receive(method_name, &block)
@@ -88,6 +88,9 @@ module RSpec
88
88
  if @expectation_set && !message_chains.all_expectations_fulfilled?
89
89
  raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
90
90
  end
91
+ ensure
92
+ stop_all_observation!
93
+ ::RSpec::Mocks.space.remove_any_instance_recorder_for(@klass)
91
94
  end
92
95
 
93
96
  # @private
@@ -12,11 +12,11 @@ module RSpec
12
12
  private
13
13
 
14
14
  def create_message_expectation_on(instance)
15
- proxy = ::RSpec::Mocks.proxy_for(instance)
16
- expected_from = IGNORED_BACKTRACE_LINE
17
- stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block)
18
- @recorder.stubs[stub.message] << stub
19
- stub
15
+ super do |proxy, expected_from|
16
+ stub = proxy.add_stub(expected_from, *@expectation_args, &@expectation_block)
17
+ @recorder.stubs[stub.message] << stub
18
+ stub
19
+ end
20
20
  end
21
21
 
22
22
  def invocation_order
@@ -0,0 +1,55 @@
1
+ module RSpec
2
+ # Consistent implementation for "cleaning" the caller method to strip out
3
+ # non-rspec lines. This enables errors to be reported at the call site in
4
+ # the code using the library, which is far more useful than the particular
5
+ # internal method that raised an error.
6
+ class CallerFilter
7
+
8
+ RSPEC_LIBS = %w[
9
+ core
10
+ mocks
11
+ expectations
12
+ matchers
13
+ rails
14
+ ]
15
+
16
+ ADDITIONAL_TOP_LEVEL_FILES = %w[ autorun ]
17
+
18
+ LIB_REGEX = %r{/lib/rspec/(#{(RSPEC_LIBS + ADDITIONAL_TOP_LEVEL_FILES).join('|')})(\.rb|/)}
19
+
20
+ if RUBY_VERSION >= '2.0.0'
21
+ def self.first_non_rspec_line
22
+ # `caller` is an expensive method that scales linearly with the size of
23
+ # the stack. The performance hit for fetching it in chunks is small,
24
+ # and since the target line is probably near the top of the stack, the
25
+ # overall improvement of a chunked search like this is significant.
26
+ #
27
+ # See benchmarks/caller.rb for measurements.
28
+
29
+ # Initial value here is mostly arbitrary, but is chosen to give good
30
+ # performance on the common case of creating a double.
31
+ increment = 5
32
+ i = 1
33
+ line = nil
34
+
35
+ while !line
36
+ stack = caller(i, increment)
37
+ raise "No non-lib lines in stack" unless stack
38
+
39
+ line = stack.find { |l| l !~ LIB_REGEX }
40
+
41
+ i += increment
42
+ increment *= 2 # The choice of two here is arbitrary.
43
+ end
44
+
45
+ line
46
+ end
47
+ else
48
+ # Earlier rubies do not support the two argument form of `caller`. This
49
+ # fallback is logically the same, but slower.
50
+ def self.first_non_rspec_line
51
+ caller.find { |line| line !~ LIB_REGEX }
52
+ end
53
+ end
54
+ end
55
+ end
@@ -2,6 +2,25 @@ module RSpec
2
2
  module Mocks
3
3
  # Provides configuration options for rspec-mocks.
4
4
  class Configuration
5
+
6
+ def initialize
7
+ @yield_receiver_to_any_instance_implementation_blocks = false
8
+ @should_warn_about_any_instance_blocks = true
9
+ end
10
+
11
+ def yield_receiver_to_any_instance_implementation_blocks?
12
+ @yield_receiver_to_any_instance_implementation_blocks
13
+ end
14
+
15
+ def yield_receiver_to_any_instance_implementation_blocks=(arg)
16
+ @should_warn_about_any_instance_blocks = false
17
+ @yield_receiver_to_any_instance_implementation_blocks = arg
18
+ end
19
+
20
+ def should_warn_about_any_instance_blocks?
21
+ @should_warn_about_any_instance_blocks
22
+ end
23
+
5
24
  # Adds `stub` and `should_receive` to the given
6
25
  # modules or classes. This is usually only necessary
7
26
  # if you application uses some proxy classes that
@@ -7,7 +7,15 @@ module RSpec
7
7
  def deprecate(deprecated, options={})
8
8
  message = "DEPRECATION: #{deprecated} is deprecated."
9
9
  message << " Use #{options[:replacement]} instead." if options[:replacement]
10
- message << " Called from #{caller(0)[2]}."
10
+ message << " Called from #{CallerFilter.first_non_rspec_line}."
11
+ warn message
12
+ end
13
+
14
+ # @private
15
+ #
16
+ # Used internally to print deprecation warnings
17
+ def warn_deprecation(warning)
18
+ message = "\nDEPRECATION: #{warning}\n"
11
19
  warn message
12
20
  end
13
21
  end
@@ -0,0 +1,63 @@
1
+ unless Proc.method_defined? :lambda?
2
+ module Backports
3
+ def self.alias_method_chain(mod, target, feature)
4
+ mod.class_eval do
5
+ # Strip out punctuation on predicates or bang methods since
6
+ # e.g. target?_without_feature is not a valid method name.
7
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
8
+ yield(aliased_target, punctuation) if block_given?
9
+
10
+ with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
11
+
12
+ alias_method without_method, target
13
+ alias_method target, with_method
14
+
15
+ case
16
+ when public_method_defined?(without_method)
17
+ public target
18
+ when protected_method_defined?(without_method)
19
+ protected target
20
+ when private_method_defined?(without_method)
21
+ private target
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ class Proc
28
+ # Standard in Ruby 1.9. See official documentation[http://ruby-doc.org/core-1.9/classes/Proc.html]
29
+ def lambda?
30
+ !!__is_lambda__
31
+ end
32
+
33
+ attr_accessor :__is_lambda__
34
+ private :__is_lambda__
35
+ private :__is_lambda__=
36
+ end
37
+
38
+ class Method
39
+ def to_proc_with_lambda_tracking
40
+ proc = to_proc_without_lambda_tracking
41
+ proc.send :__is_lambda__=, true
42
+ proc
43
+ end
44
+ Backports.alias_method_chain self, :to_proc, :lambda_tracking
45
+ end
46
+
47
+ module Kernel
48
+ def lambda_with_lambda_tracking(&block)
49
+ l = lambda_without_lambda_tracking(&block)
50
+ l.send :__is_lambda__=, true unless block.send(:__is_lambda__) == false
51
+ l
52
+ end
53
+
54
+ def proc_with_lambda_tracking(&block)
55
+ l = proc_without_lambda_tracking(&block)
56
+ l.send :__is_lambda__=, block.send(:__is_lambda__) == true
57
+ l
58
+ end
59
+
60
+ Backports.alias_method_chain self, :lambda, :lambda_tracking
61
+ Backports.alias_method_chain self, :proc, :lambda_tracking
62
+ end
63
+ end
@@ -2,8 +2,10 @@
2
2
  # supports wrapping rspec's mocking functionality without invading every
3
3
  # object in the system.
4
4
 
5
+ require 'rspec/mocks/caller_filter' unless defined?(::RSpec::CallerFilter)
5
6
  require 'rspec/mocks/deprecation'
6
7
  require 'rspec/mocks/extensions/instance_exec'
8
+ require 'rspec/mocks/extensions/proc'
7
9
  require 'rspec/mocks/instance_method_stasher'
8
10
  require 'rspec/mocks/method_double'
9
11
  require 'rspec/mocks/argument_matchers'
@@ -6,14 +6,7 @@ module RSpec
6
6
  @message = message
7
7
  @block = block
8
8
  @recorded_customizations = []
9
-
10
- # MRI, JRuby and RBX report the caller inconsistently; MRI
11
- # reports an extra "in `new'" line in the backtrace that the
12
- # others do not include. The safest way to find the right
13
- # line is to search for the first line BEFORE rspec/mocks/syntax.rb.
14
- @backtrace_line = caller.find do |line|
15
- !line.split(':').first.end_with?('rspec/mocks/syntax.rb')
16
- end
9
+ @backtrace_line = CallerFilter.first_non_rspec_line
17
10
  end
18
11
 
19
12
  def setup_expectation(subject, &block)
@@ -49,7 +42,7 @@ module RSpec
49
42
  MessageExpectation.public_instance_methods(false).each do |method|
50
43
  next if method_defined?(method)
51
44
 
52
- class_eval(<<-RUBY)
45
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
53
46
  def #{method}(*args, &block)
54
47
  @recorded_customizations << Customization.new(#{method.inspect}, args, block)
55
48
  self
@@ -4,7 +4,9 @@ module RSpec
4
4
  class MessageExpectation
5
5
  # @private
6
6
  attr_accessor :error_generator, :implementation
7
+ attr_accessor :warn_about_yielding_receiver_to_implementation_block
7
8
  attr_reader :message
9
+ attr_reader :orig_object
8
10
  attr_writer :expected_received_count, :expected_from, :argument_list_matcher
9
11
  protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
10
12
 
@@ -15,6 +17,9 @@ module RSpec
15
17
  @error_generator.opts = opts
16
18
  @expected_from = expected_from
17
19
  @method_double = method_double
20
+ @have_warned_about_yielding_receiver = false
21
+ @orig_object = @method_double.object
22
+ @warn_about_yielding_receiver_to_implementation_block = false
18
23
  @message = @method_double.method_name
19
24
  @actual_received_count = 0
20
25
  @expected_received_count = expected_received_count
@@ -24,6 +29,7 @@ module RSpec
24
29
  @args_to_yield = []
25
30
  @failed_fast = nil
26
31
  @eval_context = nil
32
+ @yield_receiver_to_implementation_block = false
27
33
 
28
34
  @implementation = Implementation.new
29
35
  self.inner_implementation_action = implementation_block
@@ -87,6 +93,15 @@ module RSpec
87
93
  nil
88
94
  end
89
95
 
96
+ def and_yield_receiver_to_implementation
97
+ @yield_receiver_to_implementation_block = true
98
+ self
99
+ end
100
+
101
+ def yield_receiver_to_implementation_block?
102
+ @yield_receiver_to_implementation_block
103
+ end
104
+
90
105
  # Tells the object to delegate to the original unmodified method
91
106
  # when it receives the message.
92
107
  #
@@ -103,6 +118,7 @@ module RSpec
103
118
  @error_generator.raise_only_valid_on_a_partial_mock(:and_call_original)
104
119
  else
105
120
  @implementation = AndCallOriginalImplementation.new(@method_double.original_method)
121
+ @yield_receiver_to_implementation_block = false
106
122
  end
107
123
  end
108
124
 
@@ -171,6 +187,10 @@ module RSpec
171
187
 
172
188
  # @private
173
189
  def invoke(parent_stub, *args, &block)
190
+ if yield_receiver_to_implementation_block?
191
+ args.unshift(orig_object)
192
+ end
193
+
174
194
  if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
175
195
  @actual_received_count += 1
176
196
  @failed_fast = true
@@ -218,6 +238,28 @@ module RSpec
218
238
  Kernel::raise error
219
239
  end
220
240
 
241
+ # @private
242
+ def display_any_instance_deprecation_warning_if_necessary(block)
243
+ if passing_an_additional_arg_would_break_block?(block) &&
244
+ should_display_any_instance_deprecation_warning
245
+ line = if block.respond_to?(:source_location)
246
+ block.source_location.join(':')
247
+ else
248
+ @any_instance_source_line
249
+ end
250
+
251
+ display_any_instance_deprecation_warning(line)
252
+ @have_warned_about_yielding_receiver = true
253
+ end
254
+ end
255
+
256
+ # @private
257
+ def passing_an_additional_arg_would_break_block?(block)
258
+ return false unless block
259
+ return true if block.lambda?
260
+ !block.arity.zero?
261
+ end
262
+
221
263
  # @private
222
264
  def expected_messages_received?
223
265
  ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
@@ -302,7 +344,15 @@ module RSpec
302
344
  # cart.add(Book.new(:isbn => 1934356379))
303
345
  # # => passes
304
346
  def with(*args, &block)
305
- self.inner_implementation_action = block if block_given? unless args.empty?
347
+ if block_given?
348
+ if args.empty?
349
+ RSpec.deprecate "Using the return value of a `with` block to validate passed arguments rather than as an implementation",
350
+ :replacement => "the `satisfy` matcher, a custom matcher or validate the arguments in an implementation block"
351
+ else
352
+ self.inner_implementation_action = block
353
+ end
354
+ end
355
+
306
356
  @argument_list_matcher = ArgumentListMatcher.new(*args, &block)
307
357
  self
308
358
  end
@@ -430,6 +480,38 @@ module RSpec
430
480
  @actual_received_count += 1
431
481
  end
432
482
 
483
+ def warn_about_receiver_passing(any_instance_source_line)
484
+ @any_instance_source_line = any_instance_source_line
485
+ @warn_about_yielding_receiver_to_implementation_block = true
486
+ end
487
+
488
+ def should_display_any_instance_deprecation_warning
489
+ warn_about_yielding_receiver_to_implementation_block &&
490
+ !@have_warned_about_yielding_receiver
491
+ end
492
+
493
+ def display_any_instance_deprecation_warning(block_source_line)
494
+ RSpec.warn_deprecation(<<MSG
495
+ In RSpec 3, `any_instance` implementation blocks will be yielded the receiving
496
+ instance as the first block argument to allow the implementation block to use
497
+ the state of the receiver. To maintain compatibility with RSpec 3 you need to
498
+ either set rspec-mocks' `yield_receiver_to_any_instance_implementation_blocks`
499
+ config option to `false` OR set it to `true` and update your `any_instance`
500
+ implementation blocks to account for the first block argument being the receiving instance.
501
+
502
+ To set the config option, use a snippet like:
503
+
504
+ RSpec.configure do |rspec|
505
+ rspec.mock_with :rspec do |mocks|
506
+ mocks.yield_receiver_to_any_instance_implementation_blocks = false
507
+ end
508
+ end
509
+
510
+ Your `any_instance` implementation block is declared at: #{block_source_line}
511
+ MSG
512
+ )
513
+ end
514
+
433
515
  private
434
516
 
435
517
  def failed_fast?
@@ -452,6 +534,7 @@ module RSpec
452
534
  end
453
535
 
454
536
  def inner_implementation_action=(action)
537
+ display_any_instance_deprecation_warning_if_necessary(action)
455
538
  implementation.inner_action = action if action
456
539
  end
457
540
 
@@ -516,31 +599,18 @@ module RSpec
516
599
 
517
600
  def call(*args, &block)
518
601
  actions.map do |action|
602
+ if action.respond_to?(:lambda?) && action.lambda? && action.arity != args.size
603
+ RSpec.deprecate "stubbing implementations with mismatched arity",
604
+ :call_site => CallerFilter.first_non_rspec_line
605
+ end
519
606
  action.call(*arg_slice_for(args, action.arity), &block)
520
607
  end.last
521
608
  end
522
609
 
523
- if RUBY_VERSION.to_f > 1.8
524
- def arg_slice_for(args, arity)
525
- if arity >= 0
526
- args.slice(0, arity)
527
- else
528
- args
529
- end
530
- end
531
- else
532
- # 1.8.7's `arity` lies somtimes:
533
- # Given:
534
- # def print_arity(&b) puts b.arity; end
535
- #
536
- # This prints 1:
537
- # print_arity { |a, b, c, &bl| }
538
- #
539
- # But this prints 3:
540
- # print_arity { |a, b, c| }
541
- #
542
- # Given that it lies, we can't trust it and we don't slice the args.
543
- def arg_slice_for(args, arity)
610
+ def arg_slice_for(args, arity)
611
+ if arity >= 0
612
+ args.slice(0, arity)
613
+ else
544
614
  args
545
615
  end
546
616
  end