muack 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 309cafc82b9f7538884fa099e8ab6827d183afcf
4
- data.tar.gz: 64a20f3374a9c72abe10c4c7a78f26651c04c09b
2
+ SHA256:
3
+ metadata.gz: e65f1260993f94d96899086e3975500583ec077bfae332a8fd4cefc5a2c7da49
4
+ data.tar.gz: 736468eb986e1925f8f83973fec00c2142df16ca80aee9ce051bbdc255cbbfc8
5
5
  SHA512:
6
- metadata.gz: 312fd3143189a03c2c8d1fbe85a74296ccc71fd8247d09fc83ea8a5fc4ba3407fde7f7507e9c58d611cc476457b02d8b8ad614d1c6f9555694f2037c783eccc9
7
- data.tar.gz: 658d76b3ddac4ad03c10f643f8e16f996d1250862995037c4460abf586423b1e389e851bf4f376004187dbe1069b765428b8993d9175130db5627be586a50c7c
6
+ metadata.gz: d3bb1281eab20185484890205696cbed1d3996ed6392b76e08297af8e742bd76a80f2e3a26fffec265848d18a5373dffd517db6d2127b4e511dfdc5a2b475937
7
+ data.tar.gz: 2c148e33b0dce444708d09a58f6364e160a926f73e5432120fa8668c7ee0dd65c83128e69723673fd6c95bb62db879b8ff3636af29c93e783954f8409a882ebd
@@ -1,11 +1,21 @@
1
-
1
+ sudo: false
2
2
  language: ruby
3
- rvm:
4
- - 2.0
5
- - 2.1
6
- - 2.2
7
- - rbx-2
8
- - jruby
9
3
 
10
- install: 'bundle install --retry=3'
11
- script: 'ruby -r bundler/setup -S rake test'
4
+ install: 'gem update --system; gem install bundler; bundle install --retry=3'
5
+ before_script: unset CI
6
+ script: 'ruby -vwr bundler/setup -S rake test'
7
+
8
+ matrix:
9
+ include:
10
+ - rvm: 2.4
11
+ env: RUBYOPT=--enable-frozen-string-literal
12
+ - rvm: 2.5
13
+ env: RUBYOPT=--enable-frozen-string-literal
14
+ - rvm: 2.6
15
+ env: RUBYOPT=--enable-frozen-string-literal
16
+ - rvm: 2.7
17
+ env: RUBYOPT=--enable-frozen-string-literal
18
+ - rvm: ruby-head
19
+ env: RUBYOPT=--enable-frozen-string-literal
20
+ - rvm: jruby
21
+ env: RUBYOPT=--enable-frozen-string-literal
data/CHANGES.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # CHANGES
2
2
 
3
+ ## Muack 1.5.0 -- 2020-11-28
4
+
5
+ ### Bugs fixed
6
+
7
+ * Properly handle prepended objects
8
+ * Properly restore method visibilities for singleton methods
9
+
10
+ ### Enhancement
11
+
12
+ * Eliminated any potential keyword arguments warnings to be future proof
13
+ * Some major internal restructure
14
+
3
15
  ## Muack 1.4.0 -- 2015-11-21
4
16
 
5
17
  ### Incompatible changes / Enhancement
data/Gemfile CHANGED
@@ -8,7 +8,3 @@ gem 'pork'
8
8
 
9
9
  gem 'simplecov', :require => false if ENV['COV']
10
10
  gem 'coveralls', :require => false if ENV['CI']
