rdnewman/loba

View on GitHub
lib/loba.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
require 'loba/version'
require 'loba/internal'

require 'binding_of_caller'
require 'rainbow'

# Loba module for quick tracing of Ruby and Rails.
# If a Rails application, will use Rails.logger.debug.
# If not a Rails application, will use STDOUT.
module Loba
  # Outputs a timestamped notice, useful for quick traces to see the code path.
  # Also does a simple elapsed time check since the previous timestamp notice to
  # help with quick, minimalist profiling.
  # @param production [Boolean] set to true if this timestamp notice is
  #   to be recorded when running in :production environment
  # @param log [Boolean] when false, will not write to Rails.logger if present;
  #   when true, will write to Rails.logger if present
  # @return [NilClass] nil
  # @example Basic use
  #   def hello
  #     Loba.timestamp
  #   end
  #   #=> [TIMESTAMP] #=0001, diff=0.000463, at=1451615389.505411, in=/path/to/file.rb:2:in 'hello'
  # @example Forced to output when in production environment
  #   def hello
  #     Loba.ts production: true # Loba.ts is a shorthand alias for Loba.timestamp
  #   end
  #   #=> [TIMESTAMP] #=0001, diff=0.000463, at=1451615389.505411, in=/path/to/file.rb:2:in 'hello'
  # @example Forced to output to log (if Rails.logger present) in addition to $STDOUT
  #   def hello
  #     Loba.timestamp log: true
  #   end
  #   #=> [TIMESTAMP] #=0001, diff=0.000463, at=1451615389.505411, in=/path/to/file.rb:2:in 'hello'
  def timestamp(production: false, log: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
    return unless Internal::Platform.logging_ok?(production)

    # NOTE: while tempting, memoizing loba_logger can lead to surprises if
    #   Rails presence isn't constant
    loba_logger = Internal::Platform.logger

    begin
      stats = Internal::TimeKeeper.instance.ping
      loba_logger.call(
        # 60: light_black / grey
        "#{Rainbow('[TIMESTAMP]').black.bg(60)}" \
        "#{Rainbow(' #=').yellow.bg(:default)}" \
        "#{format('%04d', stats[:number])}" \
        "#{Rainbow(', diff=').yellow}" \
        "#{format('%.6f', stats[:change])}" \
        "#{Rainbow(', at=').yellow}" \
        "#{format('%.6f', stats[:now].round(6).to_f)}" \
        "#{Rainbow("    \t(in #{caller(1..1).first})").color(60)}", # warning: nested interpolation
        !!log
      )
    rescue StandardError => e
      loba_logger.call Rainbow("[TIMESTAMP] #=FAIL, in=#{caller(1..1).first}, err=#{e}").red
    end

    nil
  end
  module_function :timestamp

  # Shorthand alias for Loba.timestamp.
  # @!method ts(production: false, log: false)
  alias ts timestamp
  module_function :ts

  # Outputs a value notice showing value of provided argument including method and
  # class identification.
  # @param argument [various] (required) the value to be evaluated and shown; if given as
  #   a Symbol, a label based on the argument will proceed the value the argument refers to
  # @param label [String] explicit label to be used instead of attempting
  #   to infer from the argument; default is to attempt to infer a label from the argument
  # @param inspect [Boolean] true if this value notice is to use #inspect against the
  #   content being evaluated; otherwise, false
  # @param production [Boolean] set to true if this timestamp notice is
  #   to be recorded when running in :production environment
  # @param log [Boolean] when false, will not write to Rails.logger if present;
  #   when true, will write to Rails.logger if present
  # @return [NilClass] nil
  # @example Using Symbol as argument
  #   class HelloWorld
  #     def hello(name)
  #   Loba.value :name # putting Loba statement to far left helps remember to remove later
  #       puts "Hello, #{name}!"
  #     end
  #   end
  #   HelloWorld.new.hello("Charlie")
  #   #=> [HelloWorld#hello] name: Charlie        (at /path/to/file/hello_world.rb:3:in `hello')
  #   #=> Hello, Charlie!
  # @example Using non-Symbol as argument
  #   class HelloWorld
  #     def hello(name)
  #   Loba.val name # Loba.val is a shorthand alias for Loba.value
  #       puts "Hello, #{name}!"
  #     end
  #   end
  #   HelloWorld.new.hello("Charlie")
  #   #=> [HelloWorld#hello] Charlie        (at /path/to/file/hello_world.rb:3:in `hello')
  #   #=> Hello, Charlie!
  # @example Using non-Symbol as argument with a label
  #   class HelloWorld
  #     def hello(name)
  #   Loba.value name, "Name:"
  #       puts "Hello, #{name}!"
  #     end
  #   end
  #   HelloWorld.new.hello("Charlie")
  #   #=> [HelloWorld#hello] Name: Charlie        (at /path/to/file/hello_world.rb:3:in `hello')
  #   #=> Hello, Charlie!
  def value(argument, label: nil, inspect: true, production: false, log: false) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
    return nil unless Internal::Platform.logging_ok?(production)

    text = Internal::Value.phrases(
      argument: (argument.nil? ? :nil : argument),
      label: label,
      inspect: inspect,
      depth_offset: 1
    )

    Internal::Platform.logger.call(
      # NOTE: while tempting, memoizing Internal::Platform.logger can lead to surprises
      #   if Rails presence isn't constant
      #
      # warning: nested interpolation below (slight help to performance)
      # 60: light_black
      # 62: light_green
      "#{Rainbow("#{text[:tag]} ").green.bg(:default)}" \
      "#{Rainbow("#{text[:label]} ").color(62)}" \
      "#{text[:value]}" \
      "#{Rainbow("    \t(in #{text[:line]})").color(60)}",
      !!log
    )

    nil
  end
  module_function :value

  # Shorthand alias for Loba.value.
  # @!method val(argument, label: nil, inspect: true, production: false, log: false)
  alias val value
  module_function :val
end