module NRSER::NicerError

A mixin for {Exception} and utilities to make life better… even when things go wrong.

Check the docs at the {file:lib/nrser/errors/README.md nrser/errors README}.

Constants

DEFAULT_COLUMN_WIDTH

Default column width

Public Class Methods

column_width() click to toggle source

Column width to format for (just summary/super-message at the moment).

@todo

Implement terminal width detection like Thor?

@return [Fixnum]

Positive integer.
# File lib/nrser/errors/nicer_error.rb, line 45
def self.column_width
  DEFAULT_COLUMN_WIDTH
end
included(base) click to toggle source
# File lib/nrser/errors/nicer_error.rb, line 70
def self.included base
  base.extend ClassMethods
end
new(*message, binding: nil, details: nil, **context) click to toggle source

Construct a nicer error.

@param [Array] message

Main message segments. See {#format_message} and {#format_message_segment}
for an understanding of how they are, well, formatted.

@param [Binding?] binding

When provided any details string will be rendered using it's
{Binding#erb} method.

@param [nil | String | Proc<()=>String> | to_s] details

Additional text details to add to the extended message. When:

1.  `nil` - no details will be added.

2.  `String` - the value will be used. If `binding:` is provided, it
    will be rendered against it as ERB.

3.  `Proc<()=>String>` - if and when an extended message is needed
    the proc will be called, and the resulting string will be used
    as in (2).

4.  `#to_s` - catch all; if and when an extended message is needed
    `#to_s` will be called on the value and the result will be used
    as in (2).

@param [Hash<Symbol, VALUE>] context

Any additional names and values to dump with an extended message.
Calls superclass method
# File lib/nrser/errors/nicer_error.rb, line 104
def initialize  *message,
                binding: nil,
                details: nil,
                **context
  @binding = binding.freeze
  @context = context.freeze
  @details = details.freeze
  
  message = default_message if message.empty?
  super_message = format_message *message
  
  super super_message
end

Public Instance Methods

add_extended_message?() click to toggle source

Should we add the extended message to {#to_s} output?

@todo

Just returns `true` for now... should be configurable in the future.

@return [Boolean]

# File lib/nrser/errors/nicer_error.rb, line 264
def add_extended_message?
  true
end
context() click to toggle source

Any additional context values to add to extended messages provided to {#initialize}.

@return [Hash<Symbol, *>]

# File lib/nrser/errors/nicer_error.rb, line 171
def context
  @context
end
context_section() click to toggle source

@return [String?]

# File lib/nrser/errors/nicer_error.rb, line 216
def context_section
  lazy_var :@context_section do
    if context.empty?
      nil
    else
      "# Context:\n\n" + context.map { |name, value|
        name_str = name.to_s
        value_str = PP.pp \
          value,
          ''.dup,
          (NRSER::NicerError.column_width - name_str.length - 2)
        
        if value_str.lines.count > 1
          "#{ name_str }:\n\n#{ value_str.indent 4 }\n"
        else
          "#{ name_str }: #{ value_str }\n"
        end
      }.join
    end
  end
end
default_message() click to toggle source

Main message to use when none provided to {#initialize}.

@return [String]

# File lib/nrser/errors/nicer_error.rb, line 161
def default_message
  "(no message)"
end
details() click to toggle source
# File lib/nrser/errors/nicer_error.rb, line 176
def details
  @details
end
details_section() click to toggle source

Render details (first time only, then cached) and return the string.

@return [String?]

# File lib/nrser/errors/nicer_error.rb, line 185
def details_section
  lazy_var :@details_section do
    # No details if we have nothing to work with
    if details.nil?
      nil
    else
      contents = case details
      when Proc
        details.call
      when String
        details
      else
        details.to_s
      end
      
      if contents.empty?
        nil
      else
        if @binding
          contents = binding.erb contents
        end
        
        "# Details\n\n" + contents
      end
    end
  end
end
extended_message() click to toggle source

Return the extended message, rendering if necessary (cached after first call).

@return [String]

Will be empty if there is no extended message.
# File lib/nrser/errors/nicer_error.rb, line 245
def extended_message
  @extended_message ||= begin
    sections = []
    
    sections << details_section unless details_section.nil?
    sections << context_section unless context_section.nil?
    
    joined = sections.join "\n\n"
  end
end
format_message(*message) click to toggle source

Format the main message by converting args to strings and joining them.

@param [Array] message

Message segments.

@return [String]

Formatted and joined message ready to pass up to the built-in
exception's `#initialize`.
# File lib/nrser/errors/nicer_error.rb, line 152
def format_message *message
  message.map( &method( :format_message_segment ) ).join( ' ' )
end
format_message_segment(segment) click to toggle source

Format a segment of the error message.

Strings are simply returned. Other things are inspected (for now).

@param [Object] segment

The segment.

@return [String]

The formatted string for the segment.

@todo

This should talk to config when that comes about to find out how to
dump things.
# File lib/nrser/errors/nicer_error.rb, line 133
def format_message_segment segment
  return segment.to_summary if segment.respond_to?( :to_summary )
  
  return segment if String === segment
  
  # TODO  Do better!
  segment.inspect
end
to_s(extended: nil) click to toggle source

Get the message or the extended message.

@note

This is a bit weird, having to do with what I can tell about the
built-in errors and how they handle their message - they have *no*
instance variables, and seem to rely on `#to_s` to get the message
out of C-land, however that works.

{Exception#message} just forwards here, so I overrode that with
{#message} to just get the *summary/super-message* from this method.

@param [Boolean?] extended

Flag to explicitly control summary/super or extended message:

1.  `nil` - call {#add_extended_message?} to decide (default).
2.  `false` - return just the *summary/super-message*.
3.  `true` - always add the *extended message* (unless it's empty).

@return [String]

Calls superclass method
# File lib/nrser/errors/nicer_error.rb, line 289
def to_s extended: nil
  # The way to get the superclass' message
  message = super()
  
  # If `extended` is explicitly `false` then just return that
  return message if extended == false
  
  # Otherwise, see if the extended message was explicitly requested,
  # of if we're configured to provide it as well.
  #
  # Either way, don't add it it's empty.
  #
  if  (extended || add_extended_message?) &&
      !extended_message.empty?
    message + "\n\n" + extended_message
  else
    message
  end
end