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.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.10.2
@@ -0,0 +1 @@
1
+ require 'puts_debuggerer'
@@ -1,29 +1,64 @@
1
- require 'ripper'
2
- require 'awesome_print'
1
+ require 'puts_debuggerer/core_ext/kernel'
2
+ require 'puts_debuggerer/core_ext/logger'
3
+ require 'puts_debuggerer/core_ext/logging/logger'
4
+ require 'puts_debuggerer/run_determiner'
5
+ require 'puts_debuggerer/source_file'
3
6
 
4
7
  module PutsDebuggerer
8
+ SOURCE_LINE_COUNT_DEFAULT = 1
5
9
  HEADER_DEFAULT = '*'*80
10
+ WRAPPER_DEFAULT = '*'*80
6
11
  FOOTER_DEFAULT = '*'*80
12
+ LOGGER_FORMATTER_DECORATOR = proc { |original_formatter|
13
+ proc { |severity, datetime, progname, msg|
14
+ original_formatter.call(severity, datetime, progname, msg.pd_inspect)
15
+ }
16
+ }
17
+ LOGGING_LAYOUT_DECORATOR = proc {|original_layout|
18
+ original_layout.clone.tap do |layout|
19
+ layout.singleton_class.class_eval do
20
+ alias original_format_obj format_obj
21
+ def format_obj(obj)
22
+ obj.pdi # alias to pd_inspect
23
+ end
24
+ end
25
+ end
26
+ }
27
+ RETURN_DEFAULT = true
28
+ OBJECT_PRINTER_DEFAULT = lambda do |object, print_engine_options=nil, source_line_count=nil, run_number=nil|
29
+ lambda do
30
+ if object.is_a?(Exception) && object.respond_to?(:full_message)
31
+ puts object.full_message
32
+ elsif PutsDebuggerer.print_engine.is_a?(Proc)
33
+ PutsDebuggerer.print_engine.call(object)
34
+ else
35
+ send(PutsDebuggerer.print_engine, object)
36
+ end
37
+ end
38
+ end
7
39
  PRINTER_DEFAULT = :puts
8
40
  PRINTER_RAILS = lambda do |output|
9
41
  puts output if Rails.env.test?
10
42
  Rails.logger.debug(output)
11
43
  end
12
44
  PRINT_ENGINE_DEFAULT = :ap
13
- PRINTER_MESSAGE_INVALID = 'printer must be a valid global method symbol (e.g. :puts) or lambda/proc receiving a text arg'
45
+ PRINTER_MESSAGE_INVALID = 'printer must be a valid global method symbol (e.g. :puts), a logger, or a lambda/proc receiving a text arg'
14
46
  PRINT_ENGINE_MESSAGE_INVALID = 'print_engine must be a valid global method symbol (e.g. :p, :ap or :pp) or lambda/proc receiving an object arg'
15
47
  ANNOUNCER_DEFAULT = '[PD]'
16
48
  FORMATTER_DEFAULT = -> (data) {
49
+ puts data[:wrapper] if data[:wrapper]
17
50
  puts data[:header] if data[:header]
18
- print "#{data[:announcer]} #{data[:file]}:#{data[:line_number]}#{__format_pd_expression__(data[:pd_expression], data[:object])} "
51
+ print "#{data[:announcer]} #{data[:file]}#{':' if data[:line_number]}#{data[:line_number]}#{" (run:#{data[:run_number]})" if data[:run_number]}#{__format_pd_expression__(data[:pd_expression], data[:object])} "
19
52
  data[:object_printer].call
20
53
  puts data[:caller].map {|l| ' ' + l} unless data[:caller].to_a.empty?
21
54
  puts data[:footer] if data[:footer]
55
+ puts data[:wrapper] if data[:wrapper]
22
56
  }
23
57
  CALLER_DEPTH_ZERO = 4 #depth includes pd + with_options method + nested block + build_pd_data method
24
- OBJECT_RUN_AT = {}
58
+ CALLER_DEPTH_ZERO_OPAL = -1 #depth includes pd + with_options method + nested block + build_pd_data method
25
59
  STACK_TRACE_CALL_LINE_NUMBER_REGEX = /\:(\d+)\:in /
26
60
  STACK_TRACE_CALL_SOURCE_FILE_REGEX = /[ ]*([^:]+)\:\d+\:in /
61
+ STACK_TRACE_CALL_SOURCE_FILE_REGEX_OPAL = /(http[^\)]+)/
27
62
 
