Skip to content

Commit

Permalink
Refactoring: collapse concepts of method_block and return_block into
Browse files Browse the repository at this point in the history
implementation.
  • Loading branch information
dchelimsky committed Apr 16, 2012
1 parent 764680c commit f4f4ec3
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 83 deletions.
110 changes: 41 additions & 69 deletions lib/rspec/mocks/message_expectation.rb
Expand Up @@ -4,41 +4,37 @@ module Mocks
class MessageExpectation
# @private
attr_reader :message
attr_writer :expected_received_count, :method_block, :expected_from, :argument_expectation
protected :expected_received_count=, :method_block=, :expected_from=
attr_writer :expected_received_count, :expected_from, :argument_expectation, :implementation
protected :expected_received_count=, :expected_from=, :implementation=
attr_accessor :error_generator
protected :error_generator, :error_generator=

# @private
def initialize(error_generator, expectation_ordering, expected_from, message, method_block, expected_received_count=1, opts={}, &implementation)
def initialize(error_generator, expectation_ordering, expected_from, message, expected_received_count=1, opts={}, &implementation)
@error_generator = error_generator
@error_generator.opts = opts
@expected_from = expected_from
@message = message
@method_block = method_block
@return_block = nil
@actual_received_count = 0
@expected_received_count = expected_received_count
@argument_expectation = ArgumentExpectation.new(ArgumentMatchers::AnyArgsMatcher.new)
@consecutive = false
@exception_to_raise = nil
@args_to_throw = []
@order_group = expectation_ordering
@at_least = nil
@at_most = nil
@exactly = nil
@at_least = @at_most = @exactly = nil
@args_to_yield = []
@failed_fast = nil
@args_to_yield_were_cloned = false
@return_block = implementation
@eval_context = nil
@implementation = implementation
end

# @private
def build_child(expected_from, method_block, expected_received_count, opts={})
def build_child(expected_from, expected_received_count, opts={}, &implementation)
child = clone
child.expected_from = expected_from
child.method_block = method_block
child.implementation = implementation if implementation
child.expected_received_count = expected_received_count
child.clear_actual_received_count!
new_gen = error_generator.clone
Expand Down Expand Up @@ -90,17 +86,10 @@ def expected_args
# # ... this is prefered
# counter.stub(:count) { 1 }
# counter.count # => 1
def and_return(*values, &return_block)
Kernel::raise AmbiguousReturnError if @method_block

def and_return(*values, &implementation)
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args?
@consecutive = true if values.size > 1
@return_block = if return_block
return_block
else
value = values.size == 1 ? values.first : values
lambda { value }
end
@implementation = implementation || build_implementation(values)
end

# @overload and_raise
Expand Down Expand Up @@ -151,9 +140,7 @@ def and_yield(*args, &block)
@args_to_yield_were_cloned = false
end

if block
yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec)
end
yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block

@args_to_yield << args
self
Expand All @@ -178,18 +165,12 @@ def invoke(*args, &block)
raise_exception unless @exception_to_raise.nil?
Kernel::throw(*@args_to_throw) unless @args_to_throw.empty?

default_return_val = if @method_block
invoke_method_block(*args, &block)
elsif !@args_to_yield.empty? || @eval_context
invoke_with_yield(&block)
else
nil
end
default_return_val = call_with_yield(&block) if !@args_to_yield.empty? || @eval_context

if @consecutive
invoke_consecutive_return_block(*args, &block)
elsif @return_block
invoke_return_block(*args, &block)
call_implementation_consecutive(*args, &block)
elsif @implementation
call_implementation(*args, &block)
else
default_return_val
end
Expand Down Expand Up @@ -302,7 +283,7 @@ def raise_out_of_order_error
# cart.add(Book.new(:isbn => 1934356379))
# # => passes
def with(*args, &block)
@return_block = block if block_given? unless args.empty?
@implementation = block if block_given? unless args.empty?
@argument_expectation = ArgumentExpectation.new(*args, &block)
self
end
Expand All @@ -314,7 +295,7 @@ def with(*args, &block)
#
# dealer.should_recieve(:deal_card).exactly(10).times
def exactly(n, &block)
@method_block = block if block
@implementation = block if block
set_expected_received_count :exactly, n
self
end
Expand All @@ -326,7 +307,7 @@ def exactly(n, &block)
#
# dealer.should_recieve(:deal_card).at_least(9).times
def at_least(n, &block)
@method_block = block if block
@implementation = block if block
set_expected_received_count :at_least, n
self
end
Expand All @@ -338,7 +319,7 @@ def at_least(n, &block)
#
# dealer.should_recieve(:deal_card).at_most(10).times
def at_most(n, &block)
@method_block = block if block
@implementation = block if block
set_expected_received_count :at_most, n
self
end
Expand All @@ -351,14 +332,14 @@ def at_most(n, &block)
# dealer.should_recieve(:deal_card).at_least(10).times
# dealer.should_recieve(:deal_card).at_most(10).times
def times(&block)
@method_block = block if block
@implementation = block if block
self
end


