diff --git a/lib/contracts/call_with.rb b/lib/contracts/call_with.rb index 8c66d22..c9336a1 100644 --- a/lib/contracts/call_with.rb +++ b/lib/contracts/call_with.rb @@ -1,6 +1,10 @@ module Contracts module CallWith def call_with(this, *args, &blk) + call_with_inner(false, this, *args, &blk) + end + + def call_with_inner(returns, this, *args, &blk) args << blk if blk # Explicitly append blk=nil if nil != Proc contract violation anticipated @@ -16,14 +20,16 @@ def call_with(this, *args, &blk) validator = @args_validators[i] unless validator && validator[arg] - return unless Contract.failure_callback(:arg => arg, - :contract => contract, - :class => klass, - :method => method, - :contracts => self, - :arg_pos => i+1, - :total_args => args.size, - :return_value => false) + data = {:arg => arg, + :contract => contract, + :class => klass, + :method => method, + :contracts => self, + :arg_pos => i+1, + :total_args => args.size, + :return_value => false} + return ParamContractError.new("as return value", data) if returns + return unless Contract.failure_callback(data) end if contract.is_a?(Contracts::Func) && blk && !nil_block_appended diff --git a/lib/contracts/method_handler.rb b/lib/contracts/method_handler.rb index f47926b..d57e37e 100644 --- a/lib/contracts/method_handler.rb +++ b/lib/contracts/method_handler.rb @@ -125,31 +125,23 @@ def redefine_method # function. Otherwise we return the result. # If we run out of functions, we raise the last error, but # convert it to_contract_error. - success = false - i = 0 - result = nil + expected_error = decorated_methods[0].failure_exception + last_error = nil - until success - decorated_method = decorated_methods[i] - i += 1 - begin - success = true - result = decorated_method.call_with(self, *args, &blk) - rescue expected_error => error - success = false - unless decorated_methods[i] - begin - ::Contract.failure_callback(error.data, false) - rescue expected_error => final_error - raise final_error.to_contract_error - end - end - end + decorated_methods.each do |decorated_method| + result = decorated_method.call_with_inner(true, self, *args, &blk) + return result unless result.is_a?(ParamContractError) + last_error = result end - # Return the result of successfully called method - result + begin + if ::Contract.failure_callback(last_error.data, false) + decorated_methods.last.call_with_inner(false, self, *args, &blk) + end + rescue expected_error => final_error + raise final_error.to_contract_error + end end end