enhanced_errors 3.0.3 → 3.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +43 -35
- data/benchmark/benchmark.rb +31 -29
- data/benchmark/memory_bench.rb +1 -1
- data/benchmark/result.txt +11 -0
- data/doc/Enhanced/Colors.html +2 -2
- data/doc/Enhanced/Context.html +283 -0
- data/doc/Enhanced/ExceptionBindingInfos.html +249 -0
- data/doc/Enhanced/ExceptionContext.html +397 -0
- data/doc/Enhanced.html +8 -4
- data/doc/EnhancedErrors.html +395 -275
- data/doc/EnhancedExceptionContext.html +15 -15
- data/doc/Exception.html +5 -5
- data/doc/ExceptionBindingInfos.html +2 -2
- data/doc/Minitest.html +3 -3
- data/doc/_index.html +12 -6
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +39 -44
- data/doc/index.html +39 -44
- data/doc/method_list.html +34 -18
- data/doc/top-level-namespace.html +18 -8
- data/enhanced_errors.gemspec +1 -1
- data/lib/enhanced/context.rb +7 -5
- data/lib/enhanced/exception.rb +35 -36
- data/lib/enhanced/exception_context.rb +49 -0
- data/lib/enhanced/minitest_patch.rb +1 -1
- data/lib/enhanced_errors.rb +147 -98
- metadata +8 -13
- data/.yardoc/checksums +0 -6
- data/.yardoc/complete +0 -0
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardoc/proxy_types +0 -0
- data/lib/enhanced/enhanced_exception_context.rb +0 -47
data/lib/enhanced_errors.rb
CHANGED
@@ -4,12 +4,12 @@ require 'set'
|
|
4
4
|
require 'json'
|
5
5
|
require 'monitor'
|
6
6
|
|
7
|
-
|
8
|
-
require_relative 'enhanced/exception'
|
7
|
+
module Enhanced; end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
RSpec::Matchers::BuiltIn::RaiseError
|
9
|
+
# Exceptions we could handle but overlook for other reasons. These class constants are not always loaded
|
10
|
+
# and generally are only be available when `required`, so we detect them by strings.
|
11
|
+
IGNORED_EXCEPTIONS = %w[RSpec::Expectations::ExpectationNotMetError RSpec::Matchers::BuiltIn::RaiseError
|
12
|
+
JSON::ParserError Zlib::Error OpenSSL::SSL::SSLError Psych::Exception]
|
13
13
|
|
14
14
|
class EnhancedErrors
|
15
15
|
extend ::Enhanced
|
@@ -45,13 +45,8 @@ class EnhancedErrors
|
|
45
45
|
].freeze
|
46
46
|
|
47
47
|
RAILS_SKIP_LIST = [
|
48
|
-
:@new_record,
|
49
|
-
:@attributes,
|
50
48
|
:@association_cache,
|
51
|
-
:@readonly,
|
52
|
-
:@previously_new_record,
|
53
49
|
:@_routes,
|
54
|
-
:@routes,
|
55
50
|
:@app,
|
56
51
|
:@arel_table,
|
57
52
|
:@assertion_instance,
|
@@ -127,7 +122,7 @@ class EnhancedErrors
|
|
127
122
|
mutex.synchronize { @max_capture_length || DEFAULT_MAX_CAPTURE_LENGTH }
|
128
123
|
end
|
129
124
|
|
130
|
-
def max_capture_length=(
|
125
|
+
def max_capture_length=(value)
|
131
126
|
mutex.synchronize { @max_capture_length = value }
|
132
127
|
end
|
133
128
|
|
@@ -140,7 +135,6 @@ class EnhancedErrors
|
|
140
135
|
end
|
141
136
|
end
|
142
137
|
|
143
|
-
|
144
138
|
def reset!
|
145
139
|
mutex.synchronize do
|
146
140
|
@rspec_tracepoint&.disable
|
@@ -155,7 +149,7 @@ class EnhancedErrors
|
|
155
149
|
|
156
150
|
def skip_list
|
157
151
|
mutex.synchronize do
|
158
|
-
@skip_list ||= DEFAULT_SKIP_LIST
|
152
|
+
@skip_list ||= DEFAULT_SKIP_LIST.to_set
|
159
153
|
end
|
160
154
|
end
|
161
155
|
|
@@ -187,12 +181,13 @@ class EnhancedErrors
|
|
187
181
|
|
188
182
|
def add_to_skip_list(*vars)
|
189
183
|
mutex.synchronize do
|
190
|
-
@skip_list.
|
184
|
+
@skip_list.add(*vars)
|
191
185
|
end
|
192
186
|
end
|
193
187
|
|
194
188
|
def enhance_exceptions!(enabled: true, debug: false, capture_events: nil, override_messages: false, **options, &block)
|
195
189
|
mutex.synchronize do
|
190
|
+
ensure_extensions_are_required
|
196
191
|
@exception_trace&.disable
|
197
192
|
@exception_trace = nil
|
198
193
|
|
@@ -223,10 +218,11 @@ class EnhancedErrors
|
|
223
218
|
|
224
219
|
events = @capture_events ? @capture_events.to_a : default_capture_events
|
225
220
|
@exception_trace = TracePoint.new(*events) do |tp|
|
221
|
+
return unless exception_is_handleable?(tp.raised_exception)
|
226
222
|
handle_tracepoint_event(tp)
|
227
223
|
end
|
228
224
|
|
229
|
-
@exception_trace
|
225
|
+
@exception_trace&.enable if @enabled
|
230
226
|
end
|
231
227
|
end
|
232
228
|
|
@@ -235,7 +231,8 @@ class EnhancedErrors
|
|
235
231
|
end
|
236
232
|
|
237
233
|
def start_minitest_binding_capture
|
238
|
-
|
234
|
+
ensure_extensions_are_required
|
235
|
+
Enhanced::ExceptionContext.clear_all
|
239
236
|
@enabled = true if @enabled.nil?
|
240
237
|
return unless @enabled
|
241
238
|
mutex.synchronize do
|
@@ -243,7 +240,7 @@ class EnhancedErrors
|
|
243
240
|
next unless tp.method_id.to_s.start_with?('test_') && is_a_minitest?(tp.defined_class)
|
244
241
|
@minitest_test_binding = tp.binding
|
245
242
|
end
|
246
|
-
@minitest_trace
|
243
|
+
@minitest_trace&.enable
|
247
244
|
end
|
248
245
|
end
|
249
246
|
|
@@ -269,7 +266,8 @@ class EnhancedErrors
|
|
269
266
|
end
|
270
267
|
|
271
268
|
def start_rspec_binding_capture
|
272
|
-
|
269
|
+
ensure_extensions_are_required
|
270
|
+
Enhanced::ExceptionContext.clear_all
|
273
271
|
@enabled = true if @enabled.nil?
|
274
272
|
return unless @enabled
|
275
273
|
|
@@ -277,21 +275,29 @@ class EnhancedErrors
|
|
277
275
|
@rspec_example_binding = nil
|
278
276
|
@capture_next_binding = false
|
279
277
|
@rspec_tracepoint&.disable
|
280
|
-
|
281
278
|
@rspec_tracepoint = TracePoint.new(:raise) do |tp|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
279
|
+
return unless exception_is_handleable?(tp.raised_exception)
|
280
|
+
class_name = tp.raised_exception.class.name
|
281
|
+
case class_name
|
282
|
+
when 'RSpec::Expectations::ExpectationNotMetError'
|
283
|
+
start_rspec_binding_trap
|
284
|
+
else
|
285
|
+
handle_tracepoint_event(tp)
|
289
286
|
end
|
290
287
|
end
|
291
|
-
|
288
|
+
end
|
289
|
+
@rspec_tracepoint&.enable
|
292
290
|
end
|
293
291
|
|
294
|
-
#
|
292
|
+
# Behavior: Grabs the next rspec spec binding that goes by, and stops the more-expensive b_return trace.
|
293
|
+
# This part of RSpec has been stable, since 2015, so although this is kluge-y, it is stable.
|
294
|
+
# The optimization does a 2-3x on spec speed vs. opening up the Tracepoint. With it,
|
295
|
+
# things are pretty close in speed to plain rspec.
|
296
|
+
# Should the behavior change this can be updated by using a trace to print out items
|
297
|
+
# and their local variables then, find the exception or call that goes by right
|
298
|
+
# before the spec blocks with the variables, and use that to narrow-down the costly part of
|
299
|
+
# the probe to just this point in time. The good news is that
|
300
|
+
# this part is test-time only, and this optimization and kluge only applies to RSpec.
|
295
301
|
def start_rspec_binding_trap
|
296
302
|
@rspec_binding_trap = TracePoint.new(:b_return) do |tp|
|
297
303
|
# kluge-y hack and will be a pain to maintain
|
@@ -302,13 +308,12 @@ class EnhancedErrors
|
|
302
308
|
next unless @capture_next_binding
|
303
309
|
@capture_next_binding = false
|
304
310
|
@rspec_example_binding = tp.binding
|
305
|
-
@rspec_binding_trap
|
311
|
+
@rspec_binding_trap&.disable
|
306
312
|
@rspec_binding_trap = nil
|
307
313
|
end
|
308
|
-
@rspec_binding_trap
|
314
|
+
@rspec_binding_trap&.enable
|
309
315
|
end
|
310
316
|
|
311
|
-
|
312
317
|
def stop_rspec_binding_capture
|
313
318
|
mutex.synchronize do
|
314
319
|
@rspec_tracepoint&.disable
|
@@ -327,6 +332,8 @@ class EnhancedErrors
|
|
327
332
|
|
328
333
|
locals = b.local_variables.map { |var| [var, safe_local_variable_get(b, var)] }.to_h
|
329
334
|
receiver = b.receiver
|
335
|
+
return unless safe_to_inspect?(receiver)
|
336
|
+
|
330
337
|
instance_vars = receiver.instance_variables
|
331
338
|
instances = instance_vars.map { |var| [var, safe_instance_variable_get(receiver, var)] }.to_h
|
332
339
|
|
@@ -355,7 +362,7 @@ class EnhancedErrors
|
|
355
362
|
globals: {}
|
356
363
|
},
|
357
364
|
exception: 'NoException',
|
358
|
-
capture_event: '
|
365
|
+
capture_event: 'test_context'
|
359
366
|
}
|
360
367
|
|
361
368
|
default_on_capture(binding_info)
|
@@ -437,11 +444,7 @@ class EnhancedErrors
|
|
437
444
|
env = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
438
445
|
@output_format = case env
|
439
446
|
when 'development', 'test'
|
440
|
-
|
441
|
-
:plaintext
|
442
|
-
else
|
443
|
-
:terminal
|
444
|
-
end
|
447
|
+
running_in_ci? ? :plaintext : :terminal
|
445
448
|
when 'production'
|
446
449
|
:json
|
447
450
|
else
|
@@ -453,28 +456,32 @@ class EnhancedErrors
|
|
453
456
|
def running_in_ci?
|
454
457
|
mutex.synchronize do
|
455
458
|
return @running_in_ci if defined?(@running_in_ci)
|
456
|
-
|
457
459
|
@running_in_ci = CI_ENV_VARS.any? { |_, value| value.to_s.downcase == 'true' }
|
458
460
|
end
|
459
461
|
end
|
460
462
|
|
461
463
|
def apply_skip_list(binding_info)
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
variables[:
|
466
|
-
if @debug
|
467
|
-
variables[:globals]&.reject! { |var, _| skip_list.include?(var) }
|
468
|
-
end
|
464
|
+
binding_info[:variables][:instances]&.reject! { |var, _| skip_list.include?(var) || (var.to_s[0, 2] == '@_' && !@debug) }
|
465
|
+
binding_info[:variables][:locals]&.reject! { |var, _| skip_list.include?(var) }
|
466
|
+
if @debug
|
467
|
+
binding_info[:variables][:globals]&.reject! { |var, _| skip_list.include?(var) }
|
469
468
|
end
|
470
469
|
binding_info
|
471
470
|
end
|
472
471
|
|
473
472
|
def validate_binding_format(binding_info)
|
474
|
-
|
475
|
-
|
473
|
+
binding_info.keys.include?(:capture_event) && binding_info[:variables].is_a?(Hash)
|
474
|
+
end
|
475
|
+
|
476
|
+
# Here, we are detecting BasicObject, which is surprisingly annoying.
|
477
|
+
# We also, importantly, need to detect descendants, as they will also present with a
|
478
|
+
# lack of :respond_to? and any other useful method for us.
|
479
|
+
def safe_to_inspect?(obj)
|
480
|
+
begin
|
481
|
+
obj.class
|
482
|
+
rescue NoMethodError
|
483
|
+
return false
|
476
484
|
end
|
477
|
-
binding_info
|
478
485
|
end
|
479
486
|
|
480
487
|
def binding_info_string(binding_info)
|
@@ -524,7 +531,6 @@ class EnhancedErrors
|
|
524
531
|
|
525
532
|
private
|
526
533
|
|
527
|
-
|
528
534
|
def handle_tracepoint_event(tp)
|
529
535
|
# Check enabled outside the synchronized block for speed, but still safe due to re-check inside.
|
530
536
|
return unless enabled
|
@@ -533,9 +539,13 @@ class EnhancedErrors
|
|
533
539
|
Thread.current[:enhanced_errors_processing] = true
|
534
540
|
exception = tp.raised_exception
|
535
541
|
|
536
|
-
|
537
|
-
|
538
|
-
|
542
|
+
return if exception.frozen?
|
543
|
+
|
544
|
+
capture_me = if @eligible_for_capture
|
545
|
+
@eligible_for_capture.call(exception)
|
546
|
+
else
|
547
|
+
default_eligible_for_capture(exception)
|
548
|
+
end
|
539
549
|
|
540
550
|
unless capture_me
|
541
551
|
Thread.current[:enhanced_errors_processing] = false
|
@@ -544,32 +554,43 @@ class EnhancedErrors
|
|
544
554
|
|
545
555
|
binding_context = tp.binding
|
546
556
|
method_name = tp.method_id
|
557
|
+
receiver = binding_context.receiver
|
558
|
+
|
559
|
+
locals = {}
|
560
|
+
|
561
|
+
binding_context.local_variables.each do |var|
|
562
|
+
locals[var] = safe_local_variable_get(binding_context, var)
|
563
|
+
end
|
564
|
+
|
547
565
|
method_and_args = {
|
548
566
|
object_name: determine_object_name(tp, method_name),
|
549
|
-
args: extract_arguments(tp, method_name)
|
567
|
+
args: extract_arguments(tp, method_name, locals)
|
550
568
|
}
|
551
569
|
|
552
|
-
|
553
|
-
[var, safe_local_variable_get(binding_context, var)]
|
554
|
-
}.to_h
|
570
|
+
instances = {}
|
555
571
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
572
|
+
begin
|
573
|
+
if safe_to_inspect?(receiver)
|
574
|
+
receiver.instance_variables.each { |var|
|
575
|
+
instances[var] = safe_instance_variable_get(receiver, var)
|
576
|
+
}
|
577
|
+
end
|
578
|
+
rescue => e
|
579
|
+
puts "#{e.class.name} #{e.backtrace}"
|
580
|
+
end
|
560
581
|
|
561
582
|
lets = {}
|
562
583
|
|
563
584
|
globals = {}
|
564
585
|
mutex.synchronize do
|
565
586
|
if @debug
|
566
|
-
globals = (global_variables - @original_global_variables.to_a).
|
567
|
-
[var
|
568
|
-
|
587
|
+
globals = (global_variables - @original_global_variables.to_a).each do |var|
|
588
|
+
globals[var] = get_global_variable_value(var)
|
589
|
+
end
|
569
590
|
end
|
570
591
|
end
|
571
592
|
|
572
|
-
capture_event =
|
593
|
+
capture_event = tp.event.to_s
|
573
594
|
location = "#{safe_to_s(tp.path)}:#{safe_to_s(tp.lineno)}"
|
574
595
|
binding_info = {
|
575
596
|
source: location,
|
@@ -583,17 +604,16 @@ class EnhancedErrors
|
|
583
604
|
lets: lets,
|
584
605
|
globals: globals
|
585
606
|
},
|
586
|
-
exception:
|
607
|
+
exception: exception.class.name,
|
587
608
|
capture_event: capture_event
|
588
609
|
}
|
589
610
|
|
590
611
|
binding_info = default_on_capture(binding_info)
|
591
|
-
on_capture_hook_local = mutex.synchronize { @on_capture_hook }
|
592
612
|
|
593
|
-
if
|
613
|
+
if on_capture_hook
|
594
614
|
begin
|
595
615
|
Thread.current[:on_capture] = true
|
596
|
-
binding_info =
|
616
|
+
binding_info = on_capture_hook.call(binding_info)
|
597
617
|
rescue
|
598
618
|
binding_info = nil
|
599
619
|
ensure
|
@@ -601,20 +621,16 @@ class EnhancedErrors
|
|
601
621
|
end
|
602
622
|
end
|
603
623
|
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
exception.binding_infos.delete_at(MAX_BINDING_INFOS / 2.round)
|
608
|
-
end
|
609
|
-
if binding_info
|
610
|
-
exception.binding_infos << binding_info
|
611
|
-
mutex.synchronize do
|
612
|
-
override_exception_message(exception, exception.binding_infos) if @override_messages
|
613
|
-
end
|
614
|
-
end
|
624
|
+
return unless binding_info && validate_binding_format(binding_info)
|
625
|
+
if exception.binding_infos.length >= MAX_BINDING_INFOS
|
626
|
+
exception.binding_infos.delete_at(MAX_BINDING_INFOS / 2.round)
|
615
627
|
end
|
616
|
-
|
617
|
-
|
628
|
+
exception.binding_infos << binding_info
|
629
|
+
mutex.synchronize do
|
630
|
+
override_exception_message(exception, exception.binding_infos) if @override_messages
|
631
|
+
end
|
632
|
+
rescue => e
|
633
|
+
puts "Error: #{e&.class&.name} #{e&.backtrace}"
|
618
634
|
ensure
|
619
635
|
Thread.current[:enhanced_errors_processing] = false
|
620
636
|
end
|
@@ -674,17 +690,15 @@ class EnhancedErrors
|
|
674
690
|
capture_events.is_a?(Array) && capture_events.all? { |ev| [:raise, :rescue].include?(ev) }
|
675
691
|
end
|
676
692
|
|
677
|
-
def extract_arguments(tp, method_name)
|
693
|
+
def extract_arguments(tp, method_name, local_vars_hash)
|
678
694
|
return '' unless method_name
|
679
695
|
begin
|
680
|
-
bind = tp.binding
|
681
696
|
unbound_method = tp.defined_class.instance_method(method_name)
|
682
697
|
method_obj = unbound_method.bind(tp.self)
|
683
698
|
parameters = method_obj.parameters
|
684
|
-
locals = bind.local_variables
|
685
699
|
|
686
700
|
parameters.map do |(_, name)|
|
687
|
-
value =
|
701
|
+
value = local_vars_hash[name]
|
688
702
|
"#{name}=#{safe_inspect(value)}"
|
689
703
|
rescue => e
|
690
704
|
"#{name}=[Error getting argument: #{e.message}]"
|
@@ -696,17 +710,20 @@ class EnhancedErrors
|
|
696
710
|
|
697
711
|
def determine_object_name(tp, method_name = '')
|
698
712
|
begin
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
713
|
+
# Check if we're dealing with a singleton method
|
714
|
+
if (
|
715
|
+
class << tp.self
|
716
|
+
self;
|
717
|
+
end) == tp.defined_class
|
718
|
+
# Singleton method call
|
719
|
+
object_str = safe_to_s(tp.self)
|
720
|
+
method_suffix = method_name.to_s.empty? ? '' : ".#{method_name}"
|
721
|
+
"#{object_str}#{method_suffix}"
|
706
722
|
else
|
707
|
-
|
708
|
-
|
709
|
-
"
|
723
|
+
# Instance method call
|
724
|
+
klass_name = safe_to_s(tp.self.class.name || 'UnknownClass')
|
725
|
+
method_suffix = method_name.to_s.empty? ? '' : "##{method_name}"
|
726
|
+
"#{klass_name}#{method_suffix}"
|
710
727
|
end
|
711
728
|
rescue
|
712
729
|
'[ErrorGettingName]'
|
@@ -798,10 +815,42 @@ class EnhancedErrors
|
|
798
815
|
apply_skip_list(binding_info)
|
799
816
|
end
|
800
817
|
|
818
|
+
# By default, we have filtering for safety, but past that, we capture everything by default
|
819
|
+
# at the moment.
|
801
820
|
def default_eligible_for_capture(exception)
|
802
|
-
|
803
|
-
|
804
|
-
|
821
|
+
true
|
822
|
+
end
|
823
|
+
|
824
|
+
def exception_is_handleable?(exception)
|
825
|
+
case exception
|
826
|
+
when SystemExit, SignalException, SystemStackError, NoMemoryError
|
827
|
+
# Non-actionable: Ignore these exceptions
|
828
|
+
false
|
829
|
+
when SyntaxError, LoadError, ScriptError
|
830
|
+
# Non-actionable: Structural issues so there's no useful runtime context
|
831
|
+
false
|
832
|
+
else
|
833
|
+
# Ignore internal fatal errors
|
834
|
+
exception.class.to_s != 'fatal'
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
# This prevents loading it for say, production, if you don't want to,
|
839
|
+
# and keeps things cleaner. It allows a path to put this behind a feature-flag
|
840
|
+
# or env variable, and dynamically enable some capture instrumentation only
|
841
|
+
# when a Heisenbug is being hunted.
|
842
|
+
def ensure_extensions_are_required
|
843
|
+
mutex.synchronize do
|
844
|
+
return if @loaded_required_extensions
|
845
|
+
require_relative 'enhanced/colors'
|
846
|
+
require_relative 'enhanced/exception'
|
847
|
+
@loaded_required_extensions = true
|
848
|
+
end
|
805
849
|
end
|
850
|
+
|
806
851
|
end
|
807
852
|
end
|
853
|
+
|
854
|
+
module Enhanced
|
855
|
+
|
856
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enhanced_errors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Beland
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
10
|
+
date: 2024-12-25 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: awesome_print
|
@@ -69,24 +68,22 @@ dependencies:
|
|
69
68
|
description: 'EnhancedErrors will automatically enhance your errors with messages
|
70
69
|
containing variable values from the moment they were raised, using no extra dependencies,
|
71
70
|
and only Ruby''s built-in TracePoint. '
|
72
|
-
email:
|
73
71
|
executables: []
|
74
72
|
extensions: []
|
75
73
|
extra_rdoc_files: []
|
76
74
|
files:
|
77
|
-
- ".yardoc/checksums"
|
78
|
-
- ".yardoc/complete"
|
79
|
-
- ".yardoc/object_types"
|
80
|
-
- ".yardoc/objects/root.dat"
|
81
|
-
- ".yardoc/proxy_types"
|
82
75
|
- LICENSE
|
83
76
|
- README.md
|
84
77
|
- benchmark/benchmark.rb
|
85
78
|
- benchmark/memory_bench.rb
|
79
|
+
- benchmark/result.txt
|
86
80
|
- benchmark/stackprofile.rb
|
87
81
|
- doc/Context.html
|
88
82
|
- doc/Enhanced.html
|
89
83
|
- doc/Enhanced/Colors.html
|
84
|
+
- doc/Enhanced/Context.html
|
85
|
+
- doc/Enhanced/ExceptionBindingInfos.html
|
86
|
+
- doc/Enhanced/ExceptionContext.html
|
90
87
|
- doc/Enhanced/Integrations.html
|
91
88
|
- doc/Enhanced/Integrations/RSpecErrorFailureMessage.html
|
92
89
|
- doc/EnhancedErrors.html
|
@@ -117,8 +114,8 @@ files:
|
|
117
114
|
- examples/demo_rspec.rb
|
118
115
|
- lib/enhanced/colors.rb
|
119
116
|
- lib/enhanced/context.rb
|
120
|
-
- lib/enhanced/enhanced_exception_context.rb
|
121
117
|
- lib/enhanced/exception.rb
|
118
|
+
- lib/enhanced/exception_context.rb
|
122
119
|
- lib/enhanced/minitest_patch.rb
|
123
120
|
- lib/enhanced_errors.rb
|
124
121
|
homepage: https://github.com/ericbeland/enhanced_errors
|
@@ -126,7 +123,6 @@ licenses: []
|
|
126
123
|
metadata:
|
127
124
|
homepage_uri: https://github.com/ericbeland/enhanced_errors
|
128
125
|
source_code_uri: https://github.com/ericbeland/enhanced_errors
|
129
|
-
post_install_message:
|
130
126
|
rdoc_options: []
|
131
127
|
require_paths:
|
132
128
|
- lib
|
@@ -141,8 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
137
|
- !ruby/object:Gem::Version
|
142
138
|
version: '0'
|
143
139
|
requirements: []
|
144
|
-
rubygems_version: 3.
|
145
|
-
signing_key:
|
140
|
+
rubygems_version: 3.6.2
|
146
141
|
specification_version: 4
|
147
142
|
summary: Automatically enhance your errors with messages containing variable values
|
148
143
|
from the moment they were raised.
|
data/.yardoc/checksums
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
lib/enhanced/colors.rb ed3b11d00ff9ceed089d4a65f0be5b3fca64bbe6
|
2
|
-
lib/enhanced_errors.rb f79331fea888262a0447d2fff4e5067fdf1418a9
|
3
|
-
lib/enhanced/context.rb 24ca2d1f4ee2ff48dd83c913ad1f1e7c1aa367c4
|
4
|
-
lib/enhanced/exception.rb 5572411e9e32bbe9ed01b98787e1a53a4ab61408
|
5
|
-
lib/enhanced/minitest_patch.rb 3e7fb88ddc37a1f966877735a43ae206ee396bbc
|
6
|
-
lib/enhanced/enhanced_exception_context.rb 23423dbdb33b7961a0b8a297e052ebb2fbe1c6ac
|
data/.yardoc/complete
DELETED
File without changes
|
data/.yardoc/object_types
DELETED
Binary file
|
data/.yardoc/objects/root.dat
DELETED
Binary file
|
data/.yardoc/proxy_types
DELETED
Binary file
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'weakref'
|
2
|
-
|
3
|
-
require_relative 'context'
|
4
|
-
|
5
|
-
require 'weakref'
|
6
|
-
require 'monitor'
|
7
|
-
|
8
|
-
module EnhancedExceptionContext
|
9
|
-
extend self
|
10
|
-
|
11
|
-
REGISTRY = {}
|
12
|
-
MUTEX = Monitor.new
|
13
|
-
|
14
|
-
def store_context(exception, context)
|
15
|
-
MUTEX.synchronize do
|
16
|
-
REGISTRY[exception.object_id] = { weak_exc: WeakRef.new(exception), context: context }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def context_for(exception)
|
21
|
-
MUTEX.synchronize do
|
22
|
-
entry = REGISTRY[exception.object_id]
|
23
|
-
return nil unless entry
|
24
|
-
|
25
|
-
begin
|
26
|
-
_ = entry[:weak_exc].__getobj__ # ensure exception is still alive
|
27
|
-
entry[:context]
|
28
|
-
rescue RefError
|
29
|
-
# Exception no longer alive, clean up
|
30
|
-
REGISTRY.delete(exception.object_id)
|
31
|
-
nil
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def clear_context(exception)
|
37
|
-
MUTEX.synchronize do
|
38
|
-
REGISTRY.delete(exception.object_id)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def clear_all
|
43
|
-
MUTEX.synchronize do
|
44
|
-
REGISTRY.clear
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|