rspec/rspec-mocks — pull request #182 by alindeman (2012-09-06 22:38:59+00:00)
Title: Correctly unstubs methods stubbed with `#any_instance`

* Instances stubbed with `#any_instance` would not be usable after the
test finished because `MethodDouble` would stash the implementation of
the method already overridden by `AnyInstance::Recorder`. When the
test finished, that implementation would be restored on the object's
singleton class, and any future calls to it would blow up with a stack
overflow.
* This fix only stashes methods if they are defined on the object's
singleton class to begin with; `AnyInstance::Recorder` defines a
method on the object's class so that method will not be stashed.
* If there is no method on the object's singleton class, RSpec can
safely define one there without stashing the original implementation.
At the end of the test, the method is simply removed entirely from the
singleton class. Any original implementation defined in the object's
ancestor chain will show through again.
* This issue cannot be fixed on MRI 1.8.6 because it does not support
`Method#owner`. However, `#any_instance` itself is not supported on
1.8.6 for the same reason. The fix should not negatively affect 1.8.6,
though, because the fallback behavior is to stash the method in all
cases (which was the original behavior).

* This commit also refactors the stashing behavior out into its own
object. While not explicitly necessary, it helped me reason about the
fix much easier than when all the responsibility was in `MethodDouble`
(which also has other responsibilities).

* Fixes #167