Module: PutsDebuggerer

Defined in:
lib/puts_debuggerer.rb,
lib/puts_debuggerer/source_file.rb,
lib/puts_debuggerer/run_determiner.rb

Defined Under Namespace

Modules: RunDeterminer Classes: SourceFile

Constant Summary collapse

SOURCE_LINE_COUNT_DEFAULT =
1
HEADER_DEFAULT =
'>'*80
WRAPPER_DEFAULT =
'*'*80
'<'*80
LOGGER_FORMATTER_DECORATOR =
proc { |original_formatter|
  proc { |severity, datetime, progname, msg|
    original_formatter.call(severity, datetime, progname, msg.pd_inspect)
  }
}
LOGGING_LAYOUT_DECORATOR =
proc {|original_layout|
  original_layout.clone.tap do |layout|
    layout.singleton_class.class_eval do
      alias original_format_obj format_obj
      def format_obj(obj)
        obj.pdi # alias to pd_inspect
      end
    end
  end
}
RETURN_DEFAULT =
true
OBJECT_PRINTER_DEFAULT =
lambda do |object, print_engine_options=nil, source_line_count=nil, run_number=nil|
  lambda do
    if object.is_a?(Exception)
      if RUBY_ENGINE == 'opal'
        object.backtrace.each { |line| puts line }
      else
        puts object.full_message
      end
    elsif PutsDebuggerer.print_engine.is_a?(Proc)
      PutsDebuggerer.print_engine.call(object)
    else
      send(PutsDebuggerer.print_engine, object)
    end
  end
end
PRINTER_DEFAULT =
:puts
PRINTER_RAILS =
lambda do |output|
  puts output if Rails.env.test?
  Rails.logger.debug(output)
end
:ap
PRINTER_MESSAGE_INVALID =
'printer must be a valid global method symbol (e.g. :puts), a logger, or a lambda/proc receiving a text arg'
'print_engine must be a valid global method symbol (e.g. :p, :ap or :pp) or lambda/proc receiving an object arg'
ANNOUNCER_DEFAULT =
'[PD]'
FORMATTER_DEFAULT =
-> (data) {
  puts data[:wrapper] if data[:wrapper]
  puts data[:header] if data[:header]
  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])} "
  data[:object_printer].call
  puts data[:caller].map {|l| '     ' + l} unless data[:caller].to_a.empty?
  puts data[:footer] if data[:footer]
  puts data[:wrapper] if data[:wrapper]
}
CALLER_DEPTH_ZERO =

depth includes pd + with_options method + nested block + build_pd_data method

4
CALLER_DEPTH_ZERO_OPAL =

depth includes pd + with_options method + nested block + build_pd_data method

-1 #depth includes pd + with_options method + nested block + build_pd_data method
STACK_TRACE_CALL_LINE_NUMBER_REGEX =
/\:(\d+)\:in /
STACK_TRACE_CALL_SOURCE_FILE_REGEX =
/[ ]*([^:]+)\:\d+\:in /
STACK_TRACE_CALL_SOURCE_FILE_REGEX_OPAL =
/(http[^\)]+)/
OPTIONS =
[:app_path, :source_line_count, :header, :h, :wrapper, :w, :footer, :f, :printer, :print_engine, :announcer, :formatter, :caller, :run_at]
OPTION_ALIASES =
{
  a: :announcer,
  c: :caller,
  h: :header,
  f: :footer,
  w: :wrapper,
}

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.announcerObject

Announcer (e.g. [PD]) to announce every print out with (default: “[PD]”)

Example:

PutsDebuggerer.announcer = "*** PD ***\n  "
pd (x=1)

Prints out:

*** PD ***
   /Users/User/example.rb:2
   > pd x=1
  => 1

302
303
304
# File 'lib/puts_debuggerer.rb', line 302

def announcer
  @announcer
end

.app_pathObject

Application root path to exclude when printing out file path

Example:

# File Name: /Users/User/sample_app/lib/sample.rb
PutsDebuggerer.app_path = '/Users/User/sample_app'
pd (x=1)

Prints out:

