enhanced_errors 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,20 @@ require_relative 'colors'
5
5
  require_relative 'error_enhancements'
6
6
  require_relative 'binding'
7
7
 
8
+ # While we could just catch StandardError, we would miss a number of things.
9
+
10
+ IGNORED_EXCEPTIONS = [
11
+ SystemExit,
12
+ NoMemoryError,
13
+ SignalException,
14
+ Interrupt,
15
+ ScriptError,
16
+ LoadError,
17
+ NotImplementedError,
18
+ SyntaxError,
19
+ SystemStackError
20
+ ]
21
+
8
22
  # The EnhancedErrors class provides mechanisms to enhance exception handling by capturing
9
23
  # additional context such as binding information, variables, and method arguments when exceptions are raised.
10
24
  # It offers customization options for formatting and filtering captured data.
@@ -147,7 +161,8 @@ class EnhancedErrors
147
161
  # @param options [Hash] Additional configuration options.
148
162
  # @yield [void] A block for additional configuration.
149
163
  # @return [void]
150
- def enhance!(enabled: true, debug: false, **options, &block)
164
+ def enhance!(enabled: true, debug: false, capture_events: default_capture_events, **options, &block)
165
+ capture_events = Array(capture_events)
151
166
  @output_format = nil
152
167
  @eligible_for_capture = nil
153
168
  @original_global_variables = nil
@@ -160,6 +175,7 @@ class EnhancedErrors
160
175
  @debug = debug
161
176
  @original_global_variables = global_variables
162
177
 
178
+ validate_and_set_capture_events(capture_events)
163
179
  options.each do |key, value|
164
180
  setter_method = "#{key}="
165
181
  if respond_to?(setter_method)
@@ -311,7 +327,7 @@ class EnhancedErrors
311
327
  def apply_skip_list(binding_info)
312
328
  unless @debug
313
329
  variables = binding_info[:variables]
314
- variables[:instances]&.reject! { |var, _| skip_list.include?(var) || var.to_s.start_with?('@__') }
330
+ variables[:instances]&.reject! { |var, _| skip_list.include?(var) || (var.to_s.start_with?('@_') && !@debug) }
315
331
  variables[:locals]&.reject! { |var, _| skip_list.include?(var) }
316
332
  variables[:globals]&.reject! { |var, _| skip_list.include?(var) }
317
333
  end
@@ -323,7 +339,7 @@ class EnhancedErrors
323
339
  # @param binding_info [Hash] The binding information to validate.
324
340
  # @return [Hash, nil] The validated binding information or `nil` if invalid.
325
341
  def validate_binding_format(binding_info)
326
- unless binding_info.keys.include?(:capture_type) && binding_info[:variables].is_a?(Hash)
342
+ unless binding_info.keys.include?(:capture_event) && binding_info[:variables].is_a?(Hash)
327
343
  puts "Invalid binding_info format."
328
344
  return nil
329
345
  end
@@ -335,8 +351,8 @@ class EnhancedErrors
335
351
  # @param binding_info [Hash] The binding information to format.
336
352
  # @return [String] The formatted string.
337
353
  def binding_info_string(binding_info)
338
- capture_type = binding_info[:capture_type].to_s.capitalize
339
- result = "#{Colors.red(capture_type)}: #{Colors.blue(binding_info[:source])}"
354
+ capture_event = binding_info[:capture_event].to_s.capitalize
355
+ result = "#{Colors.red(capture_event)}: #{Colors.blue(binding_info[:source])}"
340
356
 
341
357
  result += method_and_args_desc(binding_info[:method_and_args])
342
358
 
@@ -348,7 +364,6 @@ class EnhancedErrors
348
364
 
349
365
  instance_vars_to_display = variables[:instances] || {}
350
366
 
351
-
352
367
  if instance_vars_to_display && !instance_vars_to_display.empty?
353
368
  result += "\n#{Colors.green('Instances:')}\n#{variable_description(instance_vars_to_display)}"
354
369
  end
@@ -365,6 +380,11 @@ class EnhancedErrors
365
380
  result = result[0...max_length] + "... (truncated)"
366
381
  end
367
382
  result + "\n"
383
+ rescue => e
384
+ # we swallow and don't re-raise to avoid any recursion problems. We don't want to
385
+ # mess up the original exception handling.
386
+ puts "EnhancedErrors error in binding_info_string: #{e.message} #{e.backtrace}"
387
+ return ''
368
388
  end
369
389
 
370
390
  private
@@ -375,11 +395,10 @@ class EnhancedErrors
375
395
  def start_tracing
376
396
  return if @trace && @trace.enabled?
377
397
 
378
- events = [:raise]
379
- events << :rescue if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3.0')
398
+ events = @capture_events ? @capture_events.to_a : [:raise]
380
399
 
381
400
  @trace = TracePoint.new(*events) do |tp|
382
- next if Thread.current[:enhanced_errors_processing] || tp.raised_exception.is_a?(NoMemoryError)
401
+ next if Thread.current[:enhanced_errors_processing] || IGNORED_EXCEPTIONS.include?(tp.raised_exception)
383
402
  Thread.current[:enhanced_errors_processing] = true
384
403
  exception = tp.raised_exception
385
404
  capture_me = EnhancedErrors.eligible_for_capture.call(exception)
@@ -425,10 +444,9 @@ class EnhancedErrors
425
444
  globals = (global_variables - @original_global_variables).map { |var|
426
445
  [var, get_global_variable_value(var)]
427
446
  }.to_h
