puts_debuggerer 0.8.1 → 0.10.2

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,217 @@
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 PutsDebuggerer.printer.is_a?(Proc)
53
+ PutsDebuggerer.printer.call(string)
54
+ elsif PutsDebuggerer.printer.is_a?(Logger)
55
+ logger_formatter = PutsDebuggerer.printer.formatter
56
+ begin
57
+ PutsDebuggerer.printer.formatter = PutsDebuggerer.logger_original_formatter
58
+ PutsDebuggerer.printer.debug(string)
59
+ ensure
60
+ PutsDebuggerer.printer.formatter = logger_formatter
61
+ end
62
+ elsif PutsDebuggerer.printer.is_a?(Logging::Logger)
63
+ logging_layouts = PutsDebuggerer.printer.appenders.reduce({}) do |hash, appender|
64
+ hash.merge(appender => appender.layout)
65
+ end
66
+ begin
67
+ PutsDebuggerer.logging_original_layouts.each do |appender, original_layout|
68
+ appender.layout = original_layout
69
+ end
70
+ PutsDebuggerer.printer.debug(string)
71
+ ensure
72
+ PutsDebuggerer.logging_original_layouts.each do |appender, original_layout|
73
+ appender.layout = logging_layouts[appender]
74
+ end
75
+ end
76
+ elsif PutsDebuggerer.printer != false
77
+ send(PutsDebuggerer.send(:printer), string)
78
+ end
79
+ end
80
+ end
81
+
82
+ printer ? object : string
83
+ end
84
+
85
+ # Implement caller backtrace method in Opal since it returns an empty array in Opal v1
86
+ if RUBY_PLATFORM == 'opal'
87
+ def caller
88
+ begin
89
+ raise 'error'
90
+ rescue => e
91
+ e.backtrace[2..-1]
92
+ end
93
+ end
94
+ end
95
+
96
+ def pd_inspect
97
+ pd self, printer: false, pd_inspect: true
98
+ end
99
+ alias pdi pd_inspect
100
+
101
+ # Provides caller line number starting 1 level above caller of
102
+ # this method.
103
+ #
104
+ # Example:
105
+ #
106
+ # # lib/example.rb # line 1
107
+ # puts "Print out __caller_line_number__" # line 2
108
+ # puts __caller_line_number__ # line 3
109
+ #
110
+ # prints out `3`
111
+ def __caller_line_number__(caller_depth=0)
112
+ return if RUBY_PLATFORM == 'opal'
113
+ caller[caller_depth] && caller[caller_depth][PutsDebuggerer::STACK_TRACE_CALL_LINE_NUMBER_REGEX, 1].to_i
114
+ end
115
+
116
+ # Provides caller file starting 1 level above caller of
117
+ # this method.
118
+ #
119
+ # Example:
120
+ #
121
+ # # File Name: lib/example.rb
122
+ # puts __caller_file__
123
+ #
124
+ # prints out `lib/example.rb`
125
+ def __caller_file__(caller_depth=0)
126
+ regex = RUBY_PLATFORM == 'opal' ? PutsDebuggerer::STACK_TRACE_CALL_SOURCE_FILE_REGEX_OPAL : PutsDebuggerer::STACK_TRACE_CALL_SOURCE_FILE_REGEX
127
+ caller[caller_depth] && caller[caller_depth][regex, 1]
128
+ end
129
+
130
+
131
+ # Provides caller source line starting 1 level above caller of
132
+ # this method.
133
+ #
134
+ # Example:
135
+ #
136
+ # puts __caller_source_line__
137
+ #
138
+ # prints out `puts __caller_source_line__`
139
+ def __caller_source_line__(caller_depth=0, source_line_count=nil, source_file=nil, source_line_number=nil)
140
+ source_line_number ||= __caller_line_number__(caller_depth+1)
141
+ source_file ||= __caller_file__(caller_depth+1)
142
+ source_line = ''
143
+ if source_file == '(irb)'
144
+ source_line = conf.io.line(source_line_number) # TODO handle multi-lines in source_line_count
145
+ else
146
+ source_line = PutsDebuggerer::SourceFile.new(source_file).source(source_line_count, source_line_number)
147
+ end
148
+ source_line
149
+ end
150
+
151
+ private
152
+
153
+ def __with_pd_options__(options=nil)
154
+ options ||= {}
155
+ permanent_options = PutsDebuggerer.options
156
+ PutsDebuggerer.options = options.select {|option, _| PutsDebuggerer.options.keys.include?(option)}
157
+ print_engine_options = options.delete_if {|option, _| PutsDebuggerer.options.keys.include?(option)}
158
+ yield print_engine_options
159
+ PutsDebuggerer.options = permanent_options
160
+ end
161
+
162
+ 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)
163
+ depth = RUBY_PLATFORM == 'opal' ? PutsDebuggerer::CALLER_DEPTH_ZERO_OPAL : PutsDebuggerer::CALLER_DEPTH_ZERO
164
+ if pd_inspect
165
+ depth += 1
166
+ depth += 4 if logger_formatter_decorated
167
+ depth += 8 if logging_layouts_decorated
168
+ end
169
+
170
+ pd_data = {
171
+ announcer: PutsDebuggerer.announcer,
172
+ file: __caller_file__(depth)&.sub(PutsDebuggerer.app_path.to_s, ''),
173
+ line_number: __caller_line_number__(depth),
174
+ pd_expression: __caller_pd_expression__(depth, source_line_count),
175
+ run_number: run_number,
176
+ object: object,
177
+ object_printer: PutsDebuggerer::OBJECT_PRINTER_DEFAULT.call(object, print_engine_options, source_line_count, run_number)
178
+ }
179
+ pd_data[:caller] = __caller_caller__(depth)
180
+ ['header', 'wrapper', 'footer'].each do |boundary_option|
181
+ pd_data[boundary_option.to_sym] = PutsDebuggerer.send(boundary_option) if PutsDebuggerer.send("#{boundary_option}?")
182
+ end
183
+ pd_data
184
+ end
185
+
186
+ # Returns the caller stack trace of the caller of pd
187
+ def __caller_caller__(depth)
188
+ return unless PutsDebuggerer.caller?
189
+ start_depth = depth.to_i + 1
190
+ caller_depth = PutsDebuggerer.caller == -1 ? -1 : (start_depth + PutsDebuggerer.caller)
191
+ caller[start_depth..caller_depth].to_a
192
+ end
193
+
194
+ def __format_pd_expression__(expression, object)
195
+ "\n > #{expression}\n =>"
196
+ end
197
+
198
+ def __caller_pd_expression__(depth=0, source_line_count=nil)
199
+ # Caller Source Line Depth 2 = 1 to pd method + 1 to caller
200
+ source_line = __caller_source_line__(depth+1, source_line_count)
201
+ source_line = __extract_pd_expression__(source_line)
202
+ source_line = source_line.gsub(/(^'|'$)/, '"') if source_line.start_with?("'") && source_line.end_with?("'")
203
+ source_line = source_line.gsub(/(^\(|\)$)/, '') if source_line.start_with?("(") && source_line.end_with?(")")
204
+ source_line
205
+ end
206
+
207
+ # Extracts pd source line expression.
208
+ #
209
+ # Example:
210
+ #
211
+ # __extract_pd_expression__("pd (x=1)")
212
+ #
213
+ # outputs `(x=1)`
214
+ def __extract_pd_expression__(source_line)
215
+ source_line.to_s.strip
216
+ end
217
+ 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_PLATFORM == '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.1
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-30 00:00:00.000000000 Z
11
+ date: 2020-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -67,86 +67,145 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.12'
69
69
  - !ruby/object:Gem::Dependency