28
63
  class << self
29
64
  # Application root path to exclude when printing out file path
@@ -45,6 +80,28 @@ module PutsDebuggerer
45
80
  @app_path = (path || Rails.root.to_s) rescue nil
46
81
  end
47
82
 
83
+ # Source Line Count.
84
+ # * Default value is `1`
85
+ #
86
+ # Example:
87
+ #
88
+ # PutsDebuggerer.source_line_count = 2
89
+ # pd (true ||
90
+ # false), source_line_count: 2
91
+ #
92
+ # Prints out:
93
+ #
94
+ # ********************************************************************************
95
+ # [PD] /Users/User/example.rb:2
96
+ # > pd (true ||
97
+ # false), source_line_count: 2
98
+ # => "true"
99
+ attr_reader :source_line_count
100
+
101
+ def source_line_count=(value)
102
+ @source_line_count = value || SOURCE_LINE_COUNT_DEFAULT
103
+ end
104
+
48
105
  # Header to include at the top of every print out.
49
106
  # * Default value is `nil`
50
107
  # * Value `true` enables header as `'*'*80`
@@ -64,19 +121,24 @@ module PutsDebuggerer
64
121
  # => "1"
65
122
  attr_reader :header
66
123
 
67
- def header=(value)
68
- if value.equal?(true)
69
- @header = HEADER_DEFAULT
70
- elsif value == ''
71
- @header = nil
72
- else
73
- @header = value
74
- end
75
- end
76
-
77
- def header?
78
- !!@header
79
- end
124
+ # Wrapper to include at the top and bottom of every print out (both header and footer).
125
+ # * Default value is `nil`
126
+ # * Value `true` enables wrapper as `'*'*80`
127
+ # * Value `false`, `nil`, or empty string disables wrapper
128
+ # * Any other string value gets set as a custom wrapper
129
+ #
130
+ # Example:
131
+ #
132
+ # PutsDebuggerer.wrapper = true
133
+ # pd (x=1)
134
+ #
135
+ # Prints out:
136
+ #
137
+ # [PD] /Users/User/example.rb:2
138
+ # > pd x=1
139
+ # => "1"
140
+ # ********************************************************************************
141
+ attr_reader :wrapper
80
142
 
81
143
  # Footer to include at the bottom of every print out.
82
144
  # * Default value is `nil`
@@ -96,24 +158,27 @@ module PutsDebuggerer
96
158
  # => "1"
97
159
  # ********************************************************************************
98
160
  attr_reader :footer
99
-
100
- def footer=(value)
101
- if value.equal?(true)
102
- @footer = FOOTER_DEFAULT
103
- elsif value == ''
104
- @footer = nil
105
- else
106
- @footer = value
161
+
162
+ ['header', 'footer', 'wrapper'].each do |boundary_option|
163
+ define_method("#{boundary_option}=") do |value|
164
+ if value.equal?(true)
165
+ instance_variable_set(:"@#{boundary_option}", const_get(:"#{boundary_option.upcase}_DEFAULT"))
166
+ elsif value == ''
167
+ instance_variable_set(:"@#{boundary_option}", nil)
168
+ else
169
+ instance_variable_set(:"@#{boundary_option}", value)
170
+ end
171
+ end
172
+
173
+ define_method("#{boundary_option}?") do
174
+ !!instance_variable_get(:"@#{boundary_option}")
107
175
  end
108
176
  end
109
177
 
