lib/spy/subroutine.rb in spy-1.0.3 vs lib/spy/subroutine.rb in spy-1.0.4

- old
+ new

@@ -199,30 +199,32 @@ end # check if the method was called with the exact arguments # @param args Arguments that should have been sent to the method # @return [Boolean] - def has_been_called_with?(*args, &block) + def has_been_called_with?(*args, **kwargs, &block) raise NeverHookedError unless @was_hooked - match = block_given? ? block : proc { |call| call.args == args } + match = block_given? ? block : proc { |call| call.args == args && call.kwargs == kwargs } calls.any?(&match) end # invoke that the method has been called. You really shouldn't use this # method. - def invoke(object, args, block, called_from) - check_arity!(args.size) + def invoke(object, args, kwargs, block, called_from) + arity = args.size + arity += 1 if kwargs.present? + check_arity!(arity) result = if @call_through - call_plan(build_call_through_plan(object), block, *args) + call_plan(build_call_through_plan(object), block, *args, **kwargs) elsif @plan check_for_too_many_arguments!(@plan) - call_plan(@plan, block, *args) + call_plan(@plan, block, *args, **kwargs) end ensure - calls << CallLog.new(object, called_from, args, block, result) + calls << CallLog.new(object, called_from, args, kwargs, block, result) end # reset the call log def reset! @was_hooked = false @@ -235,15 +237,16 @@ # this returns a lambda that calls the spy object. # we use eval to set the spy object id as a parameter so it can be extracted # and looked up later using `Method#parameters` SPY_ARGS_PREFIX='__spy_args_'.freeze + SPY_KWARGS_PREFIX='__spy_kwargs_'.freeze def override_method eval <<-METHOD, binding, __FILE__, __LINE__ + 1 __method_spy__ = self - lambda do |*#{SPY_ARGS_PREFIX}#{self.object_id}, &block| - __method_spy__.invoke(self, #{SPY_ARGS_PREFIX}#{self.object_id}, block, caller(1)[0]) + lambda do |*#{SPY_ARGS_PREFIX}#{self.object_id}, **#{SPY_KWARGS_PREFIX}#{self.object_id}, &block| + __method_spy__.invoke(self, #{SPY_ARGS_PREFIX}#{self.object_id}, #{SPY_KWARGS_PREFIX}#{self.object_id}, block, caller(1)[0]) end METHOD end def clear_method! @@ -343,26 +346,11 @@ base_object.send(:method_missing, method_name, *args, &block) end end end - def call_plan(plan, block, *args) - if ruby_27_last_arg_hash?(args) - *prefix, last = args - plan.call(*prefix, **last, &block) - else - plan.call(*args, &block) - end - end - - # Ruby 2.7 gives a deprecation warning about passing hash as last argument for a method - # with a double-splat operator (**), and Ruby 3 raises an ArgumentError exception. - # This checks if args has a hash as last element to extract it and pass it with double-splat to avoid an exception. - def ruby_27_last_arg_hash?(args) - last = args.last - last.instance_of?(Hash) && - !last.empty? && - Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0") + def call_plan(plan, block, *args, **kwargs) + plan.call(*args, **kwargs, &block) end class << self # retrieve the method spy from an object or create a new one