Class: EnhancedErrors

Inherits:
Object
  • Object
show all
Defined in:
lib/enhanced_errors.rb

Overview

The EnhancedErrors class provides mechanisms to enhance exception handling by capturing additional context such as binding information, variables, and method arguments when exceptions are raised. It offers customization options for formatting and filtering captured data.

Constant Summary collapse

GEMS_REGEX =

Regular expression to identify gem paths.

Returns:

  • (Regexp)
%r{[\/\\]gems[\/\\]}
DEFAULT_MAX_LENGTH =

The default maximum length for formatted exception messages.

Returns:

  • (Integer)
2500
RSPEC_SKIP_LIST =

A set of RSpec-specific instance variables to skip.

Returns:

  • (Set<Symbol>)
Set.new([
  :@fixture_cache,
  :@fixture_cache_key,
  :@fixture_connection_pools,
  :@connection_subscriber,
  :@saved_pool_configs,
  :@loaded_fixtures,
  :@matcher_definitions,
])
RAILS_SKIP_LIST =

A set of Rails-specific instance variables to skip.

Returns:

  • (Set<Symbol>)
Set.new([
  :@new_record,
  :@attributes,
  :@association_cache,
  :@readonly,
  :@previously_new_record,
  :@destroyed,
  :@marked_for_destruction,
  :@destroyed_by_association,
  :@primary_key,
  :@strict_loading,
  :@strict_loading_mode,
  :@mutations_before_last_save,
  :@mutations_from_database
])

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.capture_let_variables(value = nil) ⇒ Boolean

Gets or sets whether to capture RSpec ‘let` variables.

Parameters:

  • value (Boolean, nil) (defaults to: nil)

    The desired state. If ‘nil`, returns the current value.

Returns:

  • (Boolean)

    Whether RSpec ‘let` variables are being captured.



55
56
57
# File 'lib/enhanced_errors.rb', line 55

def capture_let_variables
  @capture_let_variables
end

.config_blockProc?

The configuration block provided during enhancement.

Returns:

  • (Proc, nil)


40
41
42
# File 'lib/enhanced_errors.rb', line 40

def config_block
  @config_block
end

.eligible_for_capture {|exception| ... } ⇒ Proc

Sets or retrieves the eligibility criteria for capturing exceptions.

Yield Parameters:

  • exception (Exception)

    The exception to evaluate.

Returns:

  • (Proc)

    The current eligibility proc.



60
61
62
# File 'lib/enhanced_errors.rb', line 60

def eligible_for_capture
  @eligible_for_capture
end

.enabledBoolean

Indicates whether EnhancedErrors is enabled.

Returns:

  • (Boolean)


30
31
32
# File 'lib/enhanced_errors.rb', line 30

def enabled
  @enabled
end

.max_length(value = nil) ⇒ Integer

Gets or sets the maximum length for the formatted exception message.

Parameters:

  • value (Integer, nil) (defaults to: nil)

    The desired maximum length. If ‘nil`, returns the current value.

Returns:

  • (Integer)

    The maximum length for the formatted message.



45
46
47
# File 'lib/enhanced_errors.rb', line 45

def max_length
  @max_length
end

.on_capture_hookProc?

Hook to modify binding information upon capture.

Returns:

  • (Proc, nil)


50
51
52
# File 'lib/enhanced_errors.rb', line 50

def on_capture_hook
  @on_capture_hook
end

.skip_listSet<Symbol>

Retrieves the current skip list, initializing it with default values if not already set.

Returns:

  • (Set<Symbol>)

    The current skip list.



65
66
67
# File 'lib/enhanced_errors.rb', line 65

def skip_list
  @skip_list
end

.traceTracePoint?

The TracePoint object used for tracing exceptions.

Returns:

  • (TracePoint, nil)


35
36
37
# File 'lib/enhanced_errors.rb', line 35

def trace
  @trace
end

Class Method Details

.add_to_skip_list(*vars) ⇒ Set<Symbol>

Adds variables to the skip list to exclude them from binding information.

Parameters:

  • vars (Symbol)

    The variable names to add to the skip list.

Returns:

  • (Set<Symbol>)

    The updated skip list.



153
154
155
# File 'lib/enhanced_errors.rb', line 153

def add_to_skip_list(*vars)
  skip_list.merge(vars)
end

.apply_skip_list(binding_info) ⇒ Hash

Applies the skip list to the captured binding information, excluding specified variables.

Parameters:

  • binding_info (Hash)

    The binding information to filter.

Returns:

  • (Hash)

    The filtered binding information.



327
328
329
330
331
332
333
334
335
# File 'lib/enhanced_errors.rb', line 327