[PD] lib/sample.rb:3
   > pd x=1
  => "1"

89
90
91
# File 'lib/puts_debuggerer.rb', line 89

def app_path
  @app_path
end

.callerObject

Caller backtrace included at the end of every print out Passed an argument of true/false, nil, or depth as an integer.

  • true and -1 means include full caller backtrace

  • false and nil means do not include caller backtrace

  • depth (0-based) means include limited caller backtrace depth

Example:

# File Name: /Users/User/sample_app/lib/sample.rb
PutsDebuggerer.caller = 3
pd (x=1)

Prints out:

[PD] /Users/User/sample_app/lib/sample.rb:3
   > pd x=1
  => "1"
     /Users/User/sample_app/lib/master_samples.rb:368:in `block (3 levels) in <top (required)>'
     /Users/User/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0/irb/workspace.rb:87:in `eval'
     /Users/User/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0/irb/workspace.rb:87:in `evaluate'
     /Users/User/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0/irb/context.rb:381:in `evaluate'

379
380
381
# File 'lib/puts_debuggerer.rb', line 379

def caller
  @caller
end

Footer to include at the bottom of every print out.

  • Default value is `nil`

  • Value `true` enables footer as `'*'*80`

  • Value `false`, `nil`, or empty string disables footer

  • Any other string value gets set as a custom footer

Example:

PutsDebuggerer.footer = true
pd (x=1)

Prints out:

[PD] /Users/User/example.rb:2
   > pd x=1
  => "1"
********************************************************************************

172
173
174
# File 'lib/puts_debuggerer.rb', line 172

def footer
  @footer
end

.formatterObject

Formatter used in every print out Passed a data argument with the following keys:

  • :announcer (string)

  • :caller (array)

  • :file (string)

  • :wrapper (string)

  • :footer (string)

  • :header (string)

  • :line_number (string)

  • :pd_expression (string)

  • :object (object)

  • :object_printer (proc)

  • :source_line_count (integer)

NOTE: data for :object_printer is not a string, yet a proc that must be called to output value. It is a proc as it automatically handles usage of print_engine and encapsulates its details. In any case, data for :object is available should one want to avoid altogether.

Example:

PutsDebuggerer.formatter = -> (data) {
  puts "-<#{data[:announcer]}>-"
  puts "HEADER: #{data[:header]}"
  puts "FILE: #{data[:file]}"
  puts "LINE: #{data[:line_number]}"
  puts "EXPRESSION: #{data[:pd_expression]}"
  print "PRINT OUT: "
  data[:object_printer].call
  puts "CALLER: #{data[:caller].to_a.first}"
  puts "FOOTER: #{data[:footer]}"
}
pd (x=1)

Prints out:

-<[PD]>-
FILE: /Users/User/example.rb
HEADER: ********************************************************************************
LINE: 9
EXPRESSION: x=1
PRINT OUT: 1
CALLER: #/Users/User/master_examples.rb:83:in `block (3 levels) in <top (required)>'
FOOTER: ********************************************************************************

352
353
354
# File 'lib/puts_debuggerer.rb', line 352

def formatter
  @formatter
end

.headerObject (readonly)

Header to include at the top of every print out.

  • Default value is `nil`

  • Value `true` enables header as `'*'*80`

  • Value `false`, `nil`, or empty string disables header

  • Any other string value gets set as a custom header

Example:

PutsDebuggerer.header = true
pd (x=1)

Prints out:

********************************************************************************
[PD] /Users/User/example.rb:2
   > pd x=1
  => "1"

134
135
136
# File 'lib/puts_debuggerer.rb', line 134

def header
  @header
end

.logger_original_formatterObject (readonly)

Logger original formatter before it was decorated with PutsDebuggerer::LOGGER_FORMATTER_DECORATOR upon setting the logger as a printer.


240
241
242
# File 'lib/puts_debuggerer.rb', line 240

def logger_original_formatter
  @logger_original_formatter
end

.logging_original_layoutsObject (readonly)

Logging library original layouts before being decorated with PutsDebuggerer::LOGGING_LAYOUT_DECORATOR upon setting the Logging library logger as a printer.