70
- name: bundler
70
+ name: jeweler
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1.0'
75
+ version: 2.3.9
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.0'
82
+ version: 2.3.9
83
83
  - !ruby/object:Gem::Dependency
84
- name: jeweler
84
+ name: bundler
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 2.3.0
89
+ version: 2.1.4
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 2.3.0
96
+ version: 2.1.4
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: coveralls
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - '='
102
102
  - !ruby/object:Gem::Version
103
- version: 0.8.5
103
+ version: 0.8.23
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '='
109
109
  - !ruby/object:Gem::Version
110
- version: 0.8.5
110
+ version: 0.8.23
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: simplecov
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.10.0
117
+ version: 0.16.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.16.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov-lcov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.7.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.7.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: undercover
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 0.3.4
118
146
  type: :development
119
147
  prerelease: false
120
148
  version_requirements: !ruby/object:Gem::Requirement
121
149
  requirements:
122
150
  - - "~>"
123
151
  - !ruby/object:Gem::Version
124
- version: 0.10.0
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'
125
181
  description: |
126
- Yes, many of us avoid debuggers like the plague and clamp on to our puts statements like an umbrella in a stormy day. Why not make it official and have puts debugging become its own perfectly legitimate thing?!!
127
-
128
- Enter puts_debuggerer. A guilt-free puts debugger Ruby gem FTW!
129
-
130
- In other words, puts_debuggerer is a Ruby library for improved puts debugging, automatically displaying bonus useful information such as source line number and source code.
131
-
132
- Partially inspired (only partially ;) by this blog post:
133
- https://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html
134
- (Credit to Tenderlove.)
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.
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.
184
+ Partially inspired by this blog post: https://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html (Credit to Tenderlove.)
135
185
  email: andy.am@gmail.com
136
186
  executables: []
137
187
  extensions: []
138
188
  extra_rdoc_files:
189
+ - CHANGELOG.md
139
190
  - LICENSE.txt
140
191
  - README.md
141
192
  files:
193
+ - CHANGELOG.md
142
194
  - LICENSE.txt
143
195
  - README.md
196
+ - VERSION
197
+ - lib/pd.rb
144
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
145
204
  homepage: http://github.com/AndyObtiva/puts_debuggerer
146
205
  licenses:
147
206
  - MIT
148
207
  metadata: {}
149
- post_install_message:
208
+ post_install_message:
150
209
  rdoc_options: []
151
210
  require_paths:
152
211
  - lib
@@ -161,9 +220,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
220
  - !ruby/object:Gem::Version
162
221
  version: '0'
163
222
  requirements: []
164
- rubyforge_project:
165
- rubygems_version: 2.6.10
166
- signing_key:
223
+ rubygems_version: 3.1.4
224
+ signing_key:
167
225
  specification_version: 4
168
226
  summary: Ruby library for improved puts debugging, automatically displaying bonus
169
227
  useful information such as source line number and source code.