def apply_skip_list(binding_info)
  unless @debug
    variables = binding_info[:variables]
    variables[:instances]&.reject! { |var, _| skip_list.include?(var) || (var.to_s.start_with?('@_') && !@debug) }
    variables[:locals]&.reject! { |var, _| skip_list.include?(var) }
    variables[:globals]&.reject! { |var, _| skip_list.include?(var) }
  end
  binding_info
end

.binding_info_string(binding_info) ⇒ String

Formats a single binding information hash into a string with colorization.

Parameters:

  • binding_info (Hash)

    The binding information to format.

Returns:

  • (String)

    The formatted string.



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/enhanced_errors.rb', line 353

def binding_info_string(binding_info)
  capture_event = binding_info[:capture_event].to_s.capitalize
  result = "#{Colors.red(capture_event)}: #{Colors.blue(binding_info[:source])}"

  result += method_and_args_desc(binding_info[:method_and_args])

  variables = binding_info[:variables] || {}

  if variables[:locals] && !variables[:locals].empty?
    result += "\n#{Colors.green('Locals:')}\n#{variable_description(variables[:locals])}"
  end

  instance_vars_to_display = variables[:instances] || {}

  if instance_vars_to_display && !instance_vars_to_display.empty?
    result += "\n#{Colors.green('Instances:')}\n#{variable_description(instance_vars_to_display)}"
  end

  if variables[:lets] && !variables[:lets].empty?
    result += "\n#{Colors.green('Let Variables:')}\n#{variable_description(variables[:lets])}"
  end

  if variables[:globals] && !variables[:globals].empty?
    result += "\n#{Colors.green('Globals:')}\n#{variable_description(variables[:globals])}"
  end

  if result.length > max_length
    result = result[0...max_length] + "... (truncated)"
  end
  result + "\n"
rescue => e
  # we swallow and don't re-raise to avoid any recursion problems. We don't want to
  # mess up the original exception handling.
  puts "EnhancedErrors error in binding_info_string: #{e.message} #{e.backtrace}"
  return ''
end

.binding_infos_array_to_string(captured_bindings, format = :terminal) ⇒ String

Converts an array of binding information hashes into a formatted string.

Parameters:

  • captured_bindings (Array<Hash>)

    The array of binding information.

  • format (Symbol) (defaults to: :terminal)

    The format to use (:json, :plaintext, :terminal).

Returns:

  • (String)

    The formatted string representation of the binding information.



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/enhanced_errors.rb', line 269

def binding_infos_array_to_string(captured_bindings, format = :terminal)
  case format
  when :json
    Colors.enabled = false
    JSON.pretty_generate(captured_bindings)
  when :plaintext
    Colors.enabled = false
    captured_bindings.map { |binding_info| binding_info_string(binding_info) }.join("\n")
  when :terminal
    Colors.enabled = true
    captured_bindings.map { |binding_info| binding_info_string(binding_info) }.join("\n")
  else
    Colors.enabled = false
    captured_bindings.map { |binding_info| binding_info_string(binding_info) }.join("\n")
  end
end

.default_skip_listSet<Symbol>

Initializes the default skip list by merging Rails and RSpec specific variables.

Returns:

  • (Set<Symbol>)

    The default skip list.



145
146
147
# File 'lib/enhanced_errors.rb', line 145

def default_skip_list
  Set.new(RAILS_SKIP_LIST).merge(RSPEC_SKIP_LIST)
end

.enhance!(enabled: true, debug: false, capture_events: default_capture_events, **options) {|void| ... } ⇒ void

This method returns an undefined value.

Enhances the exception handling by setting up tracing and configuration options.

Parameters:

  • enabled (Boolean) (defaults to: true)

    Whether to enable EnhancedErrors.

  • debug (Boolean) (defaults to: false)

    Whether to enable debug mode.

  • options (Hash)

    Additional configuration options.

Yields:

  • (void)

    A block for additional configuration.



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/enhanced_errors.rb', line 164

def enhance!(enabled: true, debug: false, capture_events: default_capture_events, **options, &block)
  capture_events = Array(capture_events)
  @output_format = nil
  @eligible_for_capture = nil
  @original_global_variables = nil
  if enabled == false
    @original_global_variables = nil
    @enabled = false
    @trace.disable if @trace
  else
    @enabled = true
    @debug = debug
    @original_global_variables = global_variables

    validate_and_set_capture_events(capture_events)
    options.each do |key, value|
      setter_method = "#{key}="
      if respond_to?(setter_method)
        send(setter_method, value)
      elsif respond_to?(key)
        send(key, value)
      else
        # Ignore unknown options or handle as needed
      end
    end

    @config_block = block_given? ? block : nil
    instance_eval(&@config_block) if @config_block

    start_tracing
  end
