Module: NRSER::NicerError

Included in:
AbstractMethodError, ArgumentError, ConflictError, TypeError, Types::FromStringError, 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).



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?



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.



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

def context
  @context
end

#context_sectionString?



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.



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.



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



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.



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



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.



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.



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