lib/rspec/mocks/instance_method_stasher.rb in rspec-mocks-2.99.4 vs lib/rspec/mocks/instance_method_stasher.rb in rspec-mocks-3.0.0.beta1

- old
+ new

@@ -1,93 +1,111 @@ module RSpec module Mocks # @private class InstanceMethodStasher - def initialize(klass, method) - @klass = klass + def initialize(object, method) + @object = object @method = method + @klass = (class << object; self; end) + @original_method = nil @method_is_stashed = false end - # @private - def method_is_stashed? - @method_is_stashed - end + attr_reader :original_method - # @private - def stash - return if !method_defined_directly_on_klass? || @method_is_stashed + if RUBY_VERSION.to_f < 1.9 + # @private + def method_is_stashed? + @method_is_stashed + end - @klass.__send__(:alias_method, stashed_method_name, @method) - @method_is_stashed = true - end + # @private + def stash + return if !method_defined_directly_on_klass? || @method_is_stashed - private + @klass.__send__(:alias_method, stashed_method_name, @method) + @method_is_stashed = true + end - # @private - def method_defined_directly_on_klass? - method_defined_on_klass? && method_owned_by_klass? - end + # @private + def stashed_method_name + "obfuscated_by_rspec_mocks__#{@method}" + end + private :stashed_method_name - # @private - def method_defined_on_klass?(klass = @klass) - klass.method_defined?(@method) || klass.private_method_defined?(@method) - end - - if ::UnboundMethod.method_defined?(:owner) # @private - def method_owned_by_klass? - owner = @klass.instance_method(@method).owner + def restore + return unless @method_is_stashed - # On Ruby 2.0.0+ the owner of a method on a class which has been - # `prepend`ed may actually be an instance, e.g. - # `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`. - owner = owner.class unless owner.is_a? Class - - # On 1.8 (and some 1.9s -- e.g. rubinius) aliased methods - # can report the wrong owner. Example: - # class MyClass - # class << self - # alias alternate_new new - # end - # end - # - # MyClass.owner(:alternate_new) returns `Class` on 1.8, - # but we need to consider the owner to be `MyClass` because - # it is not actually available on `Class` but is on `MyClass`. - # Hence, we verify that the owner actually has the method defined. - # If the given owner does not have the method defined, we assume - # that the method is actually owned by @klass. - owner == @klass || !(method_defined_on_klass?(owner)) + if @klass.__send__(:method_defined?, @method) + @klass.__send__(:undef_method, @method) + end + @klass.__send__(:alias_method, @method, stashed_method_name) + @klass.__send__(:remove_method, stashed_method_name) + @method_is_stashed = false end else + # @private - def method_owned_by_klass? - # On 1.8.6, which does not support Method#owner, we have no choice but - # to assume it's defined on the klass even if it may be defined on - # a superclass. - true + def method_is_stashed? + !!@original_method end + + # @private + def stash + return if !method_defined_directly_on_klass? + @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method) + end + + # @private + def restore + return unless @original_method + + if @klass.__send__(:method_defined?, @method) + @klass.__send__(:undef_method, @method) + end + + @klass.__send__(:define_method, @method, @original_method) + @original_method = nil + end end - public + private # @private - def stashed_method_name - "obfuscated_by_rspec_mocks__#{@method}" + def method_defined_directly_on_klass? + method_defined_on_klass? && method_owned_by_klass? end # @private - def restore - return unless @method_is_stashed + def method_defined_on_klass?(klass = @klass) + klass.method_defined?(@method) || klass.private_method_defined?(@method) + end - if @klass.__send__(:method_defined?, @method) - @klass.__send__(:undef_method, @method) - end - @klass.__send__(:alias_method, @method, stashed_method_name) - @klass.__send__(:remove_method, stashed_method_name) - @method_is_stashed = false + def method_owned_by_klass? + owner = @klass.instance_method(@method).owner + + # On Ruby 2.0.0+ the owner of a method on a class which has been + # `prepend`ed may actually be an instance, e.g. + # `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`. + owner = owner.class unless Module === owner + + # On some 1.9s (e.g. rubinius) aliased methods + # can report the wrong owner. Example: + # class MyClass + # class << self + # alias alternate_new new + # end + # end + # + # MyClass.owner(:alternate_new) returns `Class` when incorrect, + # but we need to consider the owner to be `MyClass` because + # it is not actually available on `Class` but is on `MyClass`. + # Hence, we verify that the owner actually has the method defined. + # If the given owner does not have the method defined, we assume + # that the method is actually owned by @klass. + owner == @klass || !(method_defined_on_klass?(owner)) end end end end