428
- puts "Global Variables: #{globals.inspect}"
429
447
  end
430
448
 
431
- capture_type = tp.event.to_s # 'raise' or 'rescue'
449
+ capture_event = tp.event.to_s # 'raise' or 'rescue'
432
450
  location = "#{tp.path}:#{tp.lineno}"
433
451
 
434
452
  binding_info = {
@@ -444,7 +462,7 @@ class EnhancedErrors
444
462
  globals: globals
445
463
  },
446
464
  exception: exception.class.name,
447
- capture_type: capture_type.to_s
465
+ capture_event: capture_event.to_s
448
466
  }
449
467
 
450
468
  if on_capture_hook
@@ -469,6 +487,7 @@ class EnhancedErrors
469
487
  @trace.enable
470
488
  end
471
489
 
490
+
472
491
  # Retrieves the current test name from RSpec, if available.
473
492
  #
474
493
  # @return [String, nil] The current test name or `nil` if not in a test context.
@@ -480,6 +499,42 @@ class EnhancedErrors
480
499
  nil
481
500
  end
482
501
 
502
+ # Helper method to determine the default capture types based on Ruby version
503
+ #
504
+ # @return [Set<Symbol>] The default set of capture types
505
+ def default_capture_events
506
+ default_events = [:raise]
507
+ default_events << :rescue if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.3.0')
508
+ Set.new(default_events)
509
+ end
510
+
511
+ def validate_and_set_capture_events(capture_events)
512
+ if capture_events.nil? || !valid_capture_events?(capture_events)
513
+ puts "EnhancedErrors: Invalid capture_events provided. Falling back to defaults."
514
+ capture_events = default_capture_events
515
+ end
516
+
517
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.3.0') && capture_events.include?(:rescue)
518
+ puts "EnhancedErrors: Warning: :rescue capture_event is not supported in Ruby versions below 3.3.0 and will be ignored."
519
+ capture_events.delete(:rescue)
520
+ end
521
+
522
+ if capture_events.empty?
523
+ puts "No valid capture_events provided to EnhancedErrors.enhance! Falling back to defaults."
524
+ capture_events = default_capture_events
525
+ end
526
+
527
+ @capture_events = capture_events.to_a
528
+ end
529
+
530
+
531
+ # Validate capture_events: must be an Array or Set containing only :raise and/or :rescue.
532
+ def valid_capture_events?(capture_events)
533
+ return false unless capture_events.is_a?(Array) || capture_events.is_a?(Set)
534
+ valid_types = [:raise, :rescue].to_set
535
+ capture_events.to_set.subset?(valid_types)
536
+ end
537
+
483
538
  # Extracts method arguments from the TracePoint binding.
484
539
  #
485
540
  # @param tp [TracePoint] The current TracePoint.
@@ -560,6 +615,8 @@ class EnhancedErrors
560
615
  # @return [String] The formatted variable.
561
616
  def format_variable(variable)
562
617
  (awesome_print_available? && Colors.enabled?) ? variable.ai : variable.inspect
618
+ rescue => e
619
+ return "#{variable.to_s.truncate(30)}: [Inspection Error]"
563
620
  end
564
621
 
565
622
  # Checks if the `AwesomePrint` gem is available.
@@ -1,9 +1,12 @@
1
1
  module ErrorEnhancements
2
2
  def message
3
3
  original_message = super()
4
- "#{original_message}\n#{variables_message}"
4
+ if original_message.include?(variables_message)
5
+ original_message
6
+ else
7
+ "#{original_message}\n#{variables_message}"
8
+ end
5
9
  rescue => e
6
- puts "Error in message method: #{e.message}"
7
10
  original_message
8
11
  end
9
12
 
@@ -31,7 +34,7 @@ module ErrorEnhancements
31
34
  bindings_of_interest = []
32
35
 
33
36
  binding_infos.each do |info|
34
- if info[:capture_type] == 'raise' && !info[:library]
37
+ if info[:capture_event] == 'raise' && !info[:library]
35
38
  bindings_of_interest << info
36
39
  break
37
40
  end
@@ -43,7 +46,7 @@ module ErrorEnhancements
43
46
 
44
47
  # find the last rescue binding if there is one
45
48
  binding_infos.reverse.each do |info|
46
- if info[:capture_type] == 'rescue'
49
+ if info[:capture_event] == 'rescue'
47
50
  bindings_of_interest << info
48
51
  break
49
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enhanced_errors
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Beland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-25 00:00:00.000000000 Z
11
+ date: 2024-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -52,9 +52,9 @@ dependencies:
52
52
  - - ">"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.9'
55
- description: With no extra dependencies, and using only Ruby's built-in TracePoint,
56
- EnhancedErrors will automatically enhance your errors with messages containing variable
57
- values from the moment they were raised.
55
+ description: 'EnhancedErrors will automatically enhance your errors with messages
56
+ containing variable values from the moment they were raised, using no extra dependencies,
57
+ and only Ruby''s built-in TracePoint. '
58
58
  email:
59
59
  executables: []
60
60
  extensions: []
@@ -68,6 +68,7 @@ files:
68
68
  - LICENSE
69
69
  - README.md
70
70
  - benchmark/benchmark.rb
71
+ - benchmark/memory_bench.rb
71
72
  - benchmark/stackprofile.rb
72
73
  - doc/Binding.html
73
74
  - doc/Colors.html