lib/muack/mock.rb in muack-0.7.3 vs lib/muack/mock.rb in muack-1.0.0

- old
+ new

@@ -1,12 +1,15 @@ require 'muack/definition' require 'muack/modifier' require 'muack/failure' +require 'muack/block' require 'muack/error' module Muack + EmptyBlock = proc{} + class Mock < BasicObject attr_reader :object def initialize object @object = object @__mock_injected = {} @@ -68,25 +71,33 @@ end end end # used for mocked object to dispatch mocked method - def __mock_dispatch_call disp, actual_args, actual_block - if disp.proxy - value = yield # need the original context for proxy or AnyInstanceOf - if disp.block - disp.block.call(value) - else - value - end - elsif block = disp.block - arity = block.arity - if arity < 0 - block.call(*actual_args , &actual_block) - else - block.call(*actual_args.first(arity), &actual_block) - end + def __mock_dispatch_call context, disp, actual_args, actual_block, &_yield + args = if disp.peek_args + __mock_block_call(context, disp.peek_args, + actual_args, actual_block, true) + else + actual_args + end + + ret = if disp.returns + __mock_block_call(context, disp.returns, + args, actual_block, true) + elsif disp.original_method # proxies for singleton methods + context.__send__(disp.original_method, *args, &actual_block) + else # proxies for instance methods + # need the original context for calling `super` + # ruby: can't pass a block to yield, so we name it _yield + _yield.call(*args, &actual_block) + end + + if disp.peek_return + __mock_block_call(context, disp.peek_return, ret, EmptyBlock, false) + else + ret end end # used for Muack::Session#verify def __mock_verify @@ -103,13 +114,14 @@ def __mock_reset __mock_injected.each_value do |defi| object.singleton_class.module_eval do remove_method(defi.msg) # restore original method - if instance_methods(false).include?(defi.original_method) - alias_method defi.msg, defi.original_method - remove_method defi.original_method + if instance_methods(false).include?(defi.original_method) || + private_instance_methods(false).include?(defi.original_method) + alias_method(defi.msg, defi.original_method) + remove_method(defi.original_method) end end end end @@ -118,46 +130,64 @@ private def __mock_inject_method defi __mock_injected[defi.msg] = defi target = object.singleton_class # would be the class in AnyInstanceOf - Mock.store_original_method(target, defi) - __mock_inject_mock_method(target, defi) + privilege = Mock.store_original_method(target, defi) + __mock_inject_mock_method(target, defi, privilege) end def self.store_original_method klass, defi - return unless klass.instance_methods(false).include?(defi.msg) + privilege = if klass.instance_methods(false).include?(defi.msg) + :public # TODO: forget about protected methods? + elsif klass.private_instance_methods(false).include?(defi.msg) + :private + end + + return :public unless privilege # store original method original_method = find_new_name(klass, defi.msg) klass.__send__(:alias_method, original_method, defi.msg) defi.original_method = original_method + privilege end def self.find_new_name klass, message, level=0 if level >= (::ENV['MUACK_RECURSION_LEVEL'] || 9).to_i raise CannotFindInjectionName.new(level+1, message) end - new_name = "__muack_mock_#{level}_#{message}".to_sym + new_name = "__muack_#{name}_#{level}_#{message}".to_sym if klass.instance_methods(false).include?(new_name) find_new_name(klass, message, level+1) else new_name end end - def __mock_inject_mock_method target, defi + def __mock_inject_mock_method target, defi, privilege=:public mock = self # remember the context - target.__send__(:define_method, defi.msg){|*actual_args, &actual_block| + target.__send__(:define_method, defi.msg){ |*actual_args, &actual_block| disp = mock.__mock_dispatch(defi.msg, actual_args) - mock.__mock_dispatch_call(disp, actual_args, actual_block){ - if disp.original_method - __send__(disp.original_method, *actual_args, &actual_block) - else - super(*actual_args, &actual_block) - end - } + mock.__mock_dispatch_call(self, disp, actual_args, + actual_block) do |args, &block| + super(*args, &block) + end } + target.__send__(privilege, defi.msg) + end + + # used for __mock_dispatch_call + def __mock_block_call context, block, actual_args, actual_block, splat + return unless block + # for AnyInstanceOf, we don't have actually context at the time + # we're defining it, so we update it here + block.context = context if block.kind_of?(Block) + if splat + block.call(*actual_args, &actual_block) + else # peek_return doesn't need splat + block.call(actual_args, &actual_block) + end end def __mock_check_args expected_args, actual_args if expected_args == [WithAnyArgs] true