puts_debuggerer 0.8.2 → 0.11.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/CHANGELOG.md +87 -0
- data/LICENSE.txt +1 -1
- data/README.md +429 -66
- data/VERSION +1 -1
- data/lib/pd.rb +1 -0
- data/lib/puts_debuggerer.rb +157 -278
- data/lib/puts_debuggerer/core_ext/kernel.rb +224 -0
- data/lib/puts_debuggerer/core_ext/logger.rb +3 -0
- data/lib/puts_debuggerer/core_ext/logging/logger.rb +5 -0
- data/lib/puts_debuggerer/run_determiner.rb +109 -0
- data/lib/puts_debuggerer/source_file.rb +32 -0
- metadata +39 -3
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
# Prints object with bonus info such as file name, line number and source
|
5
|
+
# expression. Optionally prints out header and footer.
|
6
|
+
# Lookup PutsDebuggerer attributes for more details about configuration options.
|
7
|
+
#
|
8
|
+
# Simply invoke global `pd` method anywhere you'd like to see line number and source code with output.
|
9
|
+
# If the argument is a pure string, the print out is simplified by not showing duplicate source.
|
10
|
+
#
|
11
|
+
# Quickly locate printed lines using Find feature (e.g. CTRL+F) by looking for:
|
12
|
+
# * \[PD\]
|
13
|
+
# * file:line_number
|
14
|
+
# * ruby expression.
|
15
|
+
#
|
16
|
+
# This gives you the added benefit of easily removing your pd statements later on from the code.
|
17
|
+
#
|
18
|
+
# Happy puts_debuggerering!
|
19
|
+
#
|
20
|
+
# Example Code:
|
21
|
+
#
|
22
|
+
# # /Users/User/finance_calculator_app/pd_test.rb # line 1
|
23
|
+
# bug = 'beattle' # line 2
|
24
|
+
# pd "Show me the source of the bug: #{bug}" # line 3
|
25
|
+
# pd 'What line number am I?' # line 4
|
26
|
+
#
|
27
|
+
# Example Printout:
|
28
|
+
#
|
29
|
+
# [PD] /Users/User/finance_calculator_app/pd_test.rb:3
|
30
|
+
# > pd "Show me the source of the bug: #{bug}"
|
31
|
+
# => "Show me the source of the bug: beattle"
|
32
|
+
# [PD] /Users/User/finance_calculator_app/pd_test.rb:4 "What line number am I?"
|
33
|
+
def pd(*objects)
|
34
|
+
options = PutsDebuggerer.determine_options(objects) || {}
|
35
|
+
object = PutsDebuggerer.determine_object(objects)
|
36
|
+
run_at = PutsDebuggerer.determine_run_at(options)
|
37
|
+
printer = PutsDebuggerer.determine_printer(options)
|
38
|
+
pd_inspect = options.delete(:pd_inspect)
|
39
|
+
logger_formatter_decorated = PutsDebuggerer.printer.is_a?(Logger) && PutsDebuggerer.printer.formatter != PutsDebuggerer.logger_original_formatter
|
40
|
+
logging_layouts_decorated = PutsDebuggerer.printer.is_a?(Logging::Logger) && PutsDebuggerer.printer.appenders.map(&:layout) != (PutsDebuggerer.logging_original_layouts.values)
|
41
|
+
|
42
|
+
string = nil
|
43
|
+
if PutsDebuggerer::RunDeterminer.run_pd?(object, run_at)
|
44
|
+
__with_pd_options__(options) do |print_engine_options|
|
45
|
+
run_number = PutsDebuggerer::RunDeterminer.run_number(object, run_at)
|
46
|
+
formatter_pd_data = __build_pd_data__(object, print_engine_options: print_engine_options, source_line_count: PutsDebuggerer.source_line_count, run_number: run_number, pd_inspect: pd_inspect, logger_formatter_decorated: logger_formatter_decorated, logging_layouts_decorated: logging_layouts_decorated)
|
47
|
+
stdout = $stdout
|
48
|
+
$stdout = sio = StringIO.new
|
49
|
+
PutsDebuggerer.formatter.call(formatter_pd_data)
|
50
|
+
$stdout = stdout
|
51
|
+
string = sio.string
|
52
|
+
if RUBY_ENGINE == 'opal' && object.is_a?(Exception)
|
53
|
+
$stderr.puts(string)
|
54
|
+
else
|
55
|
+
if PutsDebuggerer.printer.is_a?(Proc)
|
56
|
+
PutsDebuggerer.printer.call(string)
|
57
|
+
elsif PutsDebuggerer.printer.is_a?(Logger)
|
58
|
+
logger_formatter = PutsDebuggerer.printer.formatter
|
59
|
+
begin
|
60
|
+
PutsDebuggerer.printer.formatter = PutsDebuggerer.logger_original_formatter
|
61
|
+
PutsDebuggerer.printer.debug(string)
|
62
|
+
ensure
|
63
|
+
PutsDebuggerer.printer.formatter = logger_formatter
|
64
|
+
end
|
65
|
+
elsif PutsDebuggerer.printer.is_a?(Logging::Logger)
|
66
|
+
logging_layouts = PutsDebuggerer.printer.appenders.reduce({}) do |hash, appender|
|
67
|
+
hash.merge(appender => appender.layout)
|
68
|
+
end
|
69
|
+
begin
|
70
|
+
PutsDebuggerer.logging_original_layouts.each do |appender, original_layout|
|
71
|
+
appender.layout = original_layout
|
72
|
+
end
|
73
|
+
PutsDebuggerer.printer.debug(string)
|
74
|
+
ensure
|
75
|
+
PutsDebuggerer.logging_original_layouts.each do |appender, original_layout|
|
76
|
+
appender.layout = logging_layouts[appender]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
elsif PutsDebuggerer.printer != false
|
80
|
+
send(PutsDebuggerer.send(:printer), string)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
printer ? object : string
|
87
|
+
end
|
88
|
+
|
89
|
+
# Implement caller backtrace method in Opal since it returns an empty array in Opal v1
|
90
|
+
if RUBY_ENGINE == 'opal'
|
91
|
+
def caller
|
92
|
+
begin
|
93
|
+
raise 'error'
|
94
|
+
rescue => e
|
95
|
+
e.backtrace[2..-1]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def pd_inspect
|
101
|
+
pd self, printer: false, pd_inspect: true
|
102
|
+
end
|
103
|
+
alias pdi pd_inspect
|
104
|
+
|
105
|
+
# Provides caller line number starting 1 level above caller of
|
106
|
+
# this method.
|
107
|
+
#
|
108
|
+
# Example:
|
109
|
+
#
|
110
|
+
# # lib/example.rb # line 1
|
111
|
+
# puts "Print out __caller_line_number__" # line 2
|
112
|
+
# puts __caller_line_number__ # line 3
|
113
|
+
#
|
114
|
+
# prints out `3`
|
115
|
+
def __caller_line_number__(caller_depth=0)
|
116
|
+
return if RUBY_ENGINE == 'opal'
|
117
|
+
caller[caller_depth] && caller[caller_depth][PutsDebuggerer::STACK_TRACE_CALL_LINE_NUMBER_REGEX, 1].to_i
|
118
|
+
end
|
119
|
+
|
120
|
+
# Provides caller file starting 1 level above caller of
|
121
|
+
# this method.
|
122
|
+
#
|
123
|
+
# Example:
|
124
|
+
#
|
125
|
+
# # File Name: lib/example.rb
|
126
|
+
# puts __caller_file__
|
127
|
+
#
|
128
|
+
# prints out `lib/example.rb`
|
129
|
+
def __caller_file__(caller_depth=0)
|
130
|
+
regex = RUBY_ENGINE == 'opal' ? PutsDebuggerer::STACK_TRACE_CALL_SOURCE_FILE_REGEX_OPAL : PutsDebuggerer::STACK_TRACE_CALL_SOURCE_FILE_REGEX
|
131
|
+
caller[caller_depth] && caller[caller_depth][regex, 1]
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# Provides caller source line starting 1 level above caller of
|
136
|
+
# this method.
|
137
|
+
#
|
138
|
+
# Example:
|
139
|
+
#
|
140
|
+
# puts __caller_source_line__
|
141
|
+
#
|
142
|
+
# prints out `puts __caller_source_line__`
|
143
|
+
def __caller_source_line__(caller_depth=0, source_line_count=nil, source_file=nil, source_line_number=nil)
|
144
|
+
source_line_number ||= __caller_line_number__(caller_depth+1)
|
145
|
+
source_file ||= __caller_file__(caller_depth+1)
|
146
|
+
source_line = ''
|
147
|
+
if defined?(Pry)
|
148
|
+
@pry_instance ||= Pry.new
|
149
|
+
source_line = Pry::Command::Hist.new(pry_instance: @pry_instance).call.instance_variable_get(:@buffer).split("\n")[source_line_number - 1] # TODO handle multi-lines in source_line_count
|
150
|
+
elsif defined?(IRB)
|
151
|
+
source_line = TOPLEVEL_BINDING.receiver.conf.io.line(source_line_number) # TODO handle multi-lines in source_line_count
|
152
|
+
else
|
153
|
+
source_line = PutsDebuggerer::SourceFile.new(source_file).source(source_line_count, source_line_number)
|
154
|
+
end
|
155
|
+
source_line
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def __with_pd_options__(options=nil)
|
161
|
+
options ||= {}
|
162
|
+
permanent_options = PutsDebuggerer.options
|
163
|
+
PutsDebuggerer.options = options.select {|option, _| PutsDebuggerer.options.keys.include?(option)}
|
164
|
+
print_engine_options = options.delete_if {|option, _| PutsDebuggerer.options.keys.include?(option)}
|
165
|
+
yield print_engine_options
|
166
|
+
PutsDebuggerer.options = permanent_options
|
167
|
+
end
|
168
|
+
|
169
|
+
def __build_pd_data__(object, print_engine_options:nil, source_line_count:nil, run_number:nil, pd_inspect:false, logger_formatter_decorated:false, logging_layouts_decorated:false)
|
170
|
+
depth = RUBY_ENGINE == 'opal' ? PutsDebuggerer::CALLER_DEPTH_ZERO_OPAL : PutsDebuggerer::CALLER_DEPTH_ZERO
|
171
|
+
if pd_inspect
|
172
|
+
depth += 1
|
173
|
+
depth += 4 if logger_formatter_decorated
|
174
|
+
depth += 8 if logging_layouts_decorated
|
175
|
+
end
|
176
|
+
|
177
|
+
pd_data = {
|
178
|
+
announcer: PutsDebuggerer.announcer,
|
179
|
+
file: __caller_file__(depth)&.sub(PutsDebuggerer.app_path.to_s, ''),
|
180
|
+
line_number: __caller_line_number__(depth),
|
181
|
+
pd_expression: __caller_pd_expression__(depth, source_line_count),
|
182
|
+
run_number: run_number,
|
183
|
+
object: object,
|
184
|
+
object_printer: PutsDebuggerer::OBJECT_PRINTER_DEFAULT.call(object, print_engine_options, source_line_count, run_number)
|
185
|
+
}
|
186
|
+
pd_data[:caller] = __caller_caller__(depth)
|
187
|
+
['header', 'wrapper', 'footer'].each do |boundary_option|
|
188
|
+
pd_data[boundary_option.to_sym] = PutsDebuggerer.send(boundary_option) if PutsDebuggerer.send("#{boundary_option}?")
|
189
|
+
end
|
190
|
+
pd_data
|
191
|
+
end
|
192
|
+
|
193
|
+
# Returns the caller stack trace of the caller of pd
|
194
|
+
def __caller_caller__(depth)
|
195
|
+
return unless PutsDebuggerer.caller?
|
196
|
+
start_depth = depth.to_i + 1
|
197
|
+
caller_depth = PutsDebuggerer.caller == -1 ? -1 : (start_depth + PutsDebuggerer.caller)
|
198
|
+
caller[start_depth..caller_depth].to_a
|
199
|
+
end
|
200
|
+
|
201
|
+
def __format_pd_expression__(expression, object)
|
202
|
+
"\n > #{expression}\n =>"
|
203
|
+
end
|
204
|
+
|
205
|
+
def __caller_pd_expression__(depth=0, source_line_count=nil)
|
206
|
+
# Caller Source Line Depth 2 = 1 to pd method + 1 to caller
|
207
|
+
source_line = __caller_source_line__(depth+1, source_line_count)
|
208
|
+
source_line = __extract_pd_expression__(source_line)
|
209
|
+
source_line = source_line.gsub(/(^'|'$)/, '"') if source_line.start_with?("'") && source_line.end_with?("'")
|
210
|
+
source_line = source_line.gsub(/(^\(|\)$)/, '') if source_line.start_with?("(") && source_line.end_with?(")")
|
211
|
+
source_line
|
212
|
+
end
|
213
|
+
|
214
|
+
# Extracts pd source line expression.
|
215
|
+
#
|
216
|
+
# Example:
|
217
|
+
#
|
218
|
+
# __extract_pd_expression__("pd (x=1)")
|
219
|
+
#
|
220
|
+
# outputs `(x=1)`
|
221
|
+
def __extract_pd_expression__(source_line)
|
222
|
+
source_line.to_s.strip
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module PutsDebuggerer
|
2
|
+
module RunDeterminer
|
3
|
+
OBJECT_RUN_AT = {}
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_reader :run_at_global_number
|
7
|
+
|
8
|
+
def run_at_global_number=(value)
|
9
|
+
@run_at_global_number = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def init_run_at_global_number
|
13
|
+
@run_at_global_number = 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def increment_run_at_global_number
|
17
|
+
@run_at_global_number += 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_run_at_global_number
|
21
|
+
@run_at_global_number = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_at_number(object, run_at)
|
25
|
+
OBJECT_RUN_AT[[object,run_at]]
|
26
|
+
end
|
27
|
+
|
28
|
+
def init_run_at_number(object, run_at)
|
29
|
+
OBJECT_RUN_AT[[object,run_at]] = 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def increment_run_at_number(object, run_at)
|
33
|
+
OBJECT_RUN_AT[[object,run_at]] += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def reset_run_at_number(object, run_at)
|
37
|
+
OBJECT_RUN_AT.delete([object, run_at])
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset_run_at_numbers
|
41
|
+
OBJECT_RUN_AT.clear
|
42
|
+
end
|
43
|
+
|
44
|
+
def run_number(object, run_at)
|
45
|
+
run_at_global_number || run_at_number(object, run_at)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_pd?(object, run_at)
|
49
|
+
run_pd = false
|
50
|
+
if run_at.nil?
|
51
|
+
run_pd = true
|
52
|
+
else
|
53
|
+
run_number = determine_run_number(object, run_at)
|
54
|
+
run_pd = determine_run_pd(run_at, run_number)
|
55
|
+
end
|
56
|
+
run_pd
|
57
|
+
end
|
58
|
+
|
59
|
+
def determine_run_number(object, run_at)
|
60
|
+
if PutsDebuggerer.run_at? # check if global option is set
|
61
|
+
determine_global_run_number
|
62
|
+
else
|
63
|
+
determine_local_run_number(object, run_at)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def determine_global_run_number
|
68
|
+
if PutsDebuggerer::RunDeterminer.run_at_global_number.nil?
|
69
|
+
PutsDebuggerer::RunDeterminer.init_run_at_global_number
|
70
|
+
else
|
71
|
+
PutsDebuggerer::RunDeterminer.increment_run_at_global_number
|
72
|
+
end
|
73
|
+
PutsDebuggerer::RunDeterminer.run_at_global_number
|
74
|
+
end
|
75
|
+
|
76
|
+
def determine_local_run_number(object, run_at)
|
77
|
+
if PutsDebuggerer::RunDeterminer.run_at_number(object, run_at).nil?
|
78
|
+
PutsDebuggerer::RunDeterminer.init_run_at_number(object, run_at)
|
79
|
+
else
|
80
|
+
PutsDebuggerer::RunDeterminer.increment_run_at_number(object, run_at)
|
81
|
+
end
|
82
|
+
PutsDebuggerer::RunDeterminer.run_at_number(object, run_at)
|
83
|
+
end
|
84
|
+
|
85
|
+
def determine_run_pd(run_at, run_number)
|
86
|
+
if run_at.is_a?(Integer)
|
87
|
+
determine_run_pd_integer(run_at, run_number)
|
88
|
+
elsif run_at.is_a?(Array)
|
89
|
+
determine_run_pd_array(run_at, run_number)
|
90
|
+
elsif run_at.is_a?(Range)
|
91
|
+
determine_run_pd_range(run_at, run_number)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def determine_run_pd_integer(run_at, run_number)
|
96
|
+
run_pd = true if run_at == run_number
|
97
|
+
end
|
98
|
+
|
99
|
+
def determine_run_pd_array(run_at, run_number)
|
100
|
+
run_pd = true if run_at.include?(run_number)
|
101
|
+
end
|
102
|
+
|
103
|
+
def determine_run_pd_range(run_at, run_number)
|
104
|
+
run_pd = true if run_at.cover?(run_number) || (run_at.end == -1 && run_number >= run_at.begin)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PutsDebuggerer
|
2
|
+
class SourceFile
|
3
|
+
def initialize(file_path)
|
4
|
+
@file = File.new(file_path) if file_path
|
5
|
+
end
|
6
|
+
|
7
|
+
def source(source_line_count, source_line_number)
|
8
|
+
@source = ''
|
9
|
+
return @source if RUBY_ENGINE == 'opal'
|
10
|
+
# For Opal Ruby compatibility, skip source lines if file does not respond to readline (as in Opal)
|
11
|
+
lines = source_lines(source_line_count, source_line_number)
|
12
|
+
@source = lines.join(' '*5) if @file.respond_to?(:readline)
|
13
|
+
@source
|
14
|
+
end
|
15
|
+
|
16
|
+
def source_lines(source_line_count, source_line_number)
|
17
|
+
lines = []
|
18
|
+
begin
|
19
|
+
while @file.lineno < source_line_number + source_line_count
|
20
|
+
file_line_number = @file.lineno + 1
|
21
|
+
file_line = @file.readline
|
22
|
+
if file_line_number >= source_line_number && file_line_number < source_line_number + source_line_count
|
23
|
+
lines << file_line
|
24
|
+
end
|
25
|
+
end
|
26
|
+
rescue EOFError
|
27
|
+
# Done
|
28
|
+
end
|
29
|
+
lines
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puts_debuggerer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_print
|
@@ -150,6 +150,34 @@ dependencies:
|
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 0.3.4
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: logging
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 2.3.0
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 2.3.0
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rake-tui
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
153
181
|
description: |
|
154
182
|
Debuggers are great! They help us troubleshoot complicated programming problems by inspecting values produced by code, line by line. They are invaluable when trying to understand what is going on in a large application composed of thousands or millions of lines of code.
|
155
183
|
In day-to-day test-driven development and simple debugging though, a puts statement can be a lot quicker in revealing what is going on than halting execution completely just to inspect a single value or a few. This is certainly true when writing the simplest possible code that could possibly work, and running a test every few seconds or minutes. Problem is you need to locate puts statements in large output logs, know which methods were invoked, find out what variable names are being printed, and see nicely formatted output. Enter puts_debuggerer. A guilt-free puts debugging Ruby gem FTW that prints file names, line numbers, code statements, and formats output nicely courtesy of awesome_print.
|
@@ -158,13 +186,21 @@ email: andy.am@gmail.com
|
|
158
186
|
executables: []
|
159
187
|
extensions: []
|
160
188
|
extra_rdoc_files:
|
189
|
+
- CHANGELOG.md
|
161
190
|
- LICENSE.txt
|
162
191
|
- README.md
|
163
192
|
files:
|
193
|
+
- CHANGELOG.md
|
164
194
|
- LICENSE.txt
|
165
195
|
- README.md
|
166
196
|
- VERSION
|
197
|
+
- lib/pd.rb
|
167
198
|
- lib/puts_debuggerer.rb
|
199
|
+
- lib/puts_debuggerer/core_ext/kernel.rb
|
200
|
+
- lib/puts_debuggerer/core_ext/logger.rb
|
201
|
+
- lib/puts_debuggerer/core_ext/logging/logger.rb
|
202
|
+
- lib/puts_debuggerer/run_determiner.rb
|
203
|
+
- lib/puts_debuggerer/source_file.rb
|
168
204
|
homepage: http://github.com/AndyObtiva/puts_debuggerer
|
169
205
|
licenses:
|
170
206
|
- MIT
|
@@ -184,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
220
|
- !ruby/object:Gem::Version
|
185
221
|
version: '0'
|
186
222
|
requirements: []
|
187
|
-
rubygems_version: 3.1.
|
223
|
+
rubygems_version: 3.1.4
|
188
224
|
signing_key:
|
189
225
|
specification_version: 4
|
190
226
|
summary: Ruby library for improved puts debugging, automatically displaying bonus
|