im-lost 1.0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +144 -0
- data/examples/foo.rb +65 -0
- data/examples/kernel_calls.rb +17 -0
- data/lib/im-lost/version.rb +6 -0
- data/lib/im-lost.rb +341 -0
- data/lib/im_lost.rb +3 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 39160aff2b7fa8320ae12c5d035fa40fbf6f96a2fbe0418ddfb31ac700c2bf72
|
4
|
+
data.tar.gz: 044bf4b9b4f9288f418b0cc5344cc82d1a88839d08a8ef1b8008d616ef11481c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d76261aafb61289cf56c23fb2acb0c0e6a0b8cc834b4e28b72d80cf0f6dfdf7966a8b613c7a0f4e8ee005eae2ba4064fe9aeb7b7520cbd53ce8e99671e33f0f
|
7
|
+
data.tar.gz: da598803cf6e2ee4e4baa92f8e702796050f279645daf80fccffa16c2be2eaa314cee7a69742691dde5d798f44f75d610dcb7db71d223a72c0fcabb2e65c7030
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Mike Blumtritt
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# ImLost 
|
2
|
+
|
3
|
+
If you have overlooked something again and don't really understand what your code is doing. If you have to maintain this application but can't really find your way around and certainly can't track down that stupid error. If you feel lost in all that code, here's the gem to help you out!
|
4
|
+
|
5
|
+
ImLost helps you by analyzing function calls of objects, informing you about exceptions and logging your way through your code. In short, ImLost is your debugging helper!
|
6
|
+
|
7
|
+
- Gem: [rubygems.org](https://rubygems.org/gems/im-lost)
|
8
|
+
- Source: [github.com](https://github.com/mblumtritt/im-lost)
|
9
|
+
- Help: [rubydoc.info](https://rubydoc.info/gems/im-lost/ImLost)
|
10
|
+
|
11
|
+
## Description
|
12
|
+
|
13
|
+
If you like to undertsand method call details you get a call trace with `ImLost.trace`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
File.open('test.txt', 'w') do |file|
|
17
|
+
ImLost.trace(file) do
|
18
|
+
file << 'hello '
|
19
|
+
file.puts(:world!)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# output will look like
|
23
|
+
# > IO#<<(?)
|
24
|
+
# /projects/test.rb:1
|
25
|
+
# > IO#write(*)
|
26
|
+
# /projects/test.rb:1
|
27
|
+
# > IO#puts(*)
|
28
|
+
# /projects/test.rb:2
|
29
|
+
# > IO#write(*)
|
30
|
+
# /projects/test.rb:2
|
31
|
+
```
|
32
|
+
|
33
|
+
When you need to know if exceptions are raised and handled you can use `ImLost.trace_exceptions`:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
ImLost.trace_exceptions do
|
37
|
+
File.write('/', 'test')
|
38
|
+
rescue SystemCallError
|
39
|
+
raise('something went wrong!')
|
40
|
+
end
|
41
|
+
# output will look like
|
42
|
+
# x Errno::EEXIST: File exists @ rb_sysopen - /
|
43
|
+
# /projects/test.rb:2
|
44
|
+
# ! Errno::EEXIST: File exists @ rb_sysopen - /
|
45
|
+
# /projects/test.rb:3
|
46
|
+
# x RuntimeError: something went wrong!
|
47
|
+
# /projects/test.rb:4
|
48
|
+
```
|
49
|
+
|
50
|
+
When you like to know if and when a code point is reached, `ImLost.here` will help:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
ImLost.here
|
54
|
+
```
|
55
|
+
|
56
|
+
## Example
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
require 'im-lost'
|
60
|
+
|
61
|
+
class Foo
|
62
|
+
def self.create(value:) = new(value)
|
63
|
+
|
64
|
+
attr_reader :value
|
65
|
+
|
66
|
+
def initialize(value)
|
67
|
+
@value = value
|
68
|
+
end
|
69
|
+
|
70
|
+
def foo(arg, *args, key: nil, **kw_args, &block)
|
71
|
+
@value = "#{arg}-#{key}-[#{args.join(',')}]-#{kw_args.inspect}-#{bar}"
|
72
|
+
block ? block.call(@value) : @value
|
73
|
+
end
|
74
|
+
|
75
|
+
def bar = :bar
|
76
|
+
end
|
77
|
+
|
78
|
+
ImLost.trace_results = true
|
79
|
+
ImLost.trace(Foo)
|
80
|
+
|
81
|
+
my_foo = Foo.create(value: :foo!)
|
82
|
+
ImLost.trace(my_foo)
|
83
|
+
|
84
|
+
my_foo.foo(1, key: :none)
|
85
|
+
my_foo.foo(2, :a, :b, :c, key: :some, name: :value)
|
86
|
+
my_foo.foo(3) { puts _1 }
|
87
|
+
|
88
|
+
# output will look like
|
89
|
+
# > Foo.create(:foo!)
|
90
|
+
# /projects/foo.rb:25
|
91
|
+
# > Foo.new(*)
|
92
|
+
# /projects/foo.rb:6
|
93
|
+
# < Foo.new(*)
|
94
|
+
# = #<Foo:0x0000000100902418 @value=:foo!>
|
95
|
+
# < Foo.create(:foo!)
|
96
|
+
# = #<Foo:0x0000000100902418 @value=:foo!>
|
97
|
+
# > Foo#foo(1, *[], :none, **{}, &nil)
|
98
|
+
# /projects/foo.rb:28
|
99
|
+
# > Foo#bar()
|
100
|
+
# /projects/foo.rb:15
|
101
|
+
# < Foo#bar()
|
102
|
+
# = :bar
|
103
|
+
# < Foo#foo(1, *[], :none, **{}, &nil)
|
104
|
+
# = "1-none-[]-{}-bar"
|
105
|
+
# > Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
106
|
+
# /projects/foo.rb:29
|
107
|
+
# > Foo#bar()
|
108
|
+
# /projects/foo.rb:15
|
109
|
+
# < Foo#bar()
|
110
|
+
# = :bar
|
111
|
+
# < Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
112
|
+
# = "2-some-[a,b,c]-{:name=>:value}-bar"
|
113
|
+
# > Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100900578 /projects/foo.rb:30>)
|
114
|
+
# /projects/foo.rb:30
|
115
|
+
# > Foo#bar()
|
116
|
+
# /projects/foo.rb:15
|
117
|
+
# < Foo#bar()
|
118
|
+
# = :bar
|
119
|
+
# 3--[]-{}-bar
|
120
|
+
# < Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100900578 /projects/foo.rb:30>)
|
121
|
+
# = nil
|
122
|
+
```
|
123
|
+
|
124
|
+
See [examples dir](./examples) for more…
|
125
|
+
|
126
|
+
## Installation
|
127
|
+
|
128
|
+
You can install the gem in your system with
|
129
|
+
|
130
|
+
```shell
|
131
|
+
gem install im-lost
|
132
|
+
```
|
133
|
+
|
134
|
+
or you can use [Bundler](http://gembundler.com/) to add ImLost to your own project:
|
135
|
+
|
136
|
+
```shell
|
137
|
+
bundle add im-lost
|
138
|
+
```
|
139
|
+
|
140
|
+
After that you only need one line of code to have everything together
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
require 'im-lost'
|
144
|
+
```
|
data/examples/foo.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/im-lost'
|
4
|
+
|
5
|
+
class Foo
|
6
|
+
def self.create(value:) = new(value)
|
7
|
+
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
def initialize(value)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def foo(arg, *args, key: nil, **kw_args, &block)
|
15
|
+
@value = "#{arg}-#{key}-[#{args.join(',')}]-#{kw_args.inspect}-#{bar}"
|
16
|
+
block ? block.call(@value) : @value
|
17
|
+
end
|
18
|
+
|
19
|
+
def bar = :bar
|
20
|
+
end
|
21
|
+
|
22
|
+
ImLost.trace_results = true
|
23
|
+
ImLost.trace(Foo)
|
24
|
+
|
25
|
+
my_foo = Foo.create(value: :foo!)
|
26
|
+
ImLost.trace(my_foo)
|
27
|
+
|
28
|
+
my_foo.foo(1, key: :none)
|
29
|
+
my_foo.foo(2, :a, :b, :c, key: :some, name: :value)
|
30
|
+
my_foo.foo(3) { puts _1 }
|
31
|
+
|
32
|
+
# output will look like
|
33
|
+
# > Foo.create(:foo!)
|
34
|
+
# /projects/foo.rb:25
|
35
|
+
# > Foo.new(*)
|
36
|
+
# /projects/foo.rb:6
|
37
|
+
# < Foo.new(*)
|
38
|
+
# = #<Foo:0x0000000100902418 @value=:foo!>
|
39
|
+
# < Foo.create(:foo!)
|
40
|
+
# = #<Foo:0x0000000100902418 @value=:foo!>
|
41
|
+
# > Foo#foo(1, *[], :none, **{}, &nil)
|
42
|
+
# /projects/foo.rb:28
|
43
|
+
# > Foo#bar()
|
44
|
+
# /projects/foo.rb:15
|
45
|
+
# < Foo#bar()
|
46
|
+
# = :bar
|
47
|
+
# < Foo#foo(1, *[], :none, **{}, &nil)
|
48
|
+
# = "1-none-[]-{}-bar"
|
49
|
+
# > Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
50
|
+
# /projects/foo.rb:29
|
51
|
+
# > Foo#bar()
|
52
|
+
# /projects/foo.rb:15
|
53
|
+
# < Foo#bar()
|
54
|
+
# = :bar
|
55
|
+
# < Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
56
|
+
# = "2-some-[a,b,c]-{:name=>:value}-bar"
|
57
|
+
# > Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100900578 /projects/foo.rb:30>)
|
58
|
+
# /projects/foo.rb:30
|
59
|
+
# > Foo#bar()
|
60
|
+
# /projects/foo.rb:15
|
61
|
+
# < Foo#bar()
|
62
|
+
# = :bar
|
63
|
+
# 3--[]-{}-bar
|
64
|
+
# < Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100900578 /projects/foo.rb:30>)
|
65
|
+
# = nil
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
puts <<~INFO
|
4
|
+
|
5
|
+
This example traces calls for very basic Ruby objects when a new Class is
|
6
|
+
generated and pretty_print is automatically loaded.
|
7
|
+
|
8
|
+
INFO
|
9
|
+
|
10
|
+
require 'im-lost'
|
11
|
+
|
12
|
+
ImLost.trace_results = true
|
13
|
+
ImLost.trace(Kernel, Object, Module, Class, self) do
|
14
|
+
puts '=' * 79
|
15
|
+
pp Class.new
|
16
|
+
puts '=' * 79
|
17
|
+
end
|
data/lib/im-lost.rb
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImLost
|
4
|
+
class << self
|
5
|
+
#
|
6
|
+
# Enables/disables to include code location into traced call information.
|
7
|
+
# This is enabled by default.
|
8
|
+
#
|
9
|
+
# @return [Boolean] whether code location will be included
|
10
|
+
#
|
11
|
+
attr_reader :caller_locations
|
12
|
+
|
13
|
+
def caller_locations=(value)
|
14
|
+
@caller_locations = value ? true : false
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# The output device used to write information.
|
19
|
+
# This should be an `IO` device or any other object responding to `#puts`.
|
20
|
+
#
|
21
|
+
# `$stderr` is configured by default.
|
22
|
+
#
|
23
|
+
# @example Write to a file
|
24
|
+
# ImLost.output = File.new('./trace', 'w')
|
25
|
+
#
|
26
|
+
# @example Write temporary into a memory stream
|
27
|
+
# require 'stringio'
|
28
|
+
#
|
29
|
+
# original = ImLost.output
|
30
|
+
# begin
|
31
|
+
# ImLost.output = StringIO.new
|
32
|
+
# # ... collect trace information
|
33
|
+
# puts(ImLost.output.string) # or whatever
|
34
|
+
# ensure
|
35
|
+
# ImLost.output = original
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @return [#puts] the output device
|
39
|
+
#
|
40
|
+
attr_reader :output
|
41
|
+
|
42
|
+
def output=(value)
|
43
|
+
return @output = value if value.respond_to?(:puts)
|
44
|
+
raise(ArgumentError, "invalid output device - #{value.inspect}")
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Enables/disables tracing of method calls.
|
49
|
+
# This is enabled by default.
|
50
|
+
#
|
51
|
+
# @attribute [r] trace_calls
|
52
|
+
# @return [Boolean] whether method calls will be traced
|
53
|
+
#
|
54
|
+
def trace_calls = @trace_calls[0].enabled?
|
55
|
+
|
56
|
+
def trace_calls=(value)
|
57
|
+
if value
|
58
|
+
@trace_calls.each(&:enable) unless trace_calls
|
59
|
+
elsif trace_calls
|
60
|
+
@trace_calls.each(&:disable)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Traces execptions raised within a given block.
|
66
|
+
#
|
67
|
+
# @example Trace exception and rescue handling
|
68
|
+
# ImLost.trace_exceptions do
|
69
|
+
# File.write('/', 'test')
|
70
|
+
# rescue SystemCallError
|
71
|
+
# raise('something went wrong!')
|
72
|
+
# end
|
73
|
+
# # output will look like
|
74
|
+
# # x Errno::EEXIST: File exists @ rb_sysopen - /
|
75
|
+
# # /projects/test.rb:2
|
76
|
+
# # ! Errno::EEXIST: File exists @ rb_sysopen - /
|
77
|
+
# # /projects/test.rb:3
|
78
|
+
# # x RuntimeError: something went wrong!
|
79
|
+
# # /projects/test.rb:4
|
80
|
+
#
|
81
|
+
# @param with_locations [Boolean] wheter the locations should be included
|
82
|
+
# into the exception trace information
|
83
|
+
# @yieldreturn [Object] return result
|
84
|
+
#
|
85
|
+
def trace_exceptions(with_locations: true)
|
86
|
+
return unless block_given?
|
87
|
+
we = @trace_exceptions.enabled?
|
88
|
+
el = @exception_locations
|
89
|
+
@exception_locations = with_locations
|
90
|
+
@trace_exceptions.enable unless we
|
91
|
+
yield
|
92
|
+
ensure
|
93
|
+
@trace_exceptions.disable unless we
|
94
|
+
@exception_locations = el
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# Enables/disables tracing of returned valuess of method calls.
|
99
|
+
# This is disabled by default.
|
100
|
+
#
|
101
|
+
# @attribute [r] trace_results
|
102
|
+
# @return [Boolean] whether return values will be traced
|
103
|
+
#
|
104
|
+
def trace_results = @trace_results[0].enabled?
|
105
|
+
|
106
|
+
def trace_results=(value)
|
107
|
+
if value
|
108
|
+
@trace_results.each(&:enable) unless trace_results
|
109
|
+
elsif trace_results
|
110
|
+
@trace_results.each(&:disable)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Print the call location conditionally.
|
116
|
+
#
|
117
|
+
# @example simply print location
|
118
|
+
# ImLost.here
|
119
|
+
#
|
120
|
+
# @example print location when instance variable is empty
|
121
|
+
# ImLost.here(@name.empty?)
|
122
|
+
#
|
123
|
+
# @example print location when instance variable is nil or empty
|
124
|
+
# ImLost.here { @name.nil? || @name.empty? }
|
125
|
+
#
|
126
|
+
# @overload here
|
127
|
+
# Prints the caller location.
|
128
|
+
# @return [true]
|
129
|
+
#
|
130
|
+
# @overload here(test)
|
131
|
+
# Prints the caller location when given argument is truthy.
|
132
|
+
# @param test [Object]
|
133
|
+
# @return [Object] test
|
134
|
+
#
|
135
|
+
# @overload here
|
136
|
+
# Prints the caller location when given block returns a truthy result.
|
137
|
+
# @yield When the block returns a truthy result the location will be print
|
138
|
+
# @yieldreturn [Object] return result
|
139
|
+
#
|
140
|
+
def here(test = true)
|
141
|
+
return test if !test || (block_given? && !(test = yield))
|
142
|
+
loc = Kernel.caller_locations(1, 1)[0]
|
143
|
+
@output.puts(": #{loc.path}:#{loc.lineno}")
|
144
|
+
test
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Trace objects.
|
149
|
+
#
|
150
|
+
# The given arguments can be any object instance or module or class.
|
151
|
+
#
|
152
|
+
# @example trace method calls of an instance variable for a while
|
153
|
+
# ImLost.trace(@file)
|
154
|
+
# # ...
|
155
|
+
# ImLost.untrace(@file)
|
156
|
+
#
|
157
|
+
# @example temporary trace method calls
|
158
|
+
# File.open('test.txt', 'w') do |file|
|
159
|
+
# ImLost.trace(file) do
|
160
|
+
# file << 'hello '
|
161
|
+
# file.puts(:world!)
|
162
|
+
# end
|
163
|
+
# end
|
164
|
+
# output will look like
|
165
|
+
# > IO#<<(?)
|
166
|
+
# /projects/test.rb:1
|
167
|
+
# > IO#write(*)
|
168
|
+
# /projects/test.rb:1
|
169
|
+
# > IO#puts(*)
|
170
|
+
# /projects/test.rb:2
|
171
|
+
# > IO#write(*)
|
172
|
+
# /projects/test.rb:2
|
173
|
+
#
|
174
|
+
# @overload trace(*args)
|
175
|
+
# @param args [[Object]] one or more objects to be traced
|
176
|
+
# @return [[Object]] the traced object(s)
|
177
|
+
# Start tracing the given objects.
|
178
|
+
# @see untrace
|
179
|
+
# @see untrace_all!
|
180
|
+
#
|
181
|
+
#
|
182
|
+
# @overload trace(*args)
|
183
|
+
# @param args [[Object]] one or more objects to be traced
|
184
|
+
# @yieldparam args [Object] the traced object(s)
|
185
|
+
# @yieldreturn [Object] return result
|
186
|
+
# Traces the given object(s) inside the block only.
|
187
|
+
# The object(s) will not be traced any longer after the block call.
|
188
|
+
#
|
189
|
+
def trace(*args, &block)
|
190
|
+
return block&.call if args.empty?
|
191
|
+
return args.size == 1 ? _trace(args[0]) : _trace_all(args) unless block
|
192
|
+
args.size == 1 ? _trace_b(args[0], &block) : _trace_all_b(args, &block)
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Stop tracing objects.
|
197
|
+
#
|
198
|
+
# @example trace some objects for some code lines
|
199
|
+
# traced_vars = ImLost.trace(@file, @client)
|
200
|
+
# # ...
|
201
|
+
# ImLost.untrace(*traced_vars)
|
202
|
+
#
|
203
|
+
# @see trace
|
204
|
+
#
|
205
|
+
# @param args [[Object]] one or more objects which should not longer be
|
206
|
+
# traced
|
207
|
+
# @return [[Object]] the object(s) which are not longer be traced
|
208
|
+
# @return [nil] when none of the objects was traced before
|
209
|
+
#
|
210
|
+
def untrace(*args)
|
211
|
+
ret = args.filter_map { @trace.delete(_1.__id__) ? _1 : nil }
|
212
|
+
args.size == 1 ? ret[0] : ret
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# Stop tracing any object.
|
217
|
+
# (When you are really lost and just like to stop tracing of all your
|
218
|
+
# objects.)
|
219
|
+
#
|
220
|
+
# @see trace
|
221
|
+
#
|
222
|
+
# @return [self] itself
|
223
|
+
#
|
224
|
+
def untrace_all!
|
225
|
+
@trace = {}.compare_by_identity
|
226
|
+
self
|
227
|
+
end
|
228
|
+
|
229
|
+
protected
|
230
|
+
|
231
|
+
def as_sig(prefix, info, args)
|
232
|
+
args = args.join(', ')
|
233
|
+
case info.self
|
234
|
+
when Class, Module
|
235
|
+
"#{prefix} #{info.self}.#{info.method_id}(#{args})"
|
236
|
+
else
|
237
|
+
"#{prefix} #{info.defined_class}##{info.method_id}(#{args})"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def _trace(arg)
|
244
|
+
@trace[arg.__id__] = 1 if self != arg && @output != arg
|
245
|
+
arg
|
246
|
+
end
|
247
|
+
|
248
|
+
def _trace_all(args)
|
249
|
+
args.each do |arg|
|
250
|
+
@trace[arg.__id__] = 1 if arg != self && @output != arg
|
251
|
+
end
|
252
|
+
args
|
253
|
+
end
|
254
|
+
|
255
|
+
def _trace_b(arg)
|
256
|
+
@trace[id = arg.__id__] = 1 if self != arg && @output != arg
|
257
|
+
yield(arg)
|
258
|
+
ensure
|
259
|
+
@trace.delete(id) if id
|
260
|
+
end
|
261
|
+
|
262
|
+
def _trace_all_b(args)
|
263
|
+
ids =
|
264
|
+
args.filter_map do |arg|
|
265
|
+
next if self == arg || @output == arg
|
266
|
+
@trace[id = arg.__id__] = 1
|
267
|
+
id
|
268
|
+
end
|
269
|
+
yield(args)
|
270
|
+
ensure
|
271
|
+
ids.each { @trace.delete(_1) }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
ARG_SIG = { rest: '*', keyrest: '**', block: '&' }.compare_by_identity.freeze
|
276
|
+
NO_NAME = %i[* ** &].freeze
|
277
|
+
EX_PREFIX = { raise: 'x', rescue: '!' }.freeze
|
278
|
+
private_constant :ARG_SIG, :NO_NAME, :EX_PREFIX
|
279
|
+
|
280
|
+
@trace = {}.compare_by_identity
|
281
|
+
@caller_locations = true
|
282
|
+
@output = $stderr.respond_to?(:puts) ? $stderr : STDERR
|
283
|
+
|
284
|
+
@trace_calls = [
|
285
|
+
TracePoint.new(:c_call) do |tp|
|
286
|
+
next unless @trace.key?(tp.self.__id__)
|
287
|
+
@output.puts(as_sig('>', tp, tp.parameters.map { ARG_SIG[_1[0]] || '?' }))
|
288
|
+
@output.puts(" #{tp.path}:#{tp.lineno}") if @caller_locations
|
289
|
+
end,
|
290
|
+
TracePoint.new(:call) do |tp|
|
291
|
+
next unless @trace.key?(tp.self.__id__)
|
292
|
+
ctx = tp.binding
|
293
|
+
@output.puts(
|
294
|
+
as_sig(
|
295
|
+
'>',
|
296
|
+
tp,
|
297
|
+
tp.parameters.map do |kind, name|
|
298
|
+
next name if NO_NAME.include?(name)
|
299
|
+
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
300
|
+
end
|
301
|
+
)
|
302
|
+
)
|
303
|
+
next unless @caller_locations
|
304
|
+
loc = ctx.eval('caller_locations(4,1)')[0]
|
305
|
+
@output.puts(" #{loc.path}:#{loc.lineno}")
|
306
|
+
end
|
307
|
+
]
|
308
|
+
|
309
|
+
@trace_results = [
|
310
|
+
TracePoint.new(:c_return) do |tp|
|
311
|
+
next unless @trace.key?(tp.self.__id__)
|
312
|
+
@output.puts(as_sig('<', tp, tp.parameters.map { ARG_SIG[_1[0]] || '?' }))
|
313
|
+
@output.puts(" = #{tp.return_value.inspect}")
|
314
|
+
end,
|
315
|
+
TracePoint.new(:return) do |tp|
|
316
|
+
next unless @trace.key?(tp.self.__id__)
|
317
|
+
ctx = tp.binding
|
318
|
+
@output.puts(
|
319
|
+
as_sig(
|
320
|
+
'<',
|
321
|
+
tp,
|
322
|
+
tp.parameters.map do |kind, name|
|
323
|
+
next name if %i[* ** &].include?(name)
|
324
|
+
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
325
|
+
end
|
326
|
+
)
|
327
|
+
)
|
328
|
+
@output.puts(" = #{tp.return_value.inspect}")
|
329
|
+
end
|
330
|
+
]
|
331
|
+
|
332
|
+
supported = RUBY_VERSION >= '3.3.0' ? %i[raise rescue] : %i[raise]
|
333
|
+
@trace_exceptions =
|
334
|
+
TracePoint.new(*supported) do |tp|
|
335
|
+
ex = tp.raised_exception.inspect
|
336
|
+
@output.puts("#{EX_PREFIX[tp.event]} #{ex[0] == '#' ? ex[2..-2] : ex}")
|
337
|
+
@output.puts(" #{tp.path}:#{tp.lineno}") if @exception_locations
|
338
|
+
end
|
339
|
+
|
340
|
+
self.trace_calls = true
|
341
|
+
end
|
data/lib/im_lost.rb
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: im-lost
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Blumtritt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-05-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
If you have overlooked something again and don't really understand what
|
15
|
+
your code is doing. If you have to maintain this application but can't
|
16
|
+
really find your way around and certainly can't track down that stupid
|
17
|
+
error. If you feel lost in all that code, here's the gem to help you out!
|
18
|
+
|
19
|
+
ImLost helps you by analyzing function calls of objects, informing you
|
20
|
+
about exceptions and logging your way through your code. In short, ImLost
|
21
|
+
is your debugging helper!
|
22
|
+
email:
|
23
|
+
executables: []
|
24
|
+
extensions: []
|
25
|
+
extra_rdoc_files:
|
26
|
+
- README.md
|
27
|
+
- LICENSE
|
28
|
+
files:
|
29
|
+
- LICENSE
|
30
|
+
- README.md
|
31
|
+
- examples/foo.rb
|
32
|
+
- examples/kernel_calls.rb
|
33
|
+
- lib/im-lost.rb
|
34
|
+
- lib/im-lost/version.rb
|
35
|
+
- lib/im_lost.rb
|
36
|
+
homepage: https://github.com/mblumtritt/im-lost
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata:
|
40
|
+
source_code_uri: https://github.com/mblumtritt/im-lost
|
41
|
+
bug_tracker_uri: https://github.com/mblumtritt/im-lost/issues
|
42
|
+
documentation_uri: https://rubydoc.info/gems/im-lost
|
43
|
+
rubygems_mfa_required: 'true'
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '3.0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubygems_version: 3.5.10
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: Your debugging helper.
|
63
|
+
test_files: []
|