244
245
246
# File 'lib/puts_debuggerer.rb', line 244

def logging_original_layouts
  @logging_original_layouts
end

.printerObject

Printer is a global method symbol, lambda expression, or logger to use in printing to the user. Examples of a global method are `:puts` and `:print`. An example of a lambda expression is `lambda {|output| Rails.logger.ap(output)}` Examples of a logger are a Ruby `Logger` instance or `Logging::Logger` instance

Defaults to `:puts` In Rails, it defaults to: `lambda {|output| Rails.logger.ap(output)}`

Example:

# File Name: /Users/User/example.rb PutsDebuggerer.printer = lambda {|output| Rails.logger.error(output)} str = “Hello” pd str

Prints out in the Rails app log as error lines:

PD

/Users/User/example.rb:5

 > pd str
=> Hello

210
211
212
# File 'lib/puts_debuggerer.rb', line 210

def printer
  @printer
end

.run_atObject

When to run as specified by an index, array, or range.

  • Default value is `nil` meaning always

  • Value as an Integer index (1-based) specifies at which run to print once

  • Value as an Array of indices specifies at which runs to print multiple times

  • Value as a range specifies at which runs to print multiple times, indefinitely if it ends with ..-1

Example:

PutsDebuggerer.run_at = 1
pd (x=1) # prints standard PD output
pd (x=1) # prints nothing

PutsDebuggerer.run_at = 2
pd (x=1) # prints nothing
pd (x=1) # prints standard PD output

PutsDebuggerer.run_at = [1, 3]
pd (x=1) # prints standard PD output
pd (x=1) # prints nothing
pd (x=1) # prints standard PD output
pd (x=1) # prints nothing

PutsDebuggerer.run_at = 3..5
pd (x=1) # prints nothing
pd (x=1) # prints nothing
pd (x=1) # prints standard PD output
pd (x=1) # prints standard PD output
pd (x=1) # prints standard PD output
pd (x=1) # prints nothing
pd (x=1) # prints nothing

PutsDebuggerer.run_at = 3...6
pd (x=1) # prints nothing
pd (x=1) # prints nothing
pd (x=1) # prints standard PD output
pd (x=1) # prints standard PD output
pd (x=1) # prints standard PD output
pd (x=1) # prints nothing

PutsDebuggerer.run_at = 3..-1
pd (x=1) # prints nothing
pd (x=1) # prints nothing
pd (x=1) # prints standard PD output
pd (x=1) ... continue printing indefinitely on all subsequent runs

PutsDebuggerer.run_at = 3...-1
pd (x=1) # prints nothing
pd (x=1) # prints nothing
pd (x=1) # prints standard PD output
pd (x=1) ... continue printing indefinitely on all subsequent runs

469
470
471
# File 'lib/puts_debuggerer.rb', line 469

def run_at
  @run_at
end

.source_line_countObject

Source Line Count.

  • Default value is `1`

Example:

PutsDebuggerer.source_line_count = 2
pd (true ||
  false), source_line_count: 2

Prints out:

********************************************************************************
[PD] /Users/User/example.rb:2
   > pd (true ||
       false), source_line_count: 2
  => "true"

111
112
113
# File 'lib/puts_debuggerer.rb', line 111

def source_line_count
  @source_line_count
end

.wrapperObject (readonly)

Wrapper to include at the top and bottom of every print out (both header and footer).

  • Default value is `nil`

  • Value `true` enables wrapper as `'*'*80`

  • Value `false`, `nil`, or empty string disables wrapper

  • Any other string value gets set as a custom wrapper

Example:

PutsDebuggerer.wrapper = true
pd (x=1)

Prints out:

[PD] /Users/User/example.rb:2
   > pd x=1
  => "1"
********************************************************************************

153
154
155
# File 'lib/puts_debuggerer.rb', line 153

def wrapper
  @wrapper
end

Class Method Details

.caller?Boolean

Returns:

  • (Boolean)

389
390
391
# File 'lib/puts_debuggerer.rb', line 389

def caller?
  !!caller
end

.convert_options(hash) ⇒ Object