110
- def footer?
111
- !!@footer
112
- end
113
-
114
- # Printer is a global method symbol or lambda expression to use in printing to the user.
115
- # Examples of global methods are `:puts` and `:print`.
178
+ # Printer is a global method symbol, lambda expression, or logger to use in printing to the user.
179
+ # Examples of a global method are `:puts` and `:print`.
116
180
  # An example of a lambda expression is `lambda {|output| Rails.logger.ap(output)}`
181
+ # Examples of a logger are a Ruby `Logger` instance or `Logging::Logger` instance
117
182
  #
118
183
  # Defaults to `:puts`
119
184
  # In Rails, it defaults to: `lambda {|output| Rails.logger.ap(output)}`
@@ -134,17 +199,37 @@ module PutsDebuggerer
134
199
 
135
200
  def printer=(printer)
136
201
  if printer.nil?
137
- if Object.const_defined?(:Rails)
138
- @printer = PRINTER_RAILS
139
- else
140
- @printer = PRINTER_DEFAULT
202
+ @printer = printer_default
203
+ elsif printer.is_a?(Logger)
204
+ @printer = printer
205
+ @logger_original_formatter = printer.formatter || Logger::Formatter.new
206
+ printer.formatter = LOGGER_FORMATTER_DECORATOR.call(@logger_original_formatter)
207
+ elsif printer.is_a?(Logging::Logger)
208
+ @printer = printer
209
+ @logging_original_layouts = printer.appenders.reduce({}) do |hash, appender|
210
+ hash.merge(appender => appender.layout)
211
+ end
212
+ printer.appenders.each do |appender|
213
+ appender.layout = LOGGING_LAYOUT_DECORATOR.call(appender.layout)
141
214
  end
142
- elsif printer.is_a?(Proc)
215
+ elsif printer == false || printer.is_a?(Proc) || printer.respond_to?(:log) # a logger
143
216
  @printer = printer
144
217
  else
145
218
  @printer = method(printer).name rescue raise(PRINTER_MESSAGE_INVALID)
146
219
  end
147
220
  end
221
+
222
+ def printer_default
223
+ Object.const_defined?(:Rails) ? PRINTER_RAILS : PRINTER_DEFAULT
224
+ end
225
+
226
+ # Logger original formatter before it was decorated with PutsDebuggerer::LOGGER_FORMATTER_DECORATOR
227
+ # upon setting the logger as a printer.
228
+ attr_reader :logger_original_formatter
229
+
230
+ # Logging library original layouts before being decorated with PutsDebuggerer::LOGGING_LAYOUT_DECORATOR
231
+ # upon setting the Logging library logger as a printer.
232
+ attr_reader :logging_original_layouts
148
233
 
149
234
  # Print engine is similar to `printer`, except it is focused on the scope of formatting
150
235
  # the data object being printed (excluding metadata such as file name, line number,
@@ -169,17 +254,25 @@ module PutsDebuggerer
169
254
  # > pd array
170
255
  # => [1, [2, 3]]
171
256
  # ]
172
- attr_reader :print_engine
257
+ def print_engine
258
+ if @print_engine.nil?
259
+ require 'awesome_print' if RUBY_PLATFORM != 'opal'
260
+ @print_engine = print_engine_default
261
+ end
262
+ @print_engine
263
+ end
173
264
 
174
265
  def print_engine=(engine)
175
- if engine.nil?
176
- @print_engine = PRINT_ENGINE_DEFAULT
177
- elsif engine.is_a?(Proc)
266
+ if engine.is_a?(Proc) || engine.nil?
178
267
  @print_engine = engine
179
268
  else
180
269
  @print_engine = method(engine).name rescue raise(PRINT_ENGINE_MESSAGE_INVALID)
181
270
  end
182
271
  end
272
+
273
+ def print_engine_default
274
+ Object.const_defined?(:AwesomePrint) ? PRINT_ENGINE_DEFAULT : :p
275
+ end
183
276
 
