spy 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aa89505674a7286f055ee019988cf29debbdd5cb7e525a022864f551d20ae842
4
- data.tar.gz: fa61fa5e78f5aea6fab75bcc471bdcc8032182d8e9c7528e9370b91022ef7ae0
3
+ metadata.gz: 3906b9e65a981746a6dc4e51106ed64fc925478cb73335eb8a9d46ae9fc31931
4
+ data.tar.gz: 85e19b544c6ccb98616dd846db8263da3d0e8abcfd2158a5749d8988c5dc32e8
5
5
  SHA512:
6
- metadata.gz: abb91b0bcef139e03b90cb59f2f1b5d9f0c52c8137beafab301f56a96d44dbcffc203bcf0c69cd7e5376bed68549e0bb9bf407b4f85e07fc91d875d401ce39d0
7
- data.tar.gz: 58d355373220b031ba5740b0cfdc2a6d9bb94a7c39166fafae23896342eec3c3b907994f7224e80abc7f2d1ae289048d98cd56a05750569befa62c98f6330a22
6
+ metadata.gz: 57544c0872ff818dc065037e1e2a10c34b1edb698ad84ebf1e184aa190d24c8663d51d011619bb6a1024c93f1d94a03a8bd6578274605a56f0b0ba06aabf6abc
7
+ data.tar.gz: 43b7ca82d10759bb6083fdd76b27941425cefbcc63bc2b8a585b34d3c8e2c2659f24c1f4f32843ee1c1b8f3aee1d64c127fb769774430e77a31ea7005aeebbcb
@@ -33,6 +33,7 @@ module Spy
33
33
  @base_object, @method_name = object, method_name
34
34
  @singleton_method = singleton_method
35
35
  @plan = nil
36
+ @call_through = false
36
37
  reset!
37
38
  end
38
39
 
@@ -58,14 +59,14 @@ module Spy
58
59
 
59
60
  if singleton_method
60
61
  if base_object.singleton_class.method_defined?(method_name) || base_object.singleton_class.private_method_defined?(method_name)
61
- base_object.singleton_class.alias_method(method_name, method_name)
62
+ base_object.singleton_class.send(:alias_method, method_name, method_name)
62
63
  end
63
64
  base_object.define_singleton_method(method_name, override_method)
64
65
  else
65
66
  if base_object.method_defined?(method_name) || base_object.private_method_defined?(method_name)
66
- base_object.alias_method(method_name, method_name)
67
+ base_object.send(:alias_method, method_name, method_name)
67
68
  end
68
- base_object.define_method(method_name, override_method)
69
+ base_object.send(:define_method, method_name, override_method)
69
70
  end
70
71
 
71
72
  if [:public, :protected, :private].include? hook_opts[:visibility]
@@ -148,28 +149,7 @@ module Spy
148
149
  # tells the spy to call the original method
149
150
  # @return [self]
150
151
  def and_call_through
151
- if @base_object.is_a? Class
152
- @plan = Proc.new do |object, *args, &block|
153
- if original_method
154
- if original_method.is_a? UnboundMethod
155
- bound_method = original_method.bind(object)
156
- bound_method.call(*args, &block)
157
- else
158
- original_method.call(*args, &block)
159
- end
160
- else
161
- base_object.send(:method_missing, method_name, *args, &block)
162
- end
163
- end
164
- else
165
- @plan = Proc.new do |*args, &block|
166
- if original_method
167
- original_method.call(*args, &block)
168
- else
169
- base_object.send(:method_missing, method_name, *args, &block)
170
- end
171
- end
172
- end
152
+ @call_through = true
173
153
 
174
154
  self
175
155
  end
@@ -232,17 +212,17 @@ module Spy
232
212
  def invoke(object, args, block, called_from)
233
213
  check_arity!(args.size)
234
214
 
235
- if base_object.is_a? Class
236
- result = if @plan
237
- check_for_too_many_arguments!(@plan)
238
- @plan.call(object, *args, &block)
239
- end
240
- else
241
- result = if @plan
242
- check_for_too_many_arguments!(@plan)
243
- @plan.call(*args, &block)
244
- end
245
- end
215
+ result =
216
+ if @call_through
217
+ call_plan(build_call_through_plan(object), block, *args)
218
+ elsif @plan
219
+ check_for_too_many_arguments!(@plan)
220
+ if base_object.is_a? Class
221
+ call_plan(@plan, block, object, *args)
222
+ else
223
+ call_plan(@plan, block, *args)
224
+ end
225
+ end
246
226
  ensure
247
227
  calls << CallLog.new(object, called_from, args, block, result)
248
228
  end
@@ -355,6 +335,39 @@ module Spy
355
335
  @method_owner ||= current_method.owner
356
336
  end
357
337
 
338
+ def build_call_through_plan(object)
339
+ if original_method
340
+ if @base_object.is_a?(Class) && original_method.is_a?(UnboundMethod)
341
+ original_method.bind(object)
342
+ else
343
+ original_method
344
+ end
345
+ else
346
+ Proc.new do |*args, &block|
347
+ base_object.send(:method_missing, method_name, *args, &block)
348
+ end
349
+ end
350
+ end
351
+
352
+ def call_plan(plan, block, *args)
353
+ if ruby_27_last_arg_hash?(args)
354
+ *prefix, last = args
355
+ plan.call(*prefix, **last, &block)
356
+ else
357
+ plan.call(*args, &block)
358
+ end
359
+ end
360
+
361
+ # Ruby 2.7 gives a deprecation warning about passing hash as last argument for a method
362
+ # with a double-splat operator (**), and Ruby 3 raises an ArgumentError exception.
363
+ # This checks if args has a hash as last element to extract it and pass it with double-splat to avoid an exception.
364
+ def ruby_27_last_arg_hash?(args)
365
+ last = args.last
366
+ last.instance_of?(Hash) &&
367
+ !last.empty? &&
368
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
369
+ end
370
+
358
371
  class << self
