From 7b07baac27120edb22a9b9f80c53a3b22d292b46 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 13 Jan 2007 07:15:07 +0000 Subject: [PATCH] Unbundle flexmock. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@5917 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activesupport/CHANGELOG | 2 + .../lib/active_support/vendor/flexmock.rb | 1068 ----------------- 2 files changed, 2 insertions(+), 1068 deletions(-) delete mode 100644 activesupport/lib/active_support/vendor/flexmock.rb diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 0fc1488974fa7..8d935ecac55a5 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Unbundle flexmock. [Jeremy Kemper] + * Fix Dependencies.autoloaded? to ignore anonymous modules. Closes #6561. [Nicholas Seckar] * Update load once paths to prevent nested once constants from being detected and claimed by an external non-once load. [Nicholas Seckar] diff --git a/activesupport/lib/active_support/vendor/flexmock.rb b/activesupport/lib/active_support/vendor/flexmock.rb deleted file mode 100644 index ad6877715af95..0000000000000 --- a/activesupport/lib/active_support/vendor/flexmock.rb +++ /dev/null @@ -1,1068 +0,0 @@ -#!/usr/bin/env ruby - -#--- -# Copyright 2003, 2004, 2005, 2006 by Jim Weirich (jim@weirichhouse.org). -# All rights reserved. - -# Permission is granted for use, copying, modification, distribution, -# and distribution of modified versions of this work as long as the -# above copyright notice is included. -#+++ - -require 'test/unit' - -###################################################################### -# FlexMock is a flexible mock object suitable for using with Ruby's -# Test::Unit unit test framework. FlexMock has a simple interface -# that's easy to remember, and leaves the hard stuff to all those -# other mock object implementations. -# -# Basic Usage: -# -# m = FlexMock.new("name") -# m.mock_handle(:meth) { |args| assert_stuff } -# -# Simplified Usage: -# -# m = FlexMock.new("name") -# m.should_receive(:upcase).with("stuff"). -# returns("STUFF") -# m.should_receive(:downcase).with(String). -# returns { |s| s.downcase }.once -# -# With Test::Unit Integration: -# -# class TestSomething < Test::Unit::TestCase -# include FlexMock::TestCase -# -# def test_something -# m = flexmock("name") -# m.should_receive(:hi).and_return("Hello") -# m.hi -# end -# end -# -# Note: When using Test::Unit integeration, don't forget to include -# FlexMock::TestCase. Also, if you override +teardown+, make sure you -# call +super+. -# -class FlexMock - include Test::Unit::Assertions - - class BadInterceptionError < RuntimeError; end - - attr_reader :mock_name, :mock_groups - attr_accessor :mock_current_order - - # Create a FlexMock object with the given name. The name is used in - # error messages. - def initialize(name="unknown") - @mock_name = name - @expectations = Hash.new - @allocated_order = 0 - @mock_current_order = 0 - @mock_groups = {} - @ignore_missing = false - @verified = false - end - - # Handle all messages denoted by +sym+ by calling the given block - # and passing any parameters to the block. If we know exactly how - # many calls are to be made to a particular method, we may check - # that by passing in the number of expected calls as a second - # paramter. - def mock_handle(sym, expected_count=nil, &block) - self.should_receive(sym).times(expected_count).returns(&block) - end - - # Verify that each method that had an explicit expected count was - # actually called that many times. - def mock_verify - return if @verified - @verified = true - mock_wrap do - @expectations.each do |sym, handler| - handler.mock_verify - end - end - end - - # Teardown and infrastructure setup for this mock. - def mock_teardown - end - - # Allocation a new order number from the mock. - def mock_allocate_order - @auto_allocate = true - @allocated_order += 1 - end - - # Ignore all undefined (missing) method calls. - def should_ignore_missing - @ignore_missing = true - end - alias mock_ignore_missing should_ignore_missing - - # Handle missing methods by attempting to look up a handler. - def method_missing(sym, *args, &block) - mock_wrap do - if handler = @expectations[sym] - args << block if block_given? - handler.call(*args) - else - super(sym, *args, &block) unless @ignore_missing - end - end - end - - # Save the original definition of respond_to? for use a bit later. - alias mock_respond_to? respond_to? - - # Override the built-in respond_to? to include the mocked methods. - def respond_to?(sym) - super || (@expectations[sym] ? true : @ignore_missing) - end - - # Override the built-in +method+ to include the mocked methods. - def method(sym) - @expectations[sym] || super - rescue NameError => ex - if @ignore_missing - proc { } - else - raise ex - end - end - - # Declare that the mock object should receive a message with the - # given name. An expectation object for the method name is returned - # as the result of this method. Further expectation constraints can - # be added by chaining to the result. - # - # See Expectation for a list of declarators that can be used. - def should_receive(sym) - @expectations[sym] ||= ExpectationDirector.new(sym) - result = Expectation.new(self, sym) - @expectations[sym] << result - override_existing_method(sym) if mock_respond_to?(sym) - result - end - - # Override the existing definition of method +sym+ in the mock. - # Most methods depend on the method_missing trick to be invoked. - # However, if the method already exists, it will not call - # method_missing. This method defines a singleton method on the - # mock to explicitly invoke the method_missing logic. - def override_existing_method(sym) - sclass.class_eval "def #{sym}(*args, &block) method_missing(:#{sym}, *args, &block) end" - end - private :override_existing_method - - # Return the singleton class of the mock object. - def sclass - class << self; self; end - end - private :sclass - - # Declare that the mock object should expect methods by providing a - # recorder for the methods and having the user invoke the expected - # methods in a block. Further expectations may be applied the - # result of the recording call. - # - # Example Usage: - # - # mock.should_expect do |record| - # record.add(Integer, 4) { |a, b| - # a + b - # }.at_least.once - # - def should_expect - yield Recorder.new(self) - end - - # Return a factory object that returns this mock. This is useful in - # Class Interception. - def mock_factory - Factory.new(self) - end - - class << self - include Test::Unit::Assertions - - # Class method to make sure that verify is called at the end of a - # test. One mock object will be created for each name given to - # the use method. The mocks will be passed to the block as - # arguments. If no names are given, then a single anonymous mock - # object will be created. - # - # At the end of the use block, each mock object will be verified - # to make sure the proper number of calls have been made. - # - # Usage: - # - # FlexMock.use("name") do |mock| # Creates a mock named "name" - # mock.should_receive(:meth). - # returns(0).once - # end # mock is verified here - # - # NOTE: If you include FlexMock::TestCase into your test case - # file, you can create mocks that will be automatically verified in - # the test teardown by using the +flexmock+ method. - # - def use(*names) - names = ["unknown"] if names.empty? - got_excecption = false - mocks = names.collect { |n| new(n) } - yield(*mocks) - rescue Exception => ex - got_exception = true - raise - ensure - mocks.each do |mock| - mock.mock_verify unless got_exception - end - end - - # Class method to format a method name and argument list as a nice - # looking string. - def format_args(sym, args) - if args - "#{sym}(#{args.collect { |a| a.inspect }.join(', ')})" - else - "#{sym}(*args)" - end - end - - # Check will assert the block returns true. If it doesn't, an - # assertion failure is triggered with the given message. - def check(msg, &block) - assert_block(msg, &block) - end - end - - private - - # Wrap a block of code so the any assertion errors are wrapped so - # that the mock name is added to the error message . - def mock_wrap(&block) - yield - rescue Test::Unit::AssertionFailedError => ex - raise Test::Unit::AssertionFailedError, - "in mock '#{@mock_name}': #{ex.message}", - ex.backtrace - end - - #################################################################### - # A Factory object is returned from a mock_factory method call. The - # factory merely returns the manufactured object it is initialized - # with. The factory is handy to use with class interception, - # allowing the intercepted class to return the mock object. - # - # If the user needs more control over the mock factory, they are - # free to create their own. - # - # Typical Usage: - # intercept(Bar).in(Foo).with(a_mock.mack_factory) - # - class Factory - def initialize(manufactured_object) - @obj = manufactured_object - end - def new(*args, &block) - @obj - end - end - - #################################################################### - # The expectation director is responsible for routing calls to the - # correct expectations for a given argument list. - # - class ExpectationDirector - - # Create an ExpectationDirector for a mock object. - def initialize(sym) - @sym = sym - @expectations = [] - @expected_order = nil - end - - # Invoke the expectations for a given set of arguments. - # - # First, look for an expectation that matches the arguements and - # is eligible to be called. Failing that, look for a expectation - # that matches the arguments (at this point it will be ineligible, - # but at least we will get a good failure message). Finally, - # check for expectations that don't have any argument matching - # criteria. - def call(*args) - exp = @expectations.find { |e| e.match_args(args) && e.eligible? } || - @expectations.find { |e| e.match_args(args) } || - @expectations.find { |e| e.expected_args.nil? } - FlexMock.check("no matching handler found for " + - FlexMock.format_args(@sym, args)) { ! exp.nil? } - exp.verify_call(*args) - end - - # Same as call. - def [](*args) - call(*args) - end - - # Append an expectation to this director. - def <<(expectation) - @expectations << expectation - end - - # Do the post test verification for this directory. Check all the - # expectations. - def mock_verify - @expectations.each do |exp| - exp.mock_verify - end - end - end - - #################################################################### - # Match any object - class AnyMatcher - def ===(target) - true - end - def inspect - "ANY" - end - end - - #################################################################### - # Match only things that are equal. - class EqualMatcher - def initialize(obj) - @obj = obj - end - def ===(target) - @obj == target - end - def inspect - "==(#{@obj.inspect})" - end - end - - ANY = AnyMatcher.new - - #################################################################### - # Match only things where the block evaluates to true. - class ProcMatcher - def initialize(&block) - @block = block - end - def ===(target) - @block.call(target) - end - def inspect - "on{...}" - end - end - - #################################################################### - # Include this module in your test class if you wish to use the +eq+ - # and +any+ argument matching methods without a prefix. (Otherwise - # use FlexMock.any and FlexMock.eq(obj). - # - module ArgumentTypes - # Return an argument matcher that matches any argument. - def any - ANY - end - - # Return an argument matcher that only matches things equal to - # (==) the given object. - def eq(obj) - EqualMatcher.new(obj) - end - - # Return an argument matcher that matches any object, that when - # passed to the supplied block, will cause the block to return - # true. - def on(&block) - ProcMatcher.new(&block) - end - end - extend ArgumentTypes - - #################################################################### - # Base class for all the count validators. - # - class CountValidator - include Test::Unit::Assertions - def initialize(expectation, limit) - @exp = expectation - @limit = limit - end - - # If the expectation has been called +n+ times, is it still - # eligible to be called again? The default answer compares n to - # the established limit. - def eligible?(n) - n < @limit - end - end - - #################################################################### - # Validator for exact call counts. - # - class ExactCountValidator < CountValidator - # Validate that the method expectation was called exactly +n+ - # times. - def validate(n) - assert_equal @limit, n, - "method '#{@exp}' called incorrect number of times" - end - end - - #################################################################### - # Validator for call counts greater than or equal to a limit. - # - class AtLeastCountValidator < CountValidator - # Validate the method expectation was called no more than +n+ - # times. - def validate(n) - assert n >= @limit, - "Method '#{@exp}' should be called at least #{@limit} times,\n" + - "only called #{n} times" - end - - # If the expectation has been called +n+ times, is it still - # eligible to be called again? Since this validator only - # establishes a lower limit, not an upper limit, then the answer - # is always true. - def eligible?(n) - true - end - end - - #################################################################### - # Validator for call counts less than or equal to a limit. - # - class AtMostCountValidator < CountValidator - # Validate the method expectation was called at least +n+ times. - def validate(n) - assert n <= @limit, - "Method '#{@exp}' should be called at most #{@limit} times,\n" + - "only called #{n} times" - end - end - - #################################################################### - # An Expectation is returned from each +should_receive+ message sent - # to mock object. Each expectation records how a message matching - # the message name (argument to +should_receive+) and the argument - # list (given by +with+) should behave. Mock expectations can be - # recorded by chaining the declaration methods defined in this - # class. - # - # For example: - # - # mock.should_receive(:meth).with(args).and_returns(result) - # - class Expectation - include Test::Unit::Assertions - - attr_reader :expected_args, :mock, :order_number - - # Create an expectation for a method named +sym+. - def initialize(mock, sym) - @mock = mock - @sym = sym - @expected_args = nil - @count_validators = [] - @count_validator_class = ExactCountValidator - @actual_count = 0 - @return_value = nil - @return_block = lambda { @return_value } - @order_number = nil - end - - def to_s - FlexMock.format_args(@sym, @expected_args) - end - - # Verify the current call with the given arguments matches the - # expectations recorded in this object. - def verify_call(*args) - validate_order - @actual_count += 1 - @return_block.call(*args) - end - - # Is this expectation eligible to be called again? It is eligible - # only if all of its count validators agree that it is eligible. - def eligible? - @count_validators.all? { |v| v.eligible?(@actual_count) } - end - - # Validate that the order - def validate_order - return if @order_number.nil? - FlexMock.check("method #{to_s} called out of order " + - "(expected order #{@order_number}, was #{@mock.mock_current_order})") { - @order_number >= @mock.mock_current_order - } - @mock.mock_current_order = @order_number - end - private :validate_order - - # Validate the correct number of calls have been made. Called by - # the teardown process. - def mock_verify - @count_validators.each do |v| - v.validate(@actual_count) - end - end - - # Does the argument list match this expectation's argument - # specification. - def match_args(args) - return false if @expected_args.nil? - return false if args.size != @expected_args.size - (0...args.size).all? { |i| match_arg(@expected_args[i], args[i]) } - end - - # Does the expected argument match the corresponding actual value. - def match_arg(expected, actual) - expected === actual || - expected == actual || - ( Regexp === expected && expected === actual.to_s ) - end - - # Declare that the method should expect the given argument list. - def with(*args) - @expected_args = args - self - end - - # Declare that the method should be called with no arguments. - def with_no_args - with - end - - # Declare that the method can be called with any number of - # arguments of any type. - def with_any_args - @expected_args = nil - self - end - - # Declare that the method returns a particular value (when the - # argument list is matched). - # - # * If a single value is given, it will be returned for all matching - # calls. - # * If multiple values are given, each value will be returned in turn for - # each successive call. If the number of matching calls is greater - # than the number of values, the last value will be returned for - # the extra matching calls. - # * If a block is given, it is evaluated on each call and its - # value is returned. - # - # For example: - # - # mock.should_receive(:f).returns(12) # returns 12 - # - # mock.should_receive(:f).with(String). # returns an - # returns { |str| str.upcase } # upcased string - # - # +and_return+ is an alias for +returns+. - # - def returns(*args, &block) - @return_block = block_given? ? - block : - lambda { args.size == 1 ? args.first : args.shift } - self - end - alias :and_return :returns # :nodoc: - - # Declare that the method may be called any number of times. - def zero_or_more_times - at_least.never - end - - # Declare that the method is called +limit+ times with the - # declared argument list. This may be modified by the +at_least+ - # and +at_most+ declarators. - def times(limit) - @count_validators << @count_validator_class.new(self, limit) unless limit.nil? - @count_validator_class = ExactCountValidator - self - end - - # Declare that the method is never expected to be called with the - # given argument list. This may be modified by the +at_least+ and - # +at_most+ declarators. - def never - times(0) - end - - # Declare that the method is expected to be called exactly once - # with the given argument list. This may be modified by the - # +at_least+ and +at_most+ declarators. - def once - times(1) - end - - # Declare that the method is expected to be called exactly twice - # with the given argument list. This may be modified by the - # +at_least+ and +at_most+ declarators. - def twice - times(2) - end - - # Modifies the next call count declarator (+times+, +never+, - # +once+ or +twice+) so that the declarator means the method is - # called at least that many times. - # - # E.g. method f must be called at least twice: - # - # mock.should_receive(:f).at_least.twice - # - def at_least - @count_validator_class = AtLeastCountValidator - self - end - - # Modifies the next call count declarator (+times+, +never+, - # +once+ or +twice+) so that the declarator means the method is - # called at most that many times. - # - # E.g. method f must be called no more than twice - # - # mock.should_receive(:f).at_most.twice - # - def at_most - @count_validator_class = AtMostCountValidator - self - end - - # Declare that the given method must be called in order. All - # ordered method calls must be received in the order specified by - # the ordering of the +should_receive+ messages. Receiving a - # methods out of the specified order will cause a test failure. - # - # If the user needs more fine control over ordering - # (e.g. specifying that a group of messages may be received in any - # order as long as they all come after another group of messages), - # a _group_ _name_ may be specified in the +ordered+ calls. All - # messages within the same group may be received in any order. - # - # For example, in the following, messages +flip+ and +flop+ may be - # received in any order (because they are in the same group), but - # must occur strictly after +start+ but before +end+. The message - # +any_time+ may be received at any time because it is not - # ordered. - # - # m = FlexMock.new - # m.should_receive(:any_time) - # m.should_receive(:start).ordered - # m.should_receive(:flip).ordered(:flip_flop_group) - # m.should_receive(:flop).ordered(:flip_flop_group) - # m.should_receive(:end).ordered - # - def ordered(group_name=nil) - if group_name.nil? - @order_number = @mock.mock_allocate_order - elsif (num = @mock.mock_groups[group_name]) - @order_number = num - else - @order_number = @mock.mock_allocate_order - @mock.mock_groups[group_name] = @order_number - end - self - end - end - - #################################################################### - # Translate arbitrary method calls into expectations on the given - # mock object. - # - class Recorder - include FlexMock::ArgumentTypes - - # Create a method recorder for the mock +mock+. - def initialize(mock) - @mock = mock - @strict = false - end - - # Place the record in strict mode. While recording expectations - # in strict mode, the following will be true. - # - # * All expectations will be expected in the order they were - # recorded. - # * All expectations will be expected once. - # * All arguments will be placed in exact match mode, - # including regular expressions and class objects. - # - # Strict mode is usually used when giving the recorder to a known - # good algorithm. Strict mode captures the exact sequence of - # calls and validate that the code under test performs the exact - # same sequence of calls. - # - # The recorder may exit strict mode via a - # should_be_strict(false) call. Non-strict expectations - # may be recorded at that point, or even explicit expectations - # (using +should_receieve+) can be specified. - # - def should_be_strict(is_strict=true) - @strict = is_strict - end - - # Is the recorder in strict mode? - def strict? - @strict - end - - # Record an expectation for receiving the method +sym+ with the - # given arguments. - def method_missing(sym, *args, &block) - expectation = @mock.should_receive(sym).and_return(&block) - if @strict - args = args.collect { |arg| eq(arg) } - expectation.with(*args).ordered.once - else - expectation.with(*args) - end - expectation - end - end - - #################################################################### - # Test::Unit::TestCase Integration. - # - # Include this module in any TestCase class in a Test::Unit test - # suite to get integration with FlexMock. When this module is - # included, mocks may be created with a simple call to the - # +flexmock+ method. Mocks created with via the method call will - # automatically be verified in the teardown of the test case. - # - # Note: If you define a +teardown+ method in the test case, - # dont' forget to invoke the +super+ method! Failure to - # invoke super will cause all mocks to not be verified. - # - module TestCase - include ArgumentTypes - - # Teardown the test case, verifying any mocks that might have been - # defined in this test case. - def teardown - super - flexmock_teardown - end - - # Do the flexmock specific teardown stuff. - def flexmock_teardown - @flexmock_created_mocks ||= [] - if passed? - @flexmock_created_mocks.each do |m| - m.mock_verify - end - end - ensure - @flexmock_created_mocks.each do |m| - m.mock_teardown - end - @flexmock_interceptors ||= [] - @flexmock_interceptors.each do |i| - i.restore - end - end - - # Create a FlexMock object with the given name. Mocks created - # with this method will be automatically verify during teardown - # (assuming the the flexmock teardown isn't overridden). - # - # If a block is given, then the mock object is passed to the block and - # may be configured in the block. - def flexmock(name="unknown") - mock = FlexMock.new(name) - yield(mock) if block_given? - flexmock_remember(mock) - mock - end - - # Stub the given object by overriding the behavior of individual methods. - # The stub object returned will respond to the +should_receive+ - # method, just like normal stubs. Singleton methods cannot be - # stubbed. - # - # Example: Stub out DBI to return a fake db connection. - # - # flexstub(DBI).should_receive(:connect).and_return { - # fake_db = flexmock("db connection") - # fake_db.should_receive(:select_all).and_return(...) - # fake_db - # } - # - def flexstub(obj, name=nil) - name ||= "flexstub(#{obj.class.to_s})" - obj.instance_eval { - @flexmock_proxy ||= StubProxy.new(obj, FlexMock.new(name)) - } - flexmock_remember(obj.instance_variable_get("@flexmock_proxy")) - end - - # Intercept the named class in the target class for the duration - # of the test. Class interception is very simple-minded and has a - # number of restrictions. First, the intercepted class must be - # reference in the tested class via a simple constant name - # (e.g. no scoped names using "::") that is not directly defined - # in the class itself. After the test, a proxy class constant - # will be left behind that will forward all calls to the original - # class. - # - # Usage: - # intercept(SomeClass).in(ClassBeingTested).with(MockClass) - # intercept(SomeClass).with(MockClass).in(ClassBeingTested) - # - def intercept(intercepted_class) - result = Interception.new(intercepted_class) - @flexmock_interceptors ||= [] - @flexmock_interceptors << result - result - end - - private - - def flexmock_remember(mocking_object) - @flexmock_created_mocks ||= [] - @flexmock_created_mocks << mocking_object - mocking_object - end - end - - #################################################################### - # A Class Interception defines a constant in the target class to be - # a proxy that points to a replacement class for the duration of a - # test. When an interception is restored, the proxy will point to - # the original intercepted class. - # - class Interception - # Create an interception object with the class to intercepted. - def initialize(intercepted_class) - @intercepted = nil - @target = nil - @replacement = nil - @proxy = nil - intercept(intercepted_class) - update - end - - # Intercept this class in the class to be tested. - def intercept(intercepted_class) - @intercepted = intercepted_class - update - self - end - - # Define the class number test that will receive the - # interceptioned definition. - def in(target_class) - @target = target_class - update - self - end - - # Define the replacement class. This is normally a proxy or a - # stub. - def with(replacement_class) - @replacement = replacement_class - update - self - end - - # Restore the original class. The proxy remains in place however. - def restore - @proxy.proxied_class = @restore_class if @proxy - end - - private - - # Update the interception if the definition is complete. - def update - if complete? - do_interception - end - end - - # Is the interception definition complete. In other words, are - # all three actors defined? - def complete? - @intercepted && @target && @replacement - end - - # Implement interception on the classes defined. - def do_interception - @target_class = coerce_class(@target, "target") - @replacement_class = coerce_class(@replacement, "replacement") - case @intercepted - when String, Symbol - @intercepted_name = @intercepted.to_s - when Class - @intercepted_name = @intercepted.name - end - @intercepted_class = coerce_class(@intercepted, "intercepted") - current_class = @target_class.const_get(@intercepted_name) - if ClassProxy === current_class - @proxy = current_class - @restore_class = @proxy.proxied_class - @proxy.proxied_class = @replacement_class - else - @proxy = ClassProxy.new(@replacement_class) - @restore_class = current_class - @target_class.const_set(@intercepted_name, @proxy) - end - end - - # Coerce a class object, string to symbol to be the class object. - def coerce_class(klass, where) - case klass - when String, Symbol - lookup_const(klass.to_s, where) - else - klass - end - end - - def lookup_const(name, where, target=Object) - begin - target.const_get(name) - rescue NameError - raise BadInterceptionError, "in #{where} class #{name}" - end - end - end - - #################################################################### - # Class Proxy for class interception. Forward all method calls to - # whatever is the proxied_class. - # - class ClassProxy - attr_accessor :proxied_class - def initialize(default_class) - @proxied_class = default_class - end - def method_missing(sym, *args, &block) - @proxied_class.__send__(sym, *args, &block) - end - end - - #################################################################### - # StubProxy is used to mate the mock framework to an existing - # object. The object is "enhanced" with a reference to a mock - # object (stored in @flexmock_mock). When the - # +should_receive+ method is sent to the proxy, it overrides the - # existing object's method by creating singleton method that - # forwards to the mock. When testing is complete, StubProxy - # will erase the mocking infrastructure from the object being - # stubbed (e.g. remove instance variables and mock singleton - # methods). - # - class StubProxy - attr_reader :mock - - def initialize(obj, mock) - @obj = obj - @mock = mock - @method_definitions = {} - @methods_proxied = [] - end - - # Stub out the given method in the existing object and then let the - # mock object handle should_receive. - def should_receive(method_name) - method_name = method_name.to_sym - unless @methods_proxied.include?(method_name) - hide_existing_method(method_name) - @methods_proxied << method_name - end - @mock.should_receive(method_name) - end - - # Verify that the mock has been properly called. After verification, - # detach the mocking infrastructure from the existing object. - def mock_verify - @mock.mock_verify - end - - # Remove all traces of the mocking framework from the existing object. - def mock_teardown - if ! detached? - @methods_proxied.each do |method_name| - remove_current_method(method_name) - restore_original_definition(method_name) - end - @obj.instance_variable_set("@flexmock_proxy", nil) - @obj = nil - end - end - - private - - # The singleton class of the object. - def sclass - class << @obj; self; end - end - - # Is the current method a singleton method in the object we are - # mocking? - def singleton?(method_name) - @obj.methods(false).include?(method_name.to_s) - end - - # Hide the existing method definition with a singleton defintion - # that proxies to our mock object. If the current definition is a - # singleton, we need to record the definition and remove it before - # creating our own singleton method. If the current definition is - # not a singleton, all we need to do is override it with our own - # singleton. - def hide_existing_method(method_name) - if singleton?(method_name) - @method_definitions[method_name] = @obj.method(method_name) - remove_current_method(method_name) - end - define_proxy_method(method_name) - end - - # Define a proxy method that forwards to our mock object. The - # proxy method is defined as a singleton method on the object - # being mocked. - def define_proxy_method(method_name) - sclass.class_eval %{ - def #{method_name}(*args, &block) - @flexmock_proxy.mock.#{method_name}(*args, &block) - end - } - end - - # Restore the original singleton defintion for method_name that - # was saved earlier. - def restore_original_definition(method_name) - method_def = @method_definitions[method_name] - if method_def - sclass.class_eval { - define_method(method_name, &method_def) - } - end - end - - # Remove the current method if it is a singleton method of the - # object being mocked. - def remove_current_method(method_name) - sclass.class_eval { remove_method(method_name) } - end - - # Have we been detached from the existing object? - def detached? - @obj.nil? - end - - end -end