peeek 1.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +127 -0
- data/Rakefile +1 -0
- data/bin/peeek +16 -0
- data/lib/peeek.rb +147 -0
- data/lib/peeek/call.rb +134 -0
- data/lib/peeek/calls.rb +43 -0
- data/lib/peeek/hook.rb +188 -0
- data/lib/peeek/hook/instance.rb +51 -0
- data/lib/peeek/hook/linker.rb +29 -0
- data/lib/peeek/hook/singleton.rb +52 -0
- data/lib/peeek/hooks.rb +46 -0
- data/lib/peeek/supervisor.rb +120 -0
- data/lib/peeek/version.rb +3 -0
- data/peeek.gemspec +25 -0
- data/spec/peeek/call_spec.rb +226 -0
- data/spec/peeek/calls_spec.rb +86 -0
- data/spec/peeek/hook/instance_spec.rb +87 -0
- data/spec/peeek/hook/linker_spec.rb +15 -0
- data/spec/peeek/hook/singleton_spec.rb +82 -0
- data/spec/peeek/hook_spec.rb +380 -0
- data/spec/peeek/hooks_spec.rb +92 -0
- data/spec/peeek/supervisor_spec.rb +129 -0
- data/spec/peeek_spec.rb +335 -0
- data/spec/spec_helper.rb +78 -0
- metadata +125 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'peeek/hook/singleton'
|
2
|
+
|
3
|
+
def sample_singleton_linker(method_name = :quote)
|
4
|
+
Peeek::Hook::Singleton.new(Regexp, method_name)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Peeek::Hook::Singleton, '#method_prefix' do
|
8
|
+
it 'returns "."' do
|
9
|
+
linker = sample_singleton_linker
|
10
|
+
linker.method_prefix.should == '.'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Peeek::Hook::Singleton, '#defined?' do
|
15
|
+
it 'is true if the method is defined in the object' do
|
16
|
+
linker = sample_singleton_linker
|
17
|
+
linker.should be_defined
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is false if the method is not defined in the object' do
|
21
|
+
linker = sample_singleton_linker(:undefined_method)
|
22
|
+
linker.should_not be_defined
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe Peeek::Hook::Singleton, '#link' do
|
27
|
+
before do
|
28
|
+
@original_method = Regexp.method(:quote)
|
29
|
+
@linker = sample_singleton_linker
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
@linker.unlink(@original_method)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'gives appropriate arguments to the block when calling the method' do
|
37
|
+
block_args = nil
|
38
|
+
@linker.link { |*args| block_args = args }
|
39
|
+
Regexp.quote('.')
|
40
|
+
block_args[0][0].should =~ %r(spec/peeek/hook/singleton_spec\.rb)
|
41
|
+
block_args[1].should == Regexp
|
42
|
+
block_args[2].should == ['.']
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'is return value from the block as return value from the method' do
|
46
|
+
return_value = 'return_value'
|
47
|
+
@linker.link { |*args| return_value }
|
48
|
+
Regexp.quote('.').should equal(return_value)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'is exception from the block as exception from the method' do
|
52
|
+
@linker.link { |*args| raise 'exception' }
|
53
|
+
lambda { Regexp.quote('.') }.should raise_error('exception')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'returns the original method' do
|
57
|
+
@linker.link { }.should == @original_method
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'raises ArgumentError if a block not given' do
|
61
|
+
lambda { @linker.link }.should raise_error(ArgumentError, 'block not supplied')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe Peeek::Hook::Singleton, '#unlink' do
|
66
|
+
before do
|
67
|
+
@original_method = Regexp.method(:quote)
|
68
|
+
@linker = sample_singleton_linker
|
69
|
+
@linker.link { fail }
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'defines method with the original method' do
|
73
|
+
call = lambda { Regexp.quote('.') }
|
74
|
+
|
75
|
+
# assert
|
76
|
+
call.should raise_error
|
77
|
+
|
78
|
+
@linker.unlink(@original_method)
|
79
|
+
call.should_not raise_error
|
80
|
+
call[].should == '\.'
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,380 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'peeek/hook'
|
3
|
+
|
4
|
+
def sample_instance_hook(linker = nil)
|
5
|
+
Peeek::Hook.create(String, :%).tap do |hook|
|
6
|
+
hook.instance_variable_set(:@linker, linker) if linker
|
7
|
+
hook.instance_variable_set(:@calls, Array.new(5))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def sample_singleton_hook(linker = nil)
|
12
|
+
Peeek::Hook.create($stdout, :write).tap do |hook|
|
13
|
+
hook.instance_variable_set(:@linker, linker) if linker
|
14
|
+
hook.instance_variable_set(:@calls, Array.new(4))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe Peeek::Hook, '.create' do
|
19
|
+
it "returns an instance of #{described_class}" do
|
20
|
+
hook = sample_instance_hook
|
21
|
+
hook.should be_a(described_class)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns a hook that corresponds to the object and the method name" do
|
25
|
+
hook = sample_instance_hook
|
26
|
+
hook.object.should == String
|
27
|
+
hook.method_name.should == :%
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when specify implicitly' do
|
31
|
+
it 'returns a hook to instance method if given any module or any class' do
|
32
|
+
hook = described_class.create(String, :%)
|
33
|
+
hook.object.should == String
|
34
|
+
hook.method_name.should == :%
|
35
|
+
hook.should be_instance
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns a hook to singleton method if given any instance' do
|
39
|
+
hook = described_class.create($stdout, :write)
|
40
|
+
hook.object.should == $stdout
|
41
|
+
hook.method_name.should == :write
|
42
|
+
hook.should be_singleton
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when specify instance method expressly' do
|
47
|
+
it 'returns a hook to instance method if given any module or any class' do
|
48
|
+
hook = described_class.create(String, '#%')
|
49
|
+
hook.object.should == String
|
50
|
+
hook.method_name.should == :%
|
51
|
+
hook.should be_instance
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises ArgumentError if given any instance' do
|
55
|
+
lambda { described_class.create($stdout, '#write') }.should raise_error(ArgumentError, "can't create a hook of instance method to an instance of any class")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when specify singleton method expressly' do
|
60
|
+
it 'returns a hook to singleton method if given any module or any class' do
|
61
|
+
hook = described_class.create(String, '.new')
|
62
|
+
hook.object.should == String
|
63
|
+
hook.method_name.should == :new
|
64
|
+
hook.should be_singleton
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns a hook to singleton method if given any instance' do
|
68
|
+
hook = described_class.create($stdout, '.write')
|
69
|
+
hook.object.should == $stdout
|
70
|
+
hook.method_name.should == :write
|
71
|
+
hook.should be_singleton
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe Peeek::Hook, '.any_module?' do
|
77
|
+
it 'is true if any module given' do
|
78
|
+
described_class.should be_any_module(Enumerable)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'is true if any class given' do
|
82
|
+
described_class.should be_any_module(String)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'is false if an instance given' do
|
86
|
+
described_class.should_not be_any_module('Koyomi')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe Peeek::Hook, '.any_instance?' do
|
91
|
+
it 'is true if an instance given' do
|
92
|
+
described_class.should be_any_instance('Koyomi')
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'is false if any module given' do
|
96
|
+
described_class.should_not be_any_instance(Enumerable)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'is false if any class given' do
|
100
|
+
described_class.should_not be_any_instance(String)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe Peeek::Hook, '#initialize' do
|
105
|
+
it 'raises ArgumentError if the linker class is invalid' do
|
106
|
+
lambda { described_class.new(String, :%, String) }.should raise_error(ArgumentError, 'invalid as linker class, Peeek::Hook::Instance or Peeek::Hook::Singleton are valid')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe Peeek::Hook, '#object' do
|
111
|
+
it 'returns the value when constructed the hook' do
|
112
|
+
hook = described_class.new(String, :%, Peeek::Hook::Instance)
|
113
|
+
hook.object.should == String
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe Peeek::Hook, '#method_name' do
|
118
|
+
it 'returns the value when constructed the hook' do
|
119
|
+
hook = described_class.new(String, :%, Peeek::Hook::Instance)
|
120
|
+
hook.method_name.should == :%
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe Peeek::Hook, '#instance?' do
|
125
|
+
it 'is true if the hook is for instance method' do
|
126
|
+
hook = sample_instance_hook
|
127
|
+
hook.should be_instance
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'is false if the hook is for singleton method' do
|
131
|
+
hook = sample_singleton_hook
|
132
|
+
hook.should_not be_instance
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe Peeek::Hook, '#singleton?' do
|
137
|
+
it 'is true if the hook is for singleton method' do
|
138
|
+
hook = sample_singleton_hook
|
139
|
+
hook.should be_singleton
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'is false if the hook is for instance method' do
|
143
|
+
hook = sample_instance_hook
|
144
|
+
hook.should_not be_singleton
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe Peeek::Hook, '#defined?' do
|
149
|
+
before do
|
150
|
+
@linker, original_method = instance_linker_stub(String, :%)
|
151
|
+
@hook = sample_instance_hook(@linker)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "calls #{described_class}::Linker#defined?" do
|
155
|
+
@linker.should_receive(:defined?)
|
156
|
+
@hook.defined?
|
157
|
+
end
|
158
|
+
|
159
|
+
it "returns return value from #{described_class}::Linker#defined?" do
|
160
|
+
@linker.stub!(:defined? => true)
|
161
|
+
return_value = @hook.defined?
|
162
|
+
return_value.should == true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe Peeek::Hook, '#link' do
|
167
|
+
before do
|
168
|
+
@linker, @original_method = instance_linker_stub(String, :%)
|
169
|
+
@hook = sample_instance_hook(@linker)
|
170
|
+
end
|
171
|
+
|
172
|
+
after do
|
173
|
+
@hook.unlink
|
174
|
+
end
|
175
|
+
|
176
|
+
it "calls #{described_class}::Linker#link with the block" do
|
177
|
+
@linker.should_receive(:link).with { }.and_return do |&block|
|
178
|
+
block.arity.should == 3
|
179
|
+
@original_method
|
180
|
+
end
|
181
|
+
|
182
|
+
@hook.link
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'links the hook to the method' do
|
186
|
+
@hook.should_not be_linked # assert
|
187
|
+
@hook.link
|
188
|
+
@hook.should be_linked
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'returns self' do
|
192
|
+
@hook.link.should equal(@hook)
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'in linked' do
|
196
|
+
before do
|
197
|
+
@linker, original_method = instance_linker_stub(String, :%)
|
198
|
+
@hook = sample_instance_hook(@linker)
|
199
|
+
@hook.link
|
200
|
+
end
|
201
|
+
|
202
|
+
after do
|
203
|
+
@hook.unlink
|
204
|
+
end
|
205
|
+
|
206
|
+
it "doesn't call #{described_class}::Linker#link" do
|
207
|
+
@linker.should_not_receive(:link)
|
208
|
+
@hook.link
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'returns self' do
|
212
|
+
@hook.link.should equal(@hook)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe Peeek::Hook, '#unlink' do
|
218
|
+
before do
|
219
|
+
@linker, @original_method = instance_linker_stub(String, :%)
|
220
|
+
@hook = sample_instance_hook(@linker)
|
221
|
+
@hook.link
|
222
|
+
end
|
223
|
+
|
224
|
+
it "calls #{described_class}::Linker#unlink with the original method" do
|
225
|
+
@linker.should_receive(:unlink).with(@original_method)
|
226
|
+
@hook.unlink
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'unlinks the hook from the method' do
|
230
|
+
@hook.should be_linked # assert
|
231
|
+
@hook.unlink
|
232
|
+
@hook.should_not be_linked
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'returns self' do
|
236
|
+
@hook.unlink.should equal(@hook)
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'in not linked' do
|
240
|
+
before do
|
241
|
+
@linker, original_method = instance_linker_stub(String, :%)
|
242
|
+
@hook = sample_instance_hook(@linker)
|
243
|
+
end
|
244
|
+
|
245
|
+
it "doesn't call #{described_class}::Linker#unlink" do
|
246
|
+
@linker.should_not_receive(:unlink)
|
247
|
+
@hook.unlink
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'returns self' do
|
251
|
+
@hook.unlink.should equal(@hook)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe Peeek::Hook, '#to_s' do
|
257
|
+
context 'for instance method' do
|
258
|
+
it 'returns the stringified hook' do
|
259
|
+
hook = sample_instance_hook
|
260
|
+
hook.to_s.should == 'String#%'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context 'for singleton method' do
|
265
|
+
it 'returns the stringified hook' do
|
266
|
+
hook = sample_singleton_hook
|
267
|
+
hook.to_s.should == "#{$stdout.inspect}.write"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe Peeek::Hook, '#inspect' do
|
273
|
+
context 'for instance method' do
|
274
|
+
it 'inspects the hook' do
|
275
|
+
hook = sample_instance_hook
|
276
|
+
hook.inspect.should == "#<#{described_class} String#%>"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'for singleton method' do
|
281
|
+
it 'inspects the hook' do
|
282
|
+
hook = sample_singleton_hook
|
283
|
+
hook.inspect.should == "#<#{described_class} #{$stdout.inspect}.write>"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context 'in linked' do
|
288
|
+
before do
|
289
|
+
@hook = sample_instance_hook
|
290
|
+
@hook.link
|
291
|
+
end
|
292
|
+
|
293
|
+
after do
|
294
|
+
@hook.unlink
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'inspects the hook' do
|
298
|
+
@hook.inspect.should == "#<#{described_class} String#% (linked)>"
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
describe 'recording of a call by', Peeek::Hook do
|
304
|
+
before do
|
305
|
+
@hook = Peeek::Hook.create(String, :%)
|
306
|
+
@hook.link
|
307
|
+
end
|
308
|
+
|
309
|
+
after do
|
310
|
+
@hook.unlink
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'sets attributes to the call' do
|
314
|
+
line = __LINE__; '%s (%d)' % ['Koyomi', 18]
|
315
|
+
call = @hook.calls.first
|
316
|
+
call.file.should == __FILE__
|
317
|
+
call.line.should == line
|
318
|
+
call.receiver.should == '%s (%d)'
|
319
|
+
call.arguments.should == [['Koyomi', 18]]
|
320
|
+
end
|
321
|
+
|
322
|
+
context 'if a value returned' do
|
323
|
+
it 'sets the return value to the call' do
|
324
|
+
'%s (%d)' % ['Koyomi', 18]
|
325
|
+
call = @hook.calls.first
|
326
|
+
call.should be_returned
|
327
|
+
call.return_value.should == 'Koyomi (18)'
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'returns same value when called the method' do
|
331
|
+
return_value = '%s (%d)' % ['Koyomi', 18]
|
332
|
+
call = @hook.calls.first
|
333
|
+
return_value.should equal(call.return_value)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'if an exception raised' do
|
338
|
+
it 'sets the exception to the call' do
|
339
|
+
'%s (%d)' % ['Koyomi'] rescue
|
340
|
+
call = @hook.calls.first
|
341
|
+
call.should be_raised
|
342
|
+
call.exception.should be_an(ArgumentError)
|
343
|
+
call.exception.message.should == 'too few arguments'
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'raises same exception when called the method' do
|
347
|
+
exception = '%s (%d)' % ['Koyomi'] rescue $!
|
348
|
+
call = @hook.calls.first
|
349
|
+
exception.should equal(call.exception)
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'raises the exception with valid backtrace' do
|
353
|
+
line = __LINE__; exception = '%s (%d)' % ['Koyomi'] rescue $!
|
354
|
+
exception.backtrace.first.should be_start_with("#{__FILE__}:#{line}")
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context 'with a block' do
|
359
|
+
before do
|
360
|
+
@hook.unlink
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'calls the block with the call' do
|
364
|
+
line = nil
|
365
|
+
|
366
|
+
hook = Peeek::Hook.create(String, :%) do |call|
|
367
|
+
call.file.should == __FILE__
|
368
|
+
call.line.should == line
|
369
|
+
call.receiver.should == '%s (%d)'
|
370
|
+
call.arguments.should == [['Koyomi', 18]]
|
371
|
+
call.should be_returned
|
372
|
+
call.return_value.should == 'Koyomi (18)'
|
373
|
+
end
|
374
|
+
|
375
|
+
hook.link
|
376
|
+
line = __LINE__; '%s (%d)' % ['Koyomi', 18]
|
377
|
+
hook.unlink
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|