Module: NRSER::NicerError

Included in:
AbstractMethodError, ArgumentError, ConflictError, TypeError, Types::FromStringError, UnreachableError, ValueError
Defined in:
lib/nrser/errors/nicer_error.rb

Overview

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

Check the docs at the nrser/errors README.

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

DEFAULT_COLUMN_WIDTH =

Default column width

78

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.column_widthFixnum

TODO:

Implement terminal width detection like Thor?

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

Returns:

  • (Fixnum)

    Positive integer.



45
46
47
# File 'lib/nrser/errors/nicer_error.rb', line 45

def self.column_width
  DEFAULT_COLUMN_WIDTH
end

.included(base) ⇒ Object



70
71
72
# File 'lib/nrser/errors/nicer_error.rb', line 70

def self.included base
  base.extend ClassMethods
end

Instance Method Details

#add_extended_message?Boolean

TODO:

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

Should we add the extended message to #to_s output?

Returns:

  • (Boolean)


264
265
266
# File 'lib/nrser/errors/nicer_error.rb', line 264

def add_extended_message?
  true
end

#contextHash<Symbol, *>

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

Returns:



171
172
173
# File 'lib/nrser/errors/nicer_error.rb', line 171

def context
  @context
end

#context_sectionString?

Returns:



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# 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_messageString

Main message to use when none provided to #initialize.

Returns:



161
162
163
# File 'lib/nrser/errors/nicer_error.rb', line 161

def default_message
  "(no message)"
end

#detailsObject



176
177
178
# File 'lib/nrser/errors/nicer_error.rb', line 176

def details
  @details
end

#details_sectionString?

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

Returns:



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# 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_messageString

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

Returns:

  • (String)

    Will be empty if there is no extended message.



245
246
247
248
249
250
251
252
253
254
# 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) ⇒ String

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

Parameters:

  • message (Array)

    Message segments.

Returns:

  • (String)

    Formatted and joined message ready to pass up to the built-in exception’s ‘#initialize`.



152
153
154
# 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) ⇒ String

TODO:

This should talk to config when that comes about to find out how to dump things.

Format a segment of the error message.

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

Parameters:

  • segment (Object)

    The segment.

Returns:

  • (String)

    The formatted string for the segment.



133
134
135
136
137
138
139
140
# 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

#initialize(*message, binding: nil, details: nil, **context) ⇒ Object

Construct a nicer error.

Parameters:

  • message (Array)

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

  • binding (Binding?) (defaults to: nil)

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

  • details (nil | String | Proc<()=>String> | #to_s) (defaults to: nil)

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

  • context (Hash<Symbol, VALUE>)

    Any additional names and values to dump with an extended message.



104
105
106
107
108
109
110
111
112
113
114
115
116
# 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

#to_s(extended: nil) ⇒ String

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.

Get the message or the extended message.

Parameters:

  • extended (Boolean?) (defaults to: nil)

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

Returns:



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# 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