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.
@@ -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,3 @@
1
+ # in case 'logger' is not required
2
+ class Logger
3
+ end
@@ -0,0 +1,5 @@
1
+ # in case 'logging' is not required
2
+ module Logging
3
+ class Logger
4
+ end
5
+ 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.8.2
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-06-28 00:00:00.000000000 Z
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.2
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