359
372
 
360
373
  # retrieve the method spy from an object or create a new one
data/lib/spy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spy
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -6,6 +6,10 @@ module Spy
6
6
  Subroutine.new(base_object, method_name).hook
7
7
  end
8
8
 
9
+ def spy_on_instance_method(base_object, method_name)
10
+ Subroutine.new(base_object, method_name, false).hook
11
+ end
12
+
9
13
  def setup
10
14
  @pen = Pen.new
11
15
  end
@@ -109,6 +113,15 @@ module Spy
109
113
  assert_empty @pen.written
110
114
  end
111
115
 
116
+ def test_spy_and_return_can_call_a_block_with_hash
117
+ result = "hello world"
118
+
119
+ spy_on(@pen, :write_hash).and_return { |**opts| opts[:test] }
120
+
121
+ assert_equal result, @pen.write_hash(test: result)
122
+ assert_empty @pen.written
123
+ end
124
+
112
125
  def test_spy_and_return_can_call_a_block_raises_when_there_is_an_arity_mismatch
113
126
  write_spy = spy_on(@pen, :write)
114
127
  write_spy.and_return do |*args|
@@ -137,6 +150,73 @@ module Spy
137
150
  assert_equal string, result
138
151
  end
139
152
 
153
+ def test_spy_and_call_through_returns_original_method_result
154
+ string = "hello world"
155
+
156
+ write_spy = spy_on(@pen, :write).and_call_through
157
+ another_spy = spy_on(@pen, :another).and_call_through
158
+
159
+ result = @pen.write(string)
160
+
161
+ assert_equal string, result
162
+ assert write_spy.has_been_called?
163
+ assert_equal 'another', @pen.another
164
+ assert another_spy.has_been_called?
165
+ end
166
+
167
+ def test_spy_and_call_through_with_hash_original_method
168
+ string = 'test:hello world'
169
+
170
+ write_spy = spy_on(@pen, :write_hash).and_call_through
171
+
172
+ @pen.write_hash(test: 'hello world')
173
+ assert_equal string, @pen.written.last
174
+ assert write_spy.has_been_called?
175
+ end
176
+
177
+ def test_spy_on_instance_and_call_through_returns_original_method_result
178
+ string = "hello world"
179
+
180
+ inst_write_spy = spy_on_instance_method(Pen, :write).and_call_through
181
+ inst_another_spy = spy_on_instance_method(Pen, :another).and_call_through
182
+
183
+ result = @pen.write(string)
184
+
185
+ assert_equal string, result
186
+ assert inst_write_spy.has_been_called?
187
+ assert_equal 'another', @pen.another
188
+ assert inst_another_spy.has_been_called?
189
+ end
190
+
191
+ def test_spy_on_instance_and_call_through_with_hash
192
+ string = 'test:hello world'
193
+
194
+ inst_write_spy = spy_on_instance_method(Pen, :write_hash).and_call_through
195
+
196
+ @pen.write_hash(test: 'hello world')
197
+
198
+ assert_equal string, @pen.written.last
199
+ assert inst_write_spy.has_been_called?
200
+ end
201
+
202
+ def test_spy_on_instance_and_call_through_to_aryable
203
+ to_aryable = Class.new do
204
+ def hello
205
+ 'hello'
206
+ end
207
+
208
+ def to_ary
209
+ [1]
210
+ end
211
+ end
212
+
213
+ inst_hello_spy = spy_on_instance_method(to_aryable, :hello).and_call_through
214
+ inst = to_aryable.new
215
+
216
+ assert_equal 'hello', inst.hello
217
+ assert inst_hello_spy.has_been_called?
218
+ end
219
+
140
220
  def test_spy_hook_records_number_of_calls
141
221
  pen_write_spy = spy_on(@pen, :write)
142
222
  assert_equal 0, pen_write_spy.calls.size
data/test/support/pen.rb CHANGED
@@ -27,6 +27,12 @@ class Pen
27
27
  end
28
28
  end
29
29
 
30
+ def write_hash(**params)
31
+ params.each do |p|
32
+ write(p.join(':'))
33
+ end
34
+ end
35
+
30
36
  def greet(hello = "hello", name)
31
37
  write("#{hello} #{name}")
32
38
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spy
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Ong
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-20 00:00:00.000000000 Z
11
+ date: 2022-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -169,7 +169,7 @@ homepage: https://github.com/ryanong/spy
169
169
  licenses:
170
170
  - MIT
171
171
  metadata: {}
172
- post_install_message:
172
+ post_install_message:
173
173
  rdoc_options: []
174
174
  require_paths:
175
175
  - lib
@@ -185,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
185
  version: '0'
186
186
  requirements: []
187
187
  rubygems_version: 3.1.2
188
- signing_key:
188
+ signing_key:
189
189
  specification_version: 4
190
190
  summary: A simple modern mocking library that uses the spy pattern and checks method's
191
191
  existence and arity.