rspec-mocks 2.14.6 → 2.99.0.beta1

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