enhanced_errors 0.1.4 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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