184
277
  # Announcer (e.g. [PD]) to announce every print out with (default: "[PD]")
185
278
  #
@@ -205,12 +298,14 @@ module PutsDebuggerer
205
298
  # * :announcer (string)
206
299
  # * :caller (array)
207
300
  # * :file (string)
301
+ # * :wrapper (string)
208
302
  # * :footer (string)
209
303
  # * :header (string)
210
304
  # * :line_number (string)
211
305
  # * :pd_expression (string)
212
306
  # * :object (object)
213
307
  # * :object_printer (proc)
308
+ # * :source_line_count (integer)
214
309
  #
215
310
  # NOTE: data for :object_printer is not a string, yet a proc that must
216
311
  # be called to output value. It is a proc as it automatically handles usage
@@ -288,9 +383,11 @@ module PutsDebuggerer
288
383
  def options
289
384
  {
290
385
  header: header,
386
+ wrapper: wrapper,
291
387
  footer: footer,
292
388
  printer: printer,
293
389
  print_engine: print_engine,
390
+ source_line_count: source_line_count,
294
391
  app_path: app_path,
295
392
  announcer: announcer,
296
393
  formatter: formatter,
@@ -367,44 +464,25 @@ module PutsDebuggerer
367
464
  !!@run_at
368
465
  end
369
466
 
370
- attr_reader :run_at_global_number
371
-
372
- def run_at_global_number=(value)
373
- @run_at_global_number = value
374
- end
375
-
376
- def init_run_at_global_number
377
- @run_at_global_number = 1
467
+ def determine_options(objects)
468
+ objects.delete_at(-1) if objects.size > 1 && objects.last.is_a?(Hash)
378
469
  end
379
470
 
380
- def increment_run_at_global_number
381
- @run_at_global_number += 1
471
+ def determine_object(objects)
472
+ objects.compact.size > 1 ? objects : objects.first
382
473
  end
383
474
 
384
- def reset_run_at_global_number
385
- @run_at_global_number = nil
475
+ def determine_run_at(options)
476
+ ((options && options[:run_at]) || PutsDebuggerer.run_at)
386
477
  end
387
478
 
388
- def run_at_number(object, run_at)
389
- PutsDebuggerer::OBJECT_RUN_AT[[object,run_at]]
390
- end
391
-
392
- def init_run_at_number(object, run_at)
393
- PutsDebuggerer::OBJECT_RUN_AT[[object,run_at]] = 1
394
- end
395
-
396
- def increment_run_at_number(object, run_at)
397
- PutsDebuggerer::OBJECT_RUN_AT[[object,run_at]] += 1
398
- end
399
-
400
- def reset_run_at_number(object, run_at)
401
- PutsDebuggerer::OBJECT_RUN_AT.delete([object, run_at])
402
- end
403
-
404
- def reset_run_at_numbers
405
- PutsDebuggerer::OBJECT_RUN_AT.clear
479
+ def determine_printer(options)
480
+ if options && options.has_key?(:printer)
481
+ options[:printer]
482
+ else
483
+ PutsDebuggerer.printer
484
+ end
406
485
  end
407
-
408
486
  end
409
487
  end
410
488
 
@@ -416,206 +494,4 @@ PutsDebuggerer.formatter = nil
416
494
  PutsDebuggerer.app_path = nil
417
495
  PutsDebuggerer.caller = nil
418
496
  PutsDebuggerer.run_at = nil
419
-
420
- # Prints object with bonus info such as file name, line number and source
421
- # expression. Optionally prints out header and footer.
422
- # Lookup PutsDebuggerer attributes for more details about configuration options.
423
- #
424
- # Simply invoke global `pd` method anywhere you'd like to see line number and source code with output.
425
- # If the argument is a pure string, the print out is simplified by not showing duplicate source.
426
- #
427
- # Quickly locate printed lines using Find feature (e.g. CTRL+F) by looking for:
428
- # * \[PD\]
429
- # * file:line_number
430
- # * ruby expression.
431
- #
432
- # This gives you the added benefit of easily removing your pd statements later on from the code.
433
- #
434
- # Happy puts_debuggerering!
435
- #
436
- # Example Code:
437
- #
438
- # # /Users/User/finance_calculator_app/pd_test.rb # line 1
439
- # bug = 'beattle' # line 2
440
- # pd "Show me the source of the bug: #{bug}" # line 3
441
- # pd 'What line number am I?' # line 4
442
- #
443
- # Example Printout:
444
- #
445
- # [PD] /Users/User/finance_calculator_app/pd_test.rb:3
446
- # > pd "Show me the source of the bug: #{bug}"
447
- # => "Show me the source of the bug: beattle"
448
- # [PD] /Users/User/finance_calculator_app/pd_test.rb:4 "What line number am I?"
449
- def pd(object, options=nil)
450
- run_at = ((options && options[:run_at]) || PutsDebuggerer.run_at)
451
-
452
- if __run_pd__(object, run_at)
453
- __with_pd_options__(options) do |print_engine_options|
454
- formatter_pd_data = __build_pd_data__(object, print_engine_options) #depth adds build method
455
- stdout = $stdout
456
- $stdout = sio = StringIO.new
457
- PutsDebuggerer.formatter.call(formatter_pd_data)
458
- $stdout = stdout
459
- if PutsDebuggerer.printer.is_a?(Proc)
460
- PutsDebuggerer.printer.call(sio.string)
461
- else
462
- send(PutsDebuggerer.send(:printer), sio.string)
463
- end
464
- end
465
- end
466
-
467
- object
468
- end
469
-
470
- def __run_pd__(object, run_at)
471
- run_pd = false
472
- if run_at.nil?
473
- run_pd = true
474
- else
475
- if PutsDebuggerer.run_at?
476
- if PutsDebuggerer.run_at_global_number.nil?
477
- PutsDebuggerer.init_run_at_global_number
478
- else
479
- PutsDebuggerer.increment_run_at_global_number
480
- end
481
- run_number = PutsDebuggerer.run_at_global_number
482
- else
483
- if PutsDebuggerer.run_at_number(object, run_at).nil?
484
- PutsDebuggerer.init_run_at_number(object, run_at)
485
- else
486
- PutsDebuggerer.increment_run_at_number(object, run_at)
487
- end
488
- run_number = PutsDebuggerer.run_at_number(object, run_at)
489
- end
490
- if run_at.is_a?(Integer)
491
- run_pd = true if run_at == run_number
492
- elsif run_at.is_a?(Array)
493
- run_pd = true if run_at.include?(run_number)
494
- elsif run_at.is_a?(Range)
495
- run_pd = true if run_at.cover?(run_number) || (run_at.end == -1 && run_number >= run_at.begin)
496
- end
497
- end
498
- run_pd
499
- end
500
-
501
- # Provides caller line number starting 1 level above caller of
502
- # this method.
503
- #
504
- # Example:
505
- #
506
- # # lib/example.rb # line 1
507
- # puts "Print out __caller_line_number__" # line 2
508
- # puts __caller_line_number__ # line 3
509
- #
510
- # prints out `3`
511
- def __caller_line_number__(caller_depth=0)
512
- caller[caller_depth][PutsDebuggerer::STACK_TRACE_CALL_LINE_NUMBER_REGEX, 1].to_i
513
- end
514
-
515
- # Provides caller file starting 1 level above caller of
516
- # this method.
517
- #
518
- # Example:
519
- #
520
- # # File Name: lib/example.rb
521
- # puts __caller_file__
522
- #
523
- # prints out `lib/example.rb`
524
- def __caller_file__(caller_depth=0)
525
- caller[caller_depth][PutsDebuggerer::STACK_TRACE_CALL_SOURCE_FILE_REGEX, 1]
526
- end
527
-
528
-
529
- # Provides caller source line starting 1 level above caller of
530
- # this method.
531
- #
532
- # Example:
533
- #
534
- # puts __caller_source_line__
535
- #
536
- # prints out `puts __caller_source_line__`
537
- def __caller_source_line__(caller_depth=0, source_file=nil, source_line_number=nil)
538
- source_line_number ||= __caller_line_number__(caller_depth+1)
539
- source_file ||= __caller_file__(caller_depth+1)
540
- source_line = nil
541
- if source_file == '(irb)'
542
- source_line = conf.io.line(source_line_number)
543
- else
544
- f = File.new(source_file)
545
- source_line = ''
546
- done = false
547
- f.each_line do |line|
548
- if !done && f.lineno == source_line_number
549
- source_line << line
550
- done = true if Ripper.sexp_raw(source_line) || source_line.include?('%>') #the last condition is for erb support (unofficial)
551
- source_line_number+=1
552
- end
553
- end
554
- end
555
- source_line
556
- end
557
-
558
- private
559
-
560
- def __with_pd_options__(options=nil)
561
- options ||= {}
562
- permanent_options = PutsDebuggerer.options
563
- PutsDebuggerer.options = options.select {|option, _| PutsDebuggerer.options.keys.include?(option)}
564
- print_engine_options = options.delete_if {|option, _| PutsDebuggerer.options.keys.include?(option)}
565
- yield print_engine_options
566
- PutsDebuggerer.options = permanent_options
567
- end
568
-
569
- def __build_pd_data__(object, print_engine_options=nil)
570
- depth = PutsDebuggerer::CALLER_DEPTH_ZERO
571
- pd_data = {
572
- announcer: PutsDebuggerer.announcer,
573
- file: __caller_file__(depth).sub(PutsDebuggerer.app_path.to_s, ''),
574
- line_number: __caller_line_number__(depth),
575
- pd_expression: __caller_pd_expression__(depth),
576
- object: object,
577
- object_printer: lambda do
578
- if PutsDebuggerer.print_engine.is_a?(Proc)
579
- PutsDebuggerer.print_engine.call(object)
580
- else
581
- if print_engine_options.to_h.empty?
582
- send(PutsDebuggerer.print_engine, object)
583
- else
584
- send(PutsDebuggerer.print_engine, object, print_engine_options) rescue send(PutsDebuggerer.print_engine, object)
585
- end
586
- end
587
- end
588
- }
589
- if PutsDebuggerer.caller?
590
- start_depth = depth.to_i
591
- caller_depth = PutsDebuggerer.caller == -1 ? -1 : (start_depth + PutsDebuggerer.caller)
592
- pd_data[:caller] = caller[start_depth..caller_depth].to_a
593
- end
594
- pd_data[:header] = PutsDebuggerer.header if PutsDebuggerer.header?
595
- pd_data[:footer] = PutsDebuggerer.footer if PutsDebuggerer.footer?
596
- pd_data
597
- end
598
-
599
- def __format_pd_expression__(expression, object)
600
- "\n > #{expression}\n =>"
601
- end
602
-
603
- def __caller_pd_expression__(depth=0)
604
- # Caller Source Line Depth 2 = 1 to pd method + 1 to caller
605
- source_line = __caller_source_line__(depth+1)
606
- source_line = __extract_pd_expression__(source_line)
607
- source_line = source_line.gsub(/(^'|'$)/, '"') if source_line.start_with?("'") && source_line.end_with?("'")
608
- source_line = source_line.gsub(/(^\(|\)$)/, '') if source_line.start_with?("(") && source_line.end_with?(")")
609
- source_line
610
- end
611
-
612
- # Extracts pd source line expression.
613
- #
614
- # Example:
615
- #
616
- # __extract_pd_expression__("pd (x=1)")
617
- #
618
- # outputs `(x=1)`
619
- def __extract_pd_expression__(source_line)
620
- source_line.strip
621
- end
497
+ PutsDebuggerer.source_line_count = nil