# Allows an expected message to be received any number of times.
def any_number_of_times(&block)
@method_block = block if block
@implementation = block if block
@expected_received_count = :any
self
end
Expand All @@ -379,7 +360,7 @@ def never
#
# car.should_receive(:go).once
def once(&block)
@method_block = block if block
@implementation = block if block
set_expected_received_count :exactly, 1
self
end
Expand All @@ -390,7 +371,7 @@ def once(&block)
#
# car.should_receive(:go).twice
def twice(&block)
@method_block = block if block
@implementation = block if block
set_expected_received_count :exactly, 2
self
end
Expand All @@ -403,7 +384,7 @@ def twice(&block)
# api.should_receive(:run).ordered
# api.should_receive(:finish).ordered
def ordered(&block)
@method_block = block if block
@implementation = block if block
@order_group.register(self)
@ordered = true
self
Expand All @@ -426,41 +407,25 @@ def increase_actual_received_count!

protected

def invoke_method_block(*args, &block)
begin
@method_block.call(*args, &block)
rescue => detail
@error_generator.raise_block_failed_error(@message, detail.message)
end
end

def invoke_with_yield(&block)
def call_with_yield(&block)
@error_generator.raise_missing_block_error @args_to_yield unless block
value = nil
@args_to_yield.each do |args_to_yield_this_time|
if block.arity > -1 && args_to_yield_this_time.length != block.arity
@error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
@args_to_yield.each do |args|
if block.arity > -1 && args.length != block.arity
@error_generator.raise_wrong_arity_error args, block.arity
end
value = eval_block(*args_to_yield_this_time, &block)
value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args)
end
value
end

def eval_block(*args, &block)
if @eval_context
@eval_context.instance_exec(*args, &block)
else
block.call(*args)
end
end

def invoke_consecutive_return_block(*args, &block)
@value ||= invoke_return_block(*args, &block)
def call_implementation_consecutive(*args, &block)
@value ||= call_implementation(*args, &block)
@value[[@actual_received_count, @value.size-1].min]
end

def invoke_return_block(*args, &block)
@return_block.arity == 0 ? @return_block.call(&block) : @return_block.call(*args, &block)
def call_implementation(*args, &block)
@implementation.arity == 0 ? @implementation.call(&block) : @implementation.call(*args, &block)
end

def clone_args_to_yield(*args)
Expand All @@ -486,13 +451,20 @@ def set_expected_received_count(relativity, n)
def clear_actual_received_count!
@actual_received_count = 0
end

private

def build_implementation(values)
value = values.size == 1 ? values.first : values
lambda { value }
end
end

# @private
class NegativeMessageExpectation < MessageExpectation
# @private
def initialize(error_generator, expectation_ordering, expected_from, message, method_block)
super(error_generator, expectation_ordering, expected_from, message, method_block, 0)
def initialize(error_generator, expectation_ordering, expected_from, message, &implementation)
super(error_generator, expectation_ordering, expected_from, message, 0, {}, &implementation)
end

# @private
Expand Down
10 changes: 5 additions & 5 deletions lib/rspec/mocks/method_double.rb
Expand Up @@ -131,12 +131,12 @@ def clear
end

# @private
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &block)
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation)
configure_method
expectation = if existing_stub = stubs.first
existing_stub.build_child(expected_from, block, 1, opts)
existing_stub.build_child(expected_from, 1, opts, &implementation)
else
MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, block, 1, opts)
MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, 1, opts, &implementation)
end
expectations << expectation
expectation
Expand All @@ -145,15 +145,15 @@ def add_expectation(error_generator, expectation_ordering, expected_from, opts,
# @private
def add_negative_expectation(error_generator, expectation_ordering, expected_from, &implementation)
configure_method
expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, implementation)
expectation = NegativeMessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, &implementation)
expectations.unshift expectation
expectation
end

# @private
def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
configure_method
stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, nil, :any, opts, &implementation)
stub = MessageExpectation.new(error_generator, expectation_ordering, expected_from, @method_name, :any, opts, &implementation)
stubs.unshift stub
stub
end
Expand Down
10 changes: 1 addition & 9 deletions spec/rspec/mocks/mock_spec.rb
Expand Up @@ -171,7 +171,7 @@ def @mock.method_with_default_argument(arg={}); end
@mock.should_receive(:something) {| bool | bool.should be_true}
expect {
@mock.something false
}.to raise_error(RSpec::Mocks::MockExpectationError, /Double \"test double\" received :something but passed block failed with: expected false to be true/)
}.to raise_error(RSpec::Expectations::ExpectationNotMetError)
end

it "passes proc to expectation block without an argument", :ruby => '> 1.8.6' do
Expand Down Expand Up @@ -258,14 +258,6 @@ def initialize(i_take_an_argument)
}.should throw_symbol(:blech)
end

it "raises when explicit return and block constrained" do
lambda {
@mock.should_receive(:fruit) do |colour|
:strawberry
end.and_return :apple
}.should raise_error(RSpec::Mocks::AmbiguousReturnError)
end

it "ignores args on any args" do
@mock.should_receive(:something).at_least(:once).with(any_args)
@mock.something
Expand Down

0 comments on commit f4f4ec3

Please sign in to comment.