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]} in #{[data[:class], data[:method]].compact.join('.')}#{" (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[^\)]+)/
STACK_TRACE_CALL_METHOD_REGEX =
/`([^']+)'$/
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


305
306
307
# File 'lib/puts_debuggerer.rb', line 305

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"


92
93
94
# File 'lib/puts_debuggerer.rb', line 92

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'


382
383
384
# File 'lib/puts_debuggerer.rb', line 382

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"
********************************************************************************


175
176
177
# File 'lib/puts_debuggerer.rb', line 175

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: ********************************************************************************


355
356
357
# File 'lib/puts_debuggerer.rb', line 355

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"


137
138
139
# File 'lib/puts_debuggerer.rb', line 137

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.



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

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.



247
248
249
# File 'lib/puts_debuggerer.rb', line 247

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


213
214
215
# File 'lib/puts_debuggerer.rb', line 213

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


472
473
474
# File 'lib/puts_debuggerer.rb', line 472

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"


114
115
116
# File 'lib/puts_debuggerer.rb', line 114

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"
********************************************************************************


156
157
158
# File 'lib/puts_debuggerer.rb', line 156

def wrapper
  @wrapper
end

Class Method Details

.caller?Boolean

Returns:

  • (Boolean)


392
393
394
# File 'lib/puts_debuggerer.rb', line 392

def caller?
  !!caller
end

.convert_options(hash) ⇒ Object



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

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



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

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

.determine_options(objects) ⇒ Object



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

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))
  end
end

.determine_printer(options) ⇒ Object



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

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

.determine_run_at(options) ⇒ Object



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

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



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/puts_debuggerer.rb', line 398

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



415
416
417
418
419
# File 'lib/puts_debuggerer.rb', line 415

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]]
]


272
273
274
275
276
277
278
# File 'lib/puts_debuggerer.rb', line 272

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


280
281
282
283
284
285
286
# File 'lib/puts_debuggerer.rb', line 280

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


288
289
290
# File 'lib/puts_debuggerer.rb', line 288

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

.printer_defaultObject



237
238
239
# File 'lib/puts_debuggerer.rb', line 237

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

.run_at?Boolean

Returns:

  • (Boolean)


478
479
480
# File 'lib/puts_debuggerer.rb', line 478

def run_at?
  !!@run_at
end