im-lost 1.0.2 → 1.1.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 +4 -4
- data/README.md +3 -1
- data/examples/foo.rb +1 -1
- data/examples/timer.rb +29 -0
- data/lib/im-lost/version.rb +1 -1
- data/lib/im-lost.rb +174 -25
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be7b612cd1d68529fbba18b5a9a4759e60111facc52e99a20b4da140e887f8c4
|
4
|
+
data.tar.gz: c1aeff67c05a2e31c0dab72c6c5ed84bb27f9231e759de874882a082f0fac284
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9821d580f5c5e4e62133dba01fbdd9a26ec3cfd8ffe8ed6603b24760d5073184bd22bdbc6ab95317c6a39690abc56ad4e2e5afdd5df4e3d261525afc927448eb
|
7
|
+
data.tar.gz: b12a64d4ee2b7e9f1764c5bcb0e79a5f9b240b9b49359c10868abc2f763cefb8ea4a7844c0ba17239346157f516b10a6fc8d368386206cb0fea0c6cc330a7601
|
data/README.md
CHANGED
@@ -19,6 +19,7 @@ File.open('test.txt', 'w') do |file|
|
|
19
19
|
file.puts(:world!)
|
20
20
|
end
|
21
21
|
end
|
22
|
+
|
22
23
|
# output will look like
|
23
24
|
# > IO#<<(?)
|
24
25
|
# /projects/test.rb:1
|
@@ -38,6 +39,7 @@ ImLost.trace_exceptions do
|
|
38
39
|
rescue SystemCallError
|
39
40
|
raise('something went wrong!')
|
40
41
|
end
|
42
|
+
|
41
43
|
# output will look like
|
42
44
|
# x Errno::EEXIST: File exists @ rb_sysopen - /
|
43
45
|
# /projects/test.rb:2
|
@@ -47,7 +49,7 @@ end
|
|
47
49
|
# /projects/test.rb:4
|
48
50
|
```
|
49
51
|
|
50
|
-
When you like to know if
|
52
|
+
When you like to know if a code point is reached, `ImLost.here` will help:
|
51
53
|
|
52
54
|
```ruby
|
53
55
|
ImLost.here
|
data/examples/foo.rb
CHANGED
data/examples/timer.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
puts <<~INFO
|
4
|
+
|
5
|
+
This is an example how to use named and anonymous timers.
|
6
|
+
|
7
|
+
INFO
|
8
|
+
|
9
|
+
require_relative '../lib/im-lost'
|
10
|
+
|
11
|
+
puts 'Create a named timer:'
|
12
|
+
ImLost.timer.create(:first)
|
13
|
+
|
14
|
+
puts 'Create an anonymous timer:'
|
15
|
+
second = ImLost.timer.create
|
16
|
+
|
17
|
+
sleep(0.5) # or whatever
|
18
|
+
|
19
|
+
puts 'print runtime for named timer:'
|
20
|
+
ImLost.timer[:first]
|
21
|
+
|
22
|
+
puts 'print runtime for anonymous timer:'
|
23
|
+
ImLost.timer[second]
|
24
|
+
|
25
|
+
puts 'delete a named timer'
|
26
|
+
ImLost.timer.delete(:first)
|
27
|
+
|
28
|
+
puts 'delete an anonymous timer'
|
29
|
+
ImLost.timer.delete(second)
|
data/lib/im-lost/version.rb
CHANGED
data/lib/im-lost.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
#
|
3
4
|
# If you have overlooked something again and don't really understand what your
|
4
5
|
# code is doing. If you have to maintain this application but can't really find
|
5
6
|
# your way around and certainly can't track down that stupid error. If you feel
|
@@ -53,6 +54,12 @@ module ImLost
|
|
53
54
|
raise(ArgumentError, "invalid output device - #{value.inspect}")
|
54
55
|
end
|
55
56
|
|
57
|
+
#
|
58
|
+
# @return [TimerStore] the timer store used to estimate the runtime of
|
59
|
+
# your code
|
60
|
+
#
|
61
|
+
attr_reader :timer
|
62
|
+
|
56
63
|
#
|
57
64
|
# Enables/disables tracing of method calls.
|
58
65
|
# This is enabled by default.
|
@@ -79,6 +86,7 @@ module ImLost
|
|
79
86
|
# rescue SystemCallError
|
80
87
|
# raise('something went wrong!')
|
81
88
|
# end
|
89
|
+
#
|
82
90
|
# # output will look like
|
83
91
|
# # x Errno::EEXIST: File exists @ rb_sysopen - /
|
84
92
|
# # /projects/test.rb:2
|
@@ -170,15 +178,16 @@ module ImLost
|
|
170
178
|
# file.puts(:world!)
|
171
179
|
# end
|
172
180
|
# end
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
#
|
177
|
-
#
|
178
|
-
#
|
179
|
-
#
|
180
|
-
#
|
181
|
-
#
|
181
|
+
#
|
182
|
+
# # output will look like
|
183
|
+
# # > IO#<<(?)
|
184
|
+
# # /projects/test.rb:1
|
185
|
+
# # > IO#write(*)
|
186
|
+
# # /projects/test.rb:1
|
187
|
+
# # > IO#puts(*)
|
188
|
+
# # /projects/test.rb:2
|
189
|
+
# # > IO#write(*)
|
190
|
+
# # /projects/test.rb:2
|
182
191
|
#
|
183
192
|
# @overload trace(*args)
|
184
193
|
# @param args [[Object]] one or more objects to be traced
|
@@ -187,7 +196,6 @@ module ImLost
|
|
187
196
|
# @see untrace
|
188
197
|
# @see untrace_all!
|
189
198
|
#
|
190
|
-
#
|
191
199
|
# @overload trace(*args)
|
192
200
|
# @param args [[Object]] one or more objects to be traced
|
193
201
|
# @yieldparam args [Object] the traced object(s)
|
@@ -257,7 +265,7 @@ module ImLost
|
|
257
265
|
@trace[traced] = traced if traced
|
258
266
|
end
|
259
267
|
|
260
|
-
|
268
|
+
private
|
261
269
|
|
262
270
|
def as_sig(prefix, info, args)
|
263
271
|
args = args.join(', ')
|
@@ -269,8 +277,6 @@ module ImLost
|
|
269
277
|
end
|
270
278
|
end
|
271
279
|
|
272
|
-
private
|
273
|
-
|
274
280
|
def _trace(arg)
|
275
281
|
id = arg.__id__
|
276
282
|
@trace[id] = id if __id__ != id && @output.__id__ != id
|
@@ -312,11 +318,11 @@ module ImLost
|
|
312
318
|
vars = obj.instance_variables
|
313
319
|
if vars.empty?
|
314
320
|
@output.puts(' <no instance variables defined>')
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
321
|
+
return obj
|
322
|
+
end
|
323
|
+
@output.puts(' instance variables:')
|
324
|
+
vars.sort!.each do |name|
|
325
|
+
@output.puts(" #{name}: #{obj.instance_variable_get(name).inspect}")
|
320
326
|
end
|
321
327
|
obj
|
322
328
|
end
|
@@ -326,16 +332,150 @@ module ImLost
|
|
326
332
|
vars = binding.local_variables
|
327
333
|
if vars.empty?
|
328
334
|
@output.puts(' <no local variables>')
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
335
|
+
return self
|
336
|
+
end
|
337
|
+
@output.puts(' local variables:')
|
338
|
+
vars.sort!.each do |name|
|
339
|
+
@output.puts(" #{name}: #{binding.local_variable_get(name).inspect}")
|
334
340
|
end
|
335
341
|
self
|
336
342
|
end
|
337
343
|
end
|
338
344
|
|
345
|
+
#
|
346
|
+
# A store to create and register timers you can use to estimate the runtime of
|
347
|
+
# some code.
|
348
|
+
#
|
349
|
+
# All timers are identified by an unique ID or a name.
|
350
|
+
#
|
351
|
+
# @example Use a named timer
|
352
|
+
# ImLost.timer.create('my_test')
|
353
|
+
#
|
354
|
+
# # ...your code here...
|
355
|
+
#
|
356
|
+
# ImLost.timer['my_test']
|
357
|
+
# # => prints the timer name, this location and runtime so far
|
358
|
+
#
|
359
|
+
# # ...more code here...
|
360
|
+
#
|
361
|
+
# ImLost.timer['my_test']
|
362
|
+
# # => prints the timer name, this location and runtime since the timer was created
|
363
|
+
#
|
364
|
+
# ImLost.timer.delete('my_test')
|
365
|
+
# # the timer with name 'my_test' is not longer valid now
|
366
|
+
#
|
367
|
+
#
|
368
|
+
# @example Use an anonymous timer (identified by ID)
|
369
|
+
# tmr = ImLost.timer.create
|
370
|
+
#
|
371
|
+
# # ...your code here...
|
372
|
+
#
|
373
|
+
# ImLost.timer[tmr]
|
374
|
+
# # => prints the timer ID, this location and runtime so far
|
375
|
+
#
|
376
|
+
# # ...more code here...
|
377
|
+
#
|
378
|
+
# ImLost.timer[tmr]
|
379
|
+
# # => prints the timer ID, this location and runtime since the timer was created
|
380
|
+
#
|
381
|
+
# ImLost.timer.delete(tmr)
|
382
|
+
# # the timer with the ID `tmr` is not longer valid now
|
383
|
+
#
|
384
|
+
# @see ImLost.timer
|
385
|
+
#
|
386
|
+
class TimerStore
|
387
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
388
|
+
# @return [Float] current time
|
389
|
+
def self.now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
390
|
+
else
|
391
|
+
# @return [Float] current time
|
392
|
+
def self.now = ::Time.now
|
393
|
+
end
|
394
|
+
|
395
|
+
# @attribute [r] count
|
396
|
+
# @return [Integer] the number of registered timers
|
397
|
+
def count = ids.size
|
398
|
+
|
399
|
+
# @attribute [r] empty?
|
400
|
+
# @return [Boolean] wheter the timer store is empty or not
|
401
|
+
def empty? = ids.empty?
|
402
|
+
|
403
|
+
# @attribute [r] ids
|
404
|
+
# @return [Array<Integer>] IDs of all registered timers
|
405
|
+
def ids = (@ll.keys.keep_if { _1.is_a?(Integer) })
|
406
|
+
|
407
|
+
#
|
408
|
+
# Create and register a new named or anonymous timer.
|
409
|
+
# It print the ID or name of the created timer and includes the location.
|
410
|
+
#
|
411
|
+
# @param name [#to_s] optional timer name
|
412
|
+
# @return [Integer] timer ID
|
413
|
+
#
|
414
|
+
def create(name = nil)
|
415
|
+
timer = []
|
416
|
+
@ll[id = timer.__id__] = timer
|
417
|
+
name ? @ll[name = name.to_s] = timer : name = id
|
418
|
+
@cb[name, Kernel.caller_locations(1, 1)[0]]
|
419
|
+
timer << name << self.class.now
|
420
|
+
id
|
421
|
+
end
|
422
|
+
|
423
|
+
#
|
424
|
+
# Delete and unregister timers.
|
425
|
+
#
|
426
|
+
# @param id_or_names [Array<Integer, #to_s>] the IDs or the names
|
427
|
+
# @return [nil]
|
428
|
+
#
|
429
|
+
def delete(*id_or_names)
|
430
|
+
id_or_names.flatten.each do |id|
|
431
|
+
if id.is_a?(Integer)
|
432
|
+
del = @ll.delete(id)
|
433
|
+
@ll.delete(del[0]) if del
|
434
|
+
else
|
435
|
+
del = @ll.delete(id.to_s)
|
436
|
+
@ll.delete(del.__id__) if del
|
437
|
+
end
|
438
|
+
end
|
439
|
+
nil
|
440
|
+
end
|
441
|
+
|
442
|
+
#
|
443
|
+
# Print the ID or name and the runtime since timer was created.
|
444
|
+
# It includes the location.
|
445
|
+
#
|
446
|
+
# @param id_or_name [Integer, #to_s] the identifier or the name of the timer
|
447
|
+
# @return [Integer] timer ID
|
448
|
+
# @raise [ArgumentError] when the given id or name is not a registered timer
|
449
|
+
# identifier or name
|
450
|
+
#
|
451
|
+
def [](id_or_name)
|
452
|
+
time = self.class.now
|
453
|
+
timer = @ll[id_or_name.is_a?(Integer) ? id_or_name : id_or_name.to_s]
|
454
|
+
raise(ArgumentError, "not a timer - #{id_or_name.inspect}") unless timer
|
455
|
+
@cb[timer[0], Kernel.caller_locations(1, 1)[0], time - timer[1]]
|
456
|
+
timer.__id__
|
457
|
+
end
|
458
|
+
|
459
|
+
#
|
460
|
+
# Print the ID or name and the runtime of all active timers.
|
461
|
+
# It includes the location.
|
462
|
+
#
|
463
|
+
# @return [nil]
|
464
|
+
#
|
465
|
+
def all
|
466
|
+
now = self.class.now
|
467
|
+
loc = Kernel.caller_locations(1, 1)[0]
|
468
|
+
@ll.values.uniq.reverse_each { |name, start| @cb[name, loc, now - start] }
|
469
|
+
nil
|
470
|
+
end
|
471
|
+
|
472
|
+
# @!visibility private
|
473
|
+
def initialize(&block)
|
474
|
+
@cb = block
|
475
|
+
@ll = {}
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
339
479
|
ARG_SIG = { rest: '*', keyrest: '**', block: '&' }.compare_by_identity.freeze
|
340
480
|
NO_NAME = { :* => 1, :** => 1, :& => 1 }.compare_by_identity.freeze
|
341
481
|
private_constant :ARG_SIG, :NO_NAME
|
@@ -344,6 +484,15 @@ module ImLost
|
|
344
484
|
@caller_locations = true
|
345
485
|
@output = $stderr.respond_to?(:puts) ? $stderr : STDERR
|
346
486
|
|
487
|
+
@timer =
|
488
|
+
TimerStore.new do |title, location, time|
|
489
|
+
@output.puts(
|
490
|
+
"T #{title}: #{time ? "#{time} sec." : 'created'}",
|
491
|
+
" #{location.path}:#{location.lineno}"
|
492
|
+
)
|
493
|
+
end
|
494
|
+
TimerStore.private_class_method(:new)
|
495
|
+
|
347
496
|
@trace_calls = [
|
348
497
|
TracePoint.new(:c_call) do |tp|
|
349
498
|
next if !@trace.key?(tp.self.__id__) || tp.path == __FILE__
|
@@ -383,7 +532,7 @@ module ImLost
|
|
383
532
|
'<',
|
384
533
|
tp,
|
385
534
|
tp.parameters.map do |kind, name|
|
386
|
-
next name if
|
535
|
+
next name if NO_NAME.key?(name)
|
387
536
|
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
388
537
|
end
|
389
538
|
)
|
@@ -392,7 +541,7 @@ module ImLost
|
|
392
541
|
end
|
393
542
|
]
|
394
543
|
|
395
|
-
supported = RUBY_VERSION
|
544
|
+
supported = RUBY_VERSION.to_f < 3.3 ? %i[raise] : %i[raise rescue]
|
396
545
|
@trace_exceptions =
|
397
546
|
TracePoint.new(*supported) do |tp|
|
398
547
|
ex = tp.raised_exception.inspect
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: im-lost
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Blumtritt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |
|
14
14
|
If you have overlooked something again and don't really understand what
|
@@ -30,6 +30,7 @@ files:
|
|
30
30
|
- README.md
|
31
31
|
- examples/foo.rb
|
32
32
|
- examples/kernel_calls.rb
|
33
|
+
- examples/timer.rb
|
33
34
|
- lib/im-lost.rb
|
34
35
|
- lib/im-lost/version.rb
|
35
36
|
- lib/im_lost.rb
|
@@ -56,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
57
|
- !ruby/object:Gem::Version
|
57
58
|
version: '0'
|
58
59
|
requirements: []
|
59
|
-
rubygems_version: 3.5.
|
60
|
+
rubygems_version: 3.5.11
|
60
61
|
signing_key:
|
61
62
|
specification_version: 4
|
62
63
|
summary: Your debugging helper.
|