490
491
492
# File 'lib/puts_debuggerer.rb', line 490

def convert_options(hash)
  Hash[hash.map { |key, value| OPTION_ALIASES[key] ? ( value == :t ?  [OPTION_ALIASES[key], true] : [OPTION_ALIASES[key], value] ) : [key, value]}]
end

.determine_object(objects) ⇒ Object


494
495
496
# File 'lib/puts_debuggerer.rb', line 494

def determine_object(objects)
  objects.compact.size > 1 ? objects : objects.first
end

.determine_options(objects) ⇒ Object


479
480
481
482
483
484
485
486
487
488
# File 'lib/puts_debuggerer.rb', line 479

def determine_options(objects)
  if objects.size > 1 && objects.last.is_a?(Hash)
    convert_options(objects.delete_at(-1))
  elsif objects.size == 1 && objects.first.is_a?(Hash)
    hash = objects.first
    convert_options(hash.slice(*OPTIONS).tap do
      hash.delete_if {|option| OPTIONS.include?(option)}
    end)
  end
end

.determine_printer(options) ⇒ Object


502
503
504
505
506
507
508
# File 'lib/puts_debuggerer.rb', line 502

def determine_printer(options)
  if options && options.has_key?(:printer)
    options[:printer]
  else
    PutsDebuggerer.printer
  end
end

.determine_run_at(options) ⇒ Object


498
499
500
# File 'lib/puts_debuggerer.rb', line 498

def determine_run_at(options)
  ((options && options[:run_at]) || PutsDebuggerer.run_at)
end

.optionsObject

Options as a hash. Useful for reading and backing up options


395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/puts_debuggerer.rb', line 395

def options
  {
    header: header,
    wrapper: wrapper,
    footer: footer,
    printer: printer,
    print_engine: print_engine,
    source_line_count: source_line_count,
    app_path: app_path,
    announcer: announcer,
    formatter: formatter,
    caller: caller,
    run_at: run_at
  }
end

.options=(hash) ⇒ Object

Sets options included in hash


412
413
414
415
416
# File 'lib/puts_debuggerer.rb', line 412

def options=(hash)
  hash.each do |option, value|
    send("#{option}=", value)
  end
end

Print engine is similar to `printer`, except it is focused on the scope of formatting the data object being printed (excluding metadata such as file name, line number, and expression, which are handled by the `printer`). As such, it is also a global method symbol or lambda expression. Examples of global methods are `:p`, `:ap`, and `:pp`. An example of a lambda expression is `lambda {|object| puts object.to_a.join(“ | ”)}`

Defaults to [awesome_print](github.com/awesome-print/awesome_print).

Example:

# File Name: /Users/User/example.rb
require 'awesome_print'
PutsDebuggerer.print_engine = :p
array = [1, [2, 3]]
pd array

Prints out:

[PD] /Users/User/example.rb:5
   > pd array
  => [1, [2, 3]]
]

269
270
271
272
273
274
275
# File 'lib/puts_debuggerer.rb', line 269

def print_engine
  if @print_engine.nil?
    require 'awesome_print' if RUBY_ENGINE != 'opal'
    @print_engine = print_engine_default
  end
  @print_engine
end

277
278
279
280
281
282
283
# File 'lib/puts_debuggerer.rb', line 277

def print_engine=(engine)
  if engine.is_a?(Proc) || engine.nil?
    @print_engine = engine
  else
    @print_engine = method(engine).name rescue raise(PRINT_ENGINE_MESSAGE_INVALID)
  end
end

285
286
287
# File 'lib/puts_debuggerer.rb', line 285

def print_engine_default
  Object.const_defined?(:AwesomePrint) ? PRINT_ENGINE_DEFAULT : :p
end

.printer_defaultObject


234
235
236
# File 'lib/puts_debuggerer.rb', line 234

def printer_default
  Object.const_defined?(:Rails) ? PRINTER_RAILS : PRINTER_DEFAULT
end

.run_at?Boolean

Returns:

  • (Boolean)

475
476
477
# File 'lib/puts_debuggerer.rb', line 475

def run_at?
  !!@run_at
end