11
-
12
- platform :rbx do
13
- gem 'rubysl-singleton' # used in rake
14
- end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Muack [![Build Status](https://secure.travis-ci.org/godfat/muack.png?branch=master)](http://travis-ci.org/godfat/muack) [![Coverage Status](https://coveralls.io/repos/godfat/muack/badge.png?branch=master)](https://coveralls.io/r/godfat/muack?branch=master) [![Join the chat at https://gitter.im/godfat/muack](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/godfat/muack)
1
+ # Muack [![Build Status](https://secure.travis-ci.org/godfat/muack.png?branch=master)](http://travis-ci.org/godfat/muack) [![Coverage Status](https://coveralls.io/repos/github/godfat/muack/badge.png)](https://coveralls.io/github/godfat/muack) [![Join the chat at https://gitter.im/godfat/muack](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/godfat/muack)
2
2
 
3
3
  by Lin Jen-Shin ([godfat](http://godfat.org))
4
4
 
@@ -7,6 +7,7 @@ by Lin Jen-Shin ([godfat](http://godfat.org))
7
7
  * [github](https://github.com/godfat/muack)
8
8
  * [rubygems](https://rubygems.org/gems/muack)
9
9
  * [rdoc](http://rdoc.info/github/godfat/muack)
10
+ * [issues](https://github.com/godfat/muack/issues) (feel free to ask for support)
10
11
 
11
12
  ## DESCRIPTION:
12
13
 
@@ -25,7 +26,7 @@ Muack is much simpler and thus much faster and much more consistent.
25
26
 
26
27
  ## REQUIREMENTS:
27
28
 
28
- * Tested with MRI (official CRuby), Rubinius and JRuby.
29
+ * Tested with MRI (official CRuby) and JRuby.
29
30
 
30
31
  ## INSTALLATION:
31
32
 
@@ -46,7 +47,7 @@ describe 'Hello' do
46
47
  after { Muack.verify }
47
48
 
48
49
  would 'say world!' do
49
- str = 'Hello'
50
+ str = 'Hello'.dup
50
51
  mock(str).say('!'){ |arg| "World#{arg}" }
51
52
  str.say('!').should.eq 'World!'
52
53
  end
@@ -172,7 +173,7 @@ affecting the others. This is helpful in the cases of mocking some very
172
173
  basic objects like Time, without causing too much side effect.
173
174
 
174
175
  ``` ruby
175
- name = 'str'
176
+ name = 'str'.dup
176
177
  stub(name).to_s{ 'hi' }
177
178
  stub(Time).new { Time.at(0) }
178
179
  mock(Time).now { Time.new }
@@ -274,7 +275,7 @@ into proxy mode we simply do not provide any block to the injected method,
274
275
  but just name it. Here's an example:
275
276
 
276
277
  ``` ruby
277
- str = 'str'
278
+ str = 'str'.dup
278
279
  mock(str).reverse
279
280
  p str.reverse # 'rts'
280
281
  p Muack.verify # true
@@ -284,7 +285,7 @@ Note that if reverse was not called exactly once, the mock would complain.
284
285
  We could also use stub + spy to do the same thing as well:
285
286
 
286
287
  ``` ruby
287
- str = 'str'
288
+ str = 'str'.dup
288
289
  stub(str).reverse
289
290
  p str.reverse # 'rts'
290
291
  spy(str).reverse
@@ -623,7 +624,7 @@ our own behaviour, then we already have full control of the arguments.
623
624
  There's no points to use both. This also applies to `peek_return`.
624
625
 
625
626
  ``` ruby
626
- str = 'ff'
627
+ str = 'ff'.dup
627
628
  mock(str).to_i.with_any_args.peek_args{ |radix| radix * 2 }
628
629
  p str.to_i(8) # 255
629
630
  p Muack.verify # true
@@ -654,7 +655,7 @@ might just want to take a look at the return? Here's an example using
654
655
  `peek_return` to modify the original return value.
655
656
 
656
657
  ``` ruby
657
- str = 'ff'
658
+ str = 'ff'.dup
658
659
  mock(str).to_i.with_any_args.peek_return{ |int| int * 2 }
659
660
  p str.to_i(16) # 510
660
661
  p Muack.verify # true
@@ -832,7 +833,7 @@ p Muack.verify # true
832
833
 
833
834
  ``` ruby
834
835
  obj = Object.new
835
- mock(obj).say(where(:a => is_a(Fixnum))){ |arg| arg }
836
+ mock(obj).say(where(:a => is_a(Integer))){ |arg| arg }
836
837
  p obj.say(:a => 0) # {:a => 0}
837
838
  p Muack.verify # true
838
839
  ```
@@ -841,7 +842,7 @@ Note that this could be recursive.
841
842
 
842
843
  ``` ruby
843
844
  obj = Object.new
844
- mock(obj).say(where(:a => {:b => [is_a(Fixnum)]})){ |arg| arg[:a] }
845
+ mock(obj).say(where(:a => {:b => [is_a(Integer)]})){ |arg| arg[:a] }
845
846
  p obj.say(:a => {:b => [0]}) # {:b => [0]}
846
847
  p Muack.verify # true
847
848
  ```
@@ -862,7 +863,7 @@ Note that this could be recursive.
862
863
 
863
864
  ``` ruby
864
865
  obj = Object.new
865
- mock(obj).say(having(:a => {:b => [is_a(Fixnum)]})){ |arg| arg[:c] }
866
+ mock(obj).say(having(:a => {:b => [is_a(Integer)]})){ |arg| arg[:c] }
866
867
  p obj.say(:a => {:b => [1]}, :c => 2) # 2
867
868
  p Muack.verify # true
868
869
  ```
@@ -883,7 +884,7 @@ Note that this could be recursive.
883
884
 
884
885
  ``` ruby
885
886
  obj = Object.new
886
- mock(obj).say(allowing(:a => {:b => is_a(Fixnum), :c => 1})){ |arg| arg[:a] }
887
+ mock(obj).say(allowing(:a => {:b => is_a(Integer), :c => 1})){ |arg| arg[:a] }
887
888
  p obj.say(:a => {:b => 2}) # {:b => 2}
888
889
  p Muack.verify # true
889
890
  ```
@@ -1209,8 +1210,8 @@ Or, I am happy to break legacy.
1209
1210
  ## USERS:
1210
1211
 
1211
1212
  * [Rib][]
1212
- * [rest-core](https://github.com/cardinalblue/rest-core)
1213
- * [rest-more](https://github.com/cardinalblue/rest-more)
1213
+ * [rest-core](https://github.com/godfat/rest-core)
1214
+ * [rest-more](https://github.com/godfat/rest-more)
1214
1215
 
1215
1216
  ## CONTRIBUTORS:
1216
1217
 
@@ -1218,9 +1219,9 @@ Or, I am happy to break legacy.
1218
1219
 
1219
1220
  ## LICENSE:
1220
1221
 
1221
- Apache License 2.0
1222
+ Apache License 2.0 (Apache-2.0)
1222
1223
 
1223
- Copyright (c) 2013-2015, Lin Jen-Shin (godfat)
1224
+ Copyright (c) 2013-2020, Lin Jen-Shin (godfat)
1224
1225
 
1225
1226
  Licensed under the Apache License, Version 2.0 (the "License");
1226
1227
  you may not use this file except in compliance with the License.
data/Rakefile CHANGED
@@ -1,14 +1,13 @@
1
1
 
2
2
  begin
3
- require "#{dir = File.dirname(__FILE__)}/task/gemgem"
3
+ require "#{__dir__}/task/gemgem"
4
4
  rescue LoadError
5
- sh 'git submodule update --init'
5
+ sh 'git submodule update --init --recursive'
6
6
  exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
7
7
  end
8
8
 
9
- Gemgem.init(dir) do |s|
9
+ Gemgem.init(__dir__) do |s|
10
10
  require 'muack/version'
11
11
  s.name = 'muack'
12
12
  s.version = Muack::VERSION
13
- %w[].each{ |g| s.add_runtime_dependency(g) }
14
13
  end
@@ -1,17 +1,6 @@
1
1
 
2
- module Muack
3
- class Block
4
- attr_accessor :block, :context
5
- def initialize block, context=nil
6
- self.block, self.context = block, context
7
- end
8
-
9
- def call *args, &actual_block
10
- if context # ruby: no way to pass actual_block to instance_exec
11
- context.instance_exec(*args, &block)
12
- else
13
- block.call(*args, &actual_block)
14
- end
15
- end
16
- end
2
+ if RUBY_VERSION < '2.7'
3
+ require 'muack/block_26'
4
+ else
5
+ require 'muack/block_27'
17
6
  end
@@ -0,0 +1,16 @@
1
+
2
+ module Muack
3
+ class Block < Struct.new(:block, :context)
4
+ def initialize block, context=nil
5
+ super
6
+ end
7
+
8
+ def call(*args, &block)
9
+ if context
10
+ context.instance_exec(*args, &block)
11
+ else
12
+ block.call(*args, &block)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Muack
3
+ class Block < Struct.new(:block, :context)
4
+ def initialize block, context=nil
5
+ super
6
+ end
7
+
8
+ def call(...)
9
+ if context
10
+ context.instance_exec(...)
11
+ else
12
+ block.call(...)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -4,7 +4,7 @@ require 'muack/mock'
4
4
  module Muack
5
5
  class Coat < Mock
6
6
  # used for mocked object to dispatch mocked method
7
- def __mock_dispatch msg, actual_args
7
+ def __mock_dispatch actual_call
8
8
  defi = super
9
9
  if __mock_defis[defi.msg].empty?
10
10
  __mock_reset_method(defi)
@@ -1,7 +1,8 @@
1
1
 
2
2
  module Muack
3
- Definition = Class.new(Struct.new(:msg, :args, :returns,
4
- :peek_args, :peek_return,
5
- :original_method))
3
+ Definition = Struct.new(:msg, :args, :returns,
4
+ :peek_args, :peek_return,
5
+ :original_method, :visibility)
6
+ ActualCall = Struct.new(:msg, :args, :block)
6
7
  WithAnyArgs = Object.new
7
8
  end
@@ -12,11 +12,12 @@ module Muack
12
12
 
13
13
  class Unexpected < Failure
14
14
  attr_reader :was
15
- def initialize obj, expected_defis, msg, args
16
- @was = "#{obj.inspect}.#{msg}(" \
17
- "#{args.map(&:inspect).join(', ')})"
15
+ def initialize obj, expected_defis, actual_call
16
+ args = actual_call.args.map(&:inspect)
17
+ @was = "#{obj.inspect}.#{actual_call.msg}(#{args.join(', ')})"
18
+
18
19
  if expected_defis.empty?
19
- super("\nUnexpected call: #{@was}")
20
+ super("\nUnexpected call: #{was}")
20
21
  else
21
22
  build_expected(obj, expected_defis)
22
23
  super("\nExpected: #{expected}\n but was: #{was}")
@@ -6,8 +6,6 @@ require 'muack/block'
6
6
  require 'muack/error'
7
7
 
8
8
  module Muack
9
- EmptyBlock = proc{}
10
-
11
9
  class Mock < BasicObject
12
10
  attr_reader :object
13
11
  def initialize object
@@ -24,8 +22,8 @@ module Muack
24
22
  end
25
23
 
26
24
  # Public API: Define mocked method
27
- def method_missing msg, *args, &block
28
- defi = Definition.new(msg, args, block)
25
+ def method_missing msg, *args, &returns
26
+ defi = Definition.new(msg, args, returns)
29
27
  if injected = __mock_injected[defi.msg]
30
28
  defi.original_method = injected.original_method
31
29
  else
@@ -51,42 +49,42 @@ module Muack
51
49
  end
52
50
 
53
51
  # used for mocked object to dispatch mocked method
54
- def __mock_dispatch msg, actual_args
55
- if defi = __mock_defis[msg].shift
52
+ def __mock_dispatch actual_call
53
+ if defi = __mock_defis[actual_call.msg].shift
56
54
  __mock_disps_push(defi)
57
- if __mock_check_args(defi.args, actual_args)
55
+ if __mock_check_args(defi, actual_call)
58
56
  defi
59
57
  else
60
58
  Mock.__send__(:raise, # Wrong argument
61
- Unexpected.new(object, [defi], msg, actual_args))
59
+ Unexpected.new(object, [defi], actual_call))
62
60
  end
63
61
  else
64
- __mock_failed(msg, actual_args)
62
+ __mock_failed(actual_call)
65
63
  end
66
64
  end
67
65
 
68
66
  # used for mocked object to dispatch mocked method
69
- def __mock_dispatch_call context, disp, actual_args, actual_block, &_yield
70
- args = if disp.peek_args
71
- __mock_block_call(context, disp.peek_args,
72
- actual_args, actual_block, true)
73
- else
74
- actual_args
75
- end
76
-
77
- ret = if disp.returns
78
- __mock_block_call(context, disp.returns,
79
- args, actual_block, true)
80
- elsif disp.original_method # proxies for singleton methods
81
- context.__send__(disp.original_method, *args, &actual_block)
82
- else # proxies for instance methods
83
- # need the original context for calling `super`
84
- # ruby: can't pass a block to yield, so we name it _yield
85
- _yield.call(args, &actual_block)
86
- end
67
+ def __mock_dispatch_call context, disp, actual_call, &proxy_super
68
+ # resolving arguments
69
+ call =
70
+ if disp.peek_args
71
+ args = __mock_block_call(context, disp.peek_args, actual_call)
72
+ ActualCall.new(actual_call.msg, args, actual_call.block)
73
+ else
74
+ actual_call
75
+ end
87
76
 
77
+ # retrieve actual return
78
+ ret =
79
+ if disp.returns
80
+ __mock_block_call(context, disp.returns, call)
81
+ else
82
+ __mock_proxy_call(context, disp, call, proxy_super)
83
+ end
84
+
85
+ # resolving return
88
86
  if disp.peek_return
89
- __mock_block_call(context, disp.peek_return, ret, EmptyBlock, false)
87
+ __mock_block_call(context, disp.peek_return, ret, true)
90
88
  else
91
89
  ret
92
90
  end
@@ -95,9 +93,9 @@ module Muack
95
93
  # used for Muack::Session#verify
96
94
  def __mock_verify
97
95
  __mock_defis.values.all?(&:empty?) || begin
98
- msg, defis_with_same_msg = __mock_defis.find{ |_, v| v.size > 0 }
96
+ msg, defis_with_same_msg = __mock_defis.find{ |_, v| v.any? }
99
97
  args, defis = defis_with_same_msg.group_by(&:args).first
100
- dsize = __mock_disps[msg].select{ |d| d.args == args }.size
98
+ dsize = __mock_disps[msg].count{ |d| d.args == args }
101
99
  Mock.__send__(:raise, # Too little times
102
100
  Expected.new(object, defis.first, defis.size + dsize, dsize))
103
101
  end
@@ -114,36 +112,46 @@ module Muack
114
112
  private
115
113
  def __mock_inject_method defi
116
114
  __mock_injected[defi.msg] = defi
117
- target = object.singleton_class # would be the class in AnyInstanceOf
118
- privilege = Mock.store_original_method(target, defi)
119
- __mock_inject_mock_method(target, defi, privilege)
115
+ # a) ancestors.first is the first module in the method chain.
116
+ # it's just the singleton_class when nothing was prepended,
117
+ # otherwise the last prepended module.
118
+ # b) would be the class in AnyInstanceOf.
119
+ target = object.singleton_class.ancestors.first
120
+ Mock.store_original_method(target, defi)
121
+ __mock_inject_mock_method(target, defi)
120
122
  end
121
123
 
122
124
  def __mock_reset_method defi
123
- object.singleton_class.module_eval do
125
+ object.singleton_class.ancestors.first.module_eval do
124
126
  remove_method(defi.msg)
125
127
  # restore original method
126
- if instance_methods(false).include?(defi.original_method) ||
128
+ if public_instance_methods(false).include?(defi.original_method) ||
129
+ protected_instance_methods(false).include?(defi.original_method) ||
127
130
  private_instance_methods(false).include?(defi.original_method)
128
131
  alias_method(defi.msg, defi.original_method)
132
+ __send__(defi.visibility, defi.msg)
129
133
  remove_method(defi.original_method)
130
134
  end
131
135
  end
132
136
  end
133
137
 
134
138
  def self.store_original_method klass, defi
135
- privilege = if klass.instance_methods(false).include?(defi.msg)
136
- :public # TODO: forget about protected methods?
139
+ visibility = if klass.public_instance_methods(false).include?(defi.msg)
140
+ :public
141
+ elsif klass.protected_instance_methods(false).include?(defi.msg)
142
+ :protected
137
143
  elsif klass.private_instance_methods(false).include?(defi.msg)
138
144
  :private
139
145
  end
140
146
 
141
- return :public unless privilege
142
- # store original method
143
- original_method = find_new_name(klass, defi.msg)
144
- klass.__send__(:alias_method, original_method, defi.msg)
145
- defi.original_method = original_method
146
- privilege
147
+ if visibility # store original method
148
+ original_method = find_new_name(klass, defi.msg)
149
+ klass.__send__(:alias_method, original_method, defi.msg)
150
+ defi.original_method = original_method
151
+ defi.visibility = visibility
152
+ else
153
+ defi.visibility = :public
154
+ end
147
155
  end
148
156
 
149
157
  def self.find_new_name klass, message, level=0
@@ -159,50 +167,100 @@ module Muack
159
167
  end
160
168
  end
161
169
 
162
- def __mock_inject_mock_method target, defi, privilege=:public
170
+ def __mock_inject_mock_method target, defi
163
171
  mock = self # remember the context
164
172
  target.__send__(:define_method, defi.msg){ |*actual_args, &actual_block|
165
- disp = mock.__mock_dispatch(defi.msg, actual_args)
166
- mock.__mock_dispatch_call(self, disp, actual_args,
167
- actual_block) do |args, &block|
168
- super(*args, &block)
173
+ actual_call = ActualCall.new(defi.msg, actual_args, actual_block)
174
+ disp = mock.__mock_dispatch(actual_call)
175
+ mock.__mock_dispatch_call(self, disp, actual_call) do |call, has_kargs|
176
+ # need the original context for calling `super`
177
+ if has_kargs && kargs = call.args.last
178
+ super(*call.args[0...-1], **kargs, &call.block)
179
+ else
180
+ super(*call.args, &call.block)
181
+ end
169
182
  end
170
183
  }
171
- target.__send__(privilege, defi.msg)
184
+ target.__send__(defi.visibility, defi.msg)
172
185
  end
173
186
 
174
187
  # used for __mock_dispatch
175
- def __mock_failed msg, actual_args, disps=__mock_disps[msg]
176
- if expected = __mock_find_checked_difi(disps, actual_args)
188
+ def __mock_failed actual_call, disps=__mock_disps[actual_call.msg]
189
+ if expected = __mock_find_checked_difi(disps, actual_call)
177
190
  Mock.__send__(:raise, # Too many times
178
191
  Expected.new(object, expected, disps.size, disps.size+1))
179
192
  else
180
193
  Mock.__send__(:raise, # Wrong argument
181
- Unexpected.new(object, disps, msg, actual_args))
194
+ Unexpected.new(object, disps, actual_call))
182
195
  end
183
196
  end
184
197
 
185
198
  # used for __mock_dispatch_call
186
- def __mock_block_call context, block, actual_args, actual_block, splat
187
- return unless block
199
+ def __mock_block_call context, block, actual_call, peek_return=false
188
200
  # for AnyInstanceOf, we don't have the actual context at the time
189
201
  # we're defining it, so we update it here
190
- block.context = context if block.kind_of?(Block)
191
- if splat
192
- block.call(*actual_args, &actual_block)
193
- else # peek_return doesn't need splat
194
- block.call(actual_args, &actual_block)
202
+ if block.kind_of?(Block)
203
+ block.context = context
204
+ instance_exec_block = block.block
205
+ end
206
+
207
+ if peek_return # actual_call is the actual return in this case
208
+ block.call(actual_call, &instance_exec_block)
209
+ else
210
+ actual_block = actual_call.block || instance_exec_block
211
+ if __mock_block_with_kargs?(instance_exec_block || block) &&
212
+ kargs = actual_call.args.last
213
+ block.call(*actual_call.args[0...-1], **kargs, &actual_block)
214
+ else
215
+ block.call(*actual_call.args, &actual_block)
216
+ end
217
+ end
218
+ end
219
+
220
+ # used for __mock_dispatch_call
221
+ def __mock_proxy_call context, disp, call, proxy_super
222
+ if disp.original_method # proxies for singleton methods with __send__
223
+ if __mock_method_with_kargs?(context, disp.original_method) &&
224
+ kargs = call.args.last
225
+ context.__send__(
226
+ disp.original_method, *call.args[0...-1], **kargs, &call.block)
227
+ else
228
+ context.__send__(disp.original_method, *call.args, &call.block)
229
+ end
230
+ else # proxies for instance methods with super
231
+ proxy_super.call(call, __mock_super_with_kargs?(context, call.msg))
195
232
  end
196
233
  end
197
234
 
198
- def __mock_find_checked_difi defis, actual_args, meth=:find
199
- defis.public_send(meth){ |d| __mock_check_args(d.args, actual_args) }
235
+ def __mock_find_checked_difi defis, actual_call, meth=:find
236
+ defis.public_send(meth){ |d| __mock_check_args(d, actual_call) }
237
+ end
238
+
239
+ def __mock_method_with_kargs? object, method_name
240
+ __mock_block_with_kargs?(
241
+ ::Kernel.instance_method(:method).bind(object).call(method_name))
200
242
  end
201
243
 
202
- def __mock_check_args expected_args, actual_args
203
- if expected_args == [WithAnyArgs]
204
- true
205
- elsif expected_args.none?{ |arg| arg.kind_of?(Satisfying) }
244
+ def __mock_super_with_kargs? object, method_name
245
+ super_method =
246
+ ::Kernel.instance_method(:method).bind(object).call(method_name).
247
+ super_method
248
+
249
+ super_method && __mock_block_with_kargs?(super_method)
250
+ end
251
+
252
+ def __mock_block_with_kargs? block
253
+ # there's no Symbol#start_with? in older Ruby
254
+ block.parameters.dig(-1, 0).to_s.start_with?('key')
255
+ end
256
+
257
+ def __mock_check_args defi, actual_call
258
+ return true if defi.args.size == 1 && defi.args.first == WithAnyArgs
259
+
260
+ expected_args = defi.args
261
+ actual_args = actual_call.args
262
+
263
+ if expected_args.none?{ |arg| arg.kind_of?(Satisfying) }
206
264
  expected_args == actual_args
207
265
 
208
266
  elsif expected_args.size == actual_args.size