end

.format(captured_bindings = [], output_format = get_default_format_for_environment) ⇒ String

Formats the captured binding information into a string based on the specified format.

Parameters:

  • captured_bindings (Array<Hash>) (defaults to: [])

    The array of captured binding information.

  • output_format (Symbol) (defaults to: get_default_format_for_environment)

    The format to use (:json, :plaintext, :terminal).

Returns:

  • (String)

    The formatted exception message.



254
255
256
257
258
259
260
261
262
# File 'lib/enhanced_errors.rb', line 254

def format(captured_bindings = [], output_format = get_default_format_for_environment)
  result = binding_infos_array_to_string(captured_bindings, output_format)
  if @on_format_hook
    result = @on_format_hook.call(result)
  else
    result = default_on_format(result)
  end
  result
end

.get_default_format_for_environmentSymbol

Determines the default output format based on the current environment.

Returns:

  • (Symbol)

    The default format (:json, :plaintext, :terminal).



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/enhanced_errors.rb', line 289

def get_default_format_for_environment
  return @output_format unless @output_format.nil?
  env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
  @output_format = case env
                   when 'development', 'test'
                     if running_in_ci?
                       :plaintext
                     else
                       :terminal
                     end
                   when 'production'
                     :json
                   else
                     :terminal
                   end
end

.on_capture {|binding_info| ... } ⇒ Proc

Sets or retrieves the hook to modify binding information upon capture.

Yield Parameters:

  • binding_info (Hash)

    The binding information captured.

Returns:

  • (Proc)

    The current on_capture hook.



213
214
215
216
217
218
219
# File 'lib/enhanced_errors.rb', line 213

def on_capture(&block)
  if block_given?
    @on_capture_hook = block
  else
    @on_capture_hook ||= method(:default_on_capture)
  end
end

.on_capture=(value) ⇒ Proc

Sets the on_capture hook.

Parameters:

  • value (Proc)

    The proc to set as the on_capture hook.

Returns:

  • (Proc)

    The newly set on_capture hook.



225
226
227
# File 'lib/enhanced_errors.rb', line 225

def on_capture=(value)
  self.on_capture_hook = value
end

.on_format {|formatted_string| ... } ⇒ Proc

Sets or retrieves the hook to modify formatted exception messages.

Yield Parameters:

  • formatted_string (String)

    The formatted exception message.

Returns:

  • (Proc)

    The current on_format hook.



233
234
235
236
237
238
239
# File 'lib/enhanced_errors.rb', line 233

def on_format(&block)
  if block_given?
    @on_format_hook = block
  else
    @on_format_hook ||= method(:default_on_format)
  end
end

.on_format=(value) ⇒ Proc

Sets the on_format hook.

Parameters:

  • value (Proc)

    The proc to set as the on_format hook.

Returns:

  • (Proc)

    The newly set on_format hook.



245
246
247
# File 'lib/enhanced_errors.rb', line 245

def on_format=(value)
  @on_format_hook = value
end

.running_in_ci?Boolean

Checks if the code is running in a Continuous Integration (CI) environment.

Returns:

  • (Boolean)

    ‘true` if running in CI, otherwise `false`.



309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/enhanced_errors.rb', line 309

def running_in_ci?
  return @running_in_ci if defined?(@running_in_ci)
  ci_env_vars = {
    'CI' => ENV['CI'],
    'JENKINS' => ENV['JENKINS'],
    'GITHUB_ACTIONS' => ENV['GITHUB_ACTIONS'],
    'CIRCLECI' => ENV['CIRCLECI'],
    'TRAVIS' => ENV['TRAVIS'],
    'APPVEYOR' => ENV['APPVEYOR'],
    'GITLAB_CI' => ENV['GITLAB_CI']
  }
  @running_in_ci = ci_env_vars.any? { |_, value| value.to_s.downcase == 'true' }
end

.validate_binding_format(binding_info) ⇒ Hash?

Validates the format of the captured binding information.

Parameters:

  • binding_info (Hash)

    The binding information to validate.

Returns:

  • (Hash, nil)

    The validated binding information or ‘nil` if invalid.



341
342
343
344
345
346
347
# File 'lib/enhanced_errors.rb', line 341

def validate_binding_format(binding_info)
  unless binding_info.keys.include?(:capture_event) && binding_info[:variables].is_a?(Hash)
    puts "Invalid binding_info format."
    return nil
  end
  binding_info
end