├── Gemfile ├── doc ├── css │ ├── common.css │ ├── full_list.css │ └── style.css ├── images │ ├── enhance.png │ ├── enhanced-spec.png │ └── enhanced-error.png ├── frames.html ├── file_list.html ├── Enhanced │ ├── Integrations.html │ ├── Integrations │ │ └── RSpecErrorFailureMessage.html │ ├── Context.html │ ├── ExceptionBindingInfos.html │ ├── Colors.html │ └── ExceptionContext.html ├── Exception.html ├── Enhanced.html ├── class_list.html ├── top-level-namespace.html ├── _index.html ├── Context.html ├── js │ ├── full_list.js │ └── app.js ├── ExceptionBindingInfos.html ├── Minitest.html ├── EnhancedExceptionContext.html └── method_list.html ├── spec ├── spec_helper.rb └── enhancement_context_spec.rb ├── .gitignore ├── lib └── enhanced │ ├── context.rb │ ├── minitest_patch.rb │ ├── colors.rb │ ├── exception_context.rb │ └── exception.rb ├── examples ├── demo_minitest.rb ├── demo_exception_enhancement.rb └── demo_rspec.rb ├── benchmark ├── result.txt ├── stackprofile.rb ├── memory_bench.rb └── benchmark.rb ├── Gemfile.lock ├── LICENSE ├── enhanced_errors.gemspec └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | -------------------------------------------------------------------------------- /doc/css/common.css: -------------------------------------------------------------------------------- 1 | /* Override this file with custom rules */ -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # spec_helper.rb 2 | require 'rspec' 3 | require 'enhanced_errors' 4 | -------------------------------------------------------------------------------- /doc/images/enhance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericbeland/enhanced_errors/HEAD/doc/images/enhance.png -------------------------------------------------------------------------------- /doc/images/enhanced-spec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericbeland/enhanced_errors/HEAD/doc/images/enhanced-spec.png -------------------------------------------------------------------------------- /doc/images/enhanced-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ericbeland/enhanced_errors/HEAD/doc/images/enhanced-error.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | /.env 3 | /log 4 | /tmp 5 | *.gem 6 | *.swp 7 | .DS_Store 8 | stackprof.dump 9 | .ruby-version 10 | .idea/ 11 | enhanced_errors.iml 12 | .yardoc 13 | 14 | -------------------------------------------------------------------------------- /lib/enhanced/context.rb: -------------------------------------------------------------------------------- 1 | module Enhanced 2 | class Context 3 | attr_accessor :binding_infos 4 | 5 | def initialize 6 | @binding_infos = [] 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /examples/demo_minitest.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require 'enhanced_errors' 3 | require 'enhanced/minitest_patch' 4 | 5 | # You must install minitest and load it first to run this demo. 6 | # EnhancedErrors does NOT ship with minitest as a dependency. 7 | 8 | class MagicBallTest < Minitest::Test 9 | def setup 10 | @foo = 'bar' 11 | end 12 | 13 | def test_boo_capture 14 | bee = 'fee' 15 | assert false 16 | end 17 | 18 | def test_i_raise 19 | zoo = 'zee' 20 | raise "Crud" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /benchmark/result.txt: -------------------------------------------------------------------------------- 1 | Result from MacOs, Ruby 3.3.6 for benchmark.rb 2 | 3 | -------------------------------------------- 4 | 5 | Cost Exploration 6 | user system total real 7 | Baseline 1k (NO EnhancedErrors, tight error raise loop): 0.000386 0.000049 0.000435 ( 0.000434) 8 | 9 | user system total real 10 | Stress 1k EnhancedErrors (Tight error raising loop w/ EnhancedErrors): 0.007653 0.000241 0.007894 ( 0.007894) 11 | Cost per 100 raised exceptions: 0.79 ms 12 | -------------------------------------------------------------------------------- /doc/frames.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Documentation by YARD 0.9.37 6 | 7 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /lib/enhanced/minitest_patch.rb: -------------------------------------------------------------------------------- 1 | module Minitest 2 | class << self 3 | alias_method :original_run_one_method, :run_one_method 4 | 5 | def run_one_method(klass, method_name) 6 | EnhancedErrors.start_minitest_binding_capture 7 | result = original_run_one_method(klass, method_name) 8 | ensure 9 | begin 10 | binding_infos = EnhancedErrors.stop_minitest_binding_capture 11 | EnhancedErrors.override_exception_message(result.failures.last, binding_infos) if result.failures.any? 12 | Enhanced::ExceptionContext.clear_all 13 | rescue => e 14 | puts "Ignored error during error enhancement: #{e}" 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/enhanced/colors.rb: -------------------------------------------------------------------------------- 1 | module Enhanced 2 | class Colors 3 | COLORS = { red: 31, green: 32, yellow: 33, blue: 34, purple: 35, cyan: 36, white: 0 }.freeze 4 | RESET_CODE = "\e[0m".freeze 5 | 6 | class << self 7 | def enabled? 8 | @enabled 9 | end 10 | 11 | def enabled=(value) 12 | @enabled = value 13 | end 14 | 15 | def color(num, string) 16 | return string unless @enabled 17 | "#{code(num)}#{string}#{RESET_CODE}" 18 | end 19 | 20 | def code(num) 21 | "\e[#{num}m".freeze 22 | end 23 | 24 | COLORS.each do |color, code| 25 | define_method(color) do |str| 26 | color(COLORS[color], str) 27 | end 28 | end 29 | end 30 | end 31 | 32 | end -------------------------------------------------------------------------------- /benchmark/stackprofile.rb: -------------------------------------------------------------------------------- 1 | require 'stackprof' 2 | require_relative '../lib/enhanced/enhanced' # Adjust the path if necessary 3 | 4 | # gem install stackprof 5 | 6 | # adjust path as needed 7 | # ruby ./lib/core_ext/enhanced/benchmark/stackprofile.rb 8 | # dumps to current folder. read the stackprof dump: 9 | # stackprof stackprof.dump 10 | 11 | # Define the number of iterations 12 | ITERATIONS = 10_000 13 | 14 | def run_with_enhanced_errors 15 | EnhancedErrors.enhance_exceptions!(debug: false, override_messages: true) 16 | ITERATIONS.times do 17 | begin 18 | raise 'Test exception with EnhancedErrors' 19 | rescue => _e 20 | # Exception handled with EnhancedErrors. 21 | end 22 | end 23 | end 24 | 25 | def stackprofile 26 | StackProf.run(mode: :wall, out: 'stackprof.dump') do 27 | run_with_enhanced_errors 28 | end 29 | end 30 | 31 | stackprofile 32 | -------------------------------------------------------------------------------- /examples/demo_exception_enhancement.rb: -------------------------------------------------------------------------------- 1 | require './lib/enhanced_errors' 2 | require 'awesome_print' # Optional, for better output 3 | 4 | # Demonstrates enhancing exceptions in normal day to day Ruby usage 5 | # from this folder: ruby demo_exception_enhancement.rb 6 | 7 | EnhancedErrors.enhance_exceptions!(override_messages: true, capture_events: [:raise, :rescue]) 8 | 9 | def foo 10 | begin 11 | myvar = 0 12 | @myinstance = 10 13 | foo = @myinstance / myvar 14 | rescue => e 15 | puts e.message 16 | end 17 | end 18 | 19 | def baz 20 | i.dontexist 21 | end 22 | 23 | def boo 24 | seeme = 'youshould' 25 | baz 26 | rescue Exception => e 27 | puts e.message 28 | end 29 | 30 | puts "\n--- Example with raise ---\n\n\n" 31 | 32 | foo 33 | 34 | puts "\n--- Example with raise and rescue (requires ruby 3.2 or greater to see rescue) ---\n\n\n" 35 | 36 | boo 37 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | enhanced_errors (3.0.7) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | awesome_print (1.9.2) 10 | diff-lcs (1.5.1) 11 | minitest (5.25.4) 12 | rspec (3.13.0) 13 | rspec-core (~> 3.13.0) 14 | rspec-expectations (~> 3.13.0) 15 | rspec-mocks (~> 3.13.0) 16 | rspec-core (3.13.2) 17 | rspec-support (~> 3.13.0) 18 | rspec-expectations (3.13.3) 19 | diff-lcs (>= 1.2.0, < 2.0) 20 | rspec-support (~> 3.13.0) 21 | rspec-mocks (3.13.2) 22 | diff-lcs (>= 1.2.0, < 2.0) 23 | rspec-support (~> 3.13.0) 24 | rspec-support (3.13.2) 25 | yard (0.9.37) 26 | 27 | PLATFORMS 28 | ruby 29 | 30 | DEPENDENCIES 31 | awesome_print (~> 1.0) 32 | enhanced_errors! 33 | minitest 34 | rspec (~> 3.0) 35 | yard (~> 0.9) 36 | 37 | BUNDLED WITH 38 | 2.4.19 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2024 Eric Beland 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/enhanced/exception_context.rb: -------------------------------------------------------------------------------- 1 | require 'weakref' 2 | 3 | require_relative 'context' 4 | 5 | require 'weakref' 6 | require 'monitor' 7 | 8 | module Enhanced 9 | module ExceptionContext 10 | extend self 11 | 12 | REGISTRY = {} 13 | MUTEX = Monitor.new 14 | 15 | def store_context(exception, context) 16 | MUTEX.synchronize do 17 | REGISTRY[exception.object_id] = { weak_exc: WeakRef.new(exception), context: context } 18 | end 19 | end 20 | 21 | def context_for(exception) 22 | MUTEX.synchronize do 23 | entry = REGISTRY[exception.object_id] 24 | return nil unless entry 25 | 26 | begin 27 | _ = entry[:weak_exc].__getobj__ # ensure exception is still alive 28 | entry[:context] 29 | rescue RefError 30 | # Exception no longer alive, clean up 31 | REGISTRY.delete(exception.object_id) 32 | nil 33 | end 34 | end 35 | end 36 | 37 | def clear_context(exception) 38 | MUTEX.synchronize do 39 | REGISTRY.delete(exception.object_id) 40 | end 41 | end 42 | 43 | def clear_all 44 | MUTEX.synchronize do 45 | REGISTRY.clear 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /examples/demo_rspec.rb: -------------------------------------------------------------------------------- 1 | # spec/enhanced_errors_spec.rb 2 | 3 | # INSTRUCTIONS: Install rspec 4 | # gem install rspec 5 | # rspec examples/example_spec.rb 6 | 7 | require 'rspec' 8 | require_relative '../lib/enhanced_errors' 9 | 10 | RSpec.configure do |config| 11 | 12 | # -- Add to your RSPec config in your spec_helper. 13 | config.before(:example) do |_example| 14 | EnhancedErrors.start_rspec_binding_capture 15 | end 16 | 17 | config.after(:example) do |example| 18 | EnhancedErrors.override_rspec_message(example, EnhancedErrors.stop_rspec_binding_capture) 19 | end 20 | # -- End EnhancedErrors config 21 | 22 | end 23 | 24 | 25 | RSpec.describe 'Neo' do 26 | describe 'sees through' do 27 | let(:the_matrix) { 'code rains, dramatically' } 28 | 29 | before(:each) do 30 | @spoon = 'there is no spoon' 31 | end 32 | 33 | it 'the matrix' do 34 | #activate memoized item 35 | the_matrix 36 | stop = 'bullets' 37 | raise 'No!' 38 | end 39 | 40 | it "dodges multiple exception-bullets at once" do 41 | foo = 'bar' 42 | expect(1).to eq(2) 43 | expect(true).to eq(false) 44 | end 45 | 46 | after(:each) do 47 | raise "This is another error" 48 | end 49 | 50 | end 51 | end 52 | 53 | -------------------------------------------------------------------------------- /enhanced_errors.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |spec| 2 | spec.name = "enhanced_errors" 3 | spec.version = "3.0.7" 4 | spec.authors = ["Eric Beland"] 5 | 6 | spec.summary = "Automatically enhance your errors with messages containing variable values from the moment they were raised." 7 | spec.description = "EnhancedErrors will automatically enhance your errors with messages containing variable values from the moment they were raised, using no extra dependencies, and only Ruby's built-in TracePoint. " 8 | spec.homepage = "https://github.com/ericbeland/enhanced_errors" 9 | spec.required_ruby_version = ">= 3.0.0" 10 | 11 | spec.metadata["homepage_uri"] = spec.homepage 12 | spec.metadata["source_code_uri"] = "https://github.com/ericbeland/enhanced_errors" 13 | 14 | # Specify which files should be added to the gem when it is released. 15 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 16 | spec.files = Dir.chdir(__dir__) do 17 | `git ls-files -z`.split("\x0").reject do |f| 18 | (File.expand_path(f) == __FILE__) || 19 | f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile]) 20 | end 21 | end 22 | spec.require_paths = ["lib"] 23 | 24 | # For development on this gem 25 | spec.add_development_dependency "awesome_print", "~> 1.0" 26 | spec.add_development_dependency "rspec", "~> 3.0" 27 | spec.add_development_dependency 'yard', '~> 0.9' 28 | spec.add_development_dependency 'minitest' 29 | 30 | end 31 | -------------------------------------------------------------------------------- /lib/enhanced/exception.rb: -------------------------------------------------------------------------------- 1 | # exception.rb 2 | require_relative 'exception_context' 3 | 4 | module Enhanced 5 | module ExceptionBindingInfos 6 | def binding_infos 7 | ctx = Enhanced::ExceptionContext.context_for(self) 8 | unless ctx 9 | ctx = Context.new 10 | Enhanced::ExceptionContext.store_context(self, ctx) 11 | end 12 | ctx.binding_infos 13 | end 14 | 15 | def captured_variables 16 | return '' unless binding_infos&.any? 17 | bindings_of_interest = select_binding_infos 18 | EnhancedErrors.format(bindings_of_interest) 19 | rescue 20 | '' 21 | end 22 | 23 | private 24 | 25 | def select_binding_infos 26 | # Preference: 27 | # 1. First 'raise' binding that isn't from a library (gem). 28 | # 2. If none, the first binding. 29 | # 3. The last 'rescue' binding if available. 30 | 31 | bindings_of_interest = [] 32 | 33 | first_app_raise = binding_infos.find do |info| 34 | info[:capture_event] == 'raise' && !info[:library] 35 | end 36 | bindings_of_interest << first_app_raise if first_app_raise 37 | 38 | if bindings_of_interest.empty? && binding_infos.first 39 | bindings_of_interest << binding_infos.first 40 | end 41 | 42 | last_rescue = binding_infos.reverse.find do |info| 43 | info[:capture_event] == 'rescue' 44 | end 45 | bindings_of_interest << last_rescue if last_rescue 46 | 47 | bindings_of_interest.compact 48 | end 49 | end 50 | end 51 | 52 | class Exception 53 | prepend Enhanced::ExceptionBindingInfos 54 | end 55 | -------------------------------------------------------------------------------- /spec/enhancement_context_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/enhanced_errors' 2 | require_relative '../lib/enhanced/exception' 3 | 4 | RSpec.describe Enhanced::ExceptionContext do 5 | 6 | let(:exception) { StandardError.new("Test exception") } 7 | 8 | 9 | before do 10 | Enhanced::ExceptionContext.clear_all 11 | end 12 | 13 | it 'stores context for an exception' do 14 | ctx = Enhanced::Context.new 15 | ctx.binding_infos << { detail: "Some info" } 16 | Enhanced::ExceptionContext.store_context(exception, ctx) 17 | 18 | retrieved_ctx = Enhanced::ExceptionContext.context_for(exception) 19 | expect(retrieved_ctx).to eq(ctx) 20 | expect(retrieved_ctx.binding_infos).to eq([{ detail: "Some info" }]) 21 | end 22 | 23 | it 'clears context for a single exception' do 24 | ctx = Enhanced::Context.new 25 | ctx.binding_infos << { detail: "Some info" } 26 | Enhanced::ExceptionContext.store_context(exception, ctx) 27 | 28 | Enhanced::ExceptionContext.clear_context(exception) 29 | expect(Enhanced::ExceptionContext.context_for(exception)).to be_nil 30 | end 31 | 32 | it 'clears all stored contexts' do 33 | exc1 = StandardError.new("Exc1") 34 | exc2 = StandardError.new("Exc2") 35 | 36 | ctx1 = Enhanced::Context.new 37 | ctx1.binding_infos << { info: "Exc1 info" } 38 | Enhanced::ExceptionContext.store_context(exc1, ctx1) 39 | 40 | ctx2 = Enhanced::Context.new 41 | ctx2.binding_infos << { info: "Exc2 info" } 42 | Enhanced::ExceptionContext.store_context(exc2, ctx2) 43 | 44 | Enhanced::ExceptionContext.clear_all 45 | 46 | expect(Enhanced::ExceptionContext.context_for(exc1)).to be_nil 47 | expect(Enhanced::ExceptionContext.context_for(exc2)).to be_nil 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /doc/file_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | File List 19 | 20 | 21 | 22 |
23 |
24 |

File List

25 |
26 | 27 | 28 | Classes 29 | 30 | 31 | 32 | Methods 33 | 34 | 35 | 36 | Files 37 | 38 | 39 |
40 | 41 | 45 |
46 | 47 | 57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /benchmark/memory_bench.rb: -------------------------------------------------------------------------------- 1 | # benchmark_tracepoint.rb 2 | 3 | def memory_usage 4 | pid = Process.pid 5 | `ps -o rss= -p #{pid}`.to_i # Returns memory usage in KB 6 | end 7 | 8 | # Generate a 100 MB string outside the iterations 9 | large_string = 'a' * 10_000_000 # 10 million characters 10 | 11 | def test_with_tracepoint(iterations, large_string) 12 | puts "\nTest with TracePoint capturing bindings:" 13 | captured_bindings = [] 14 | 15 | trace = TracePoint.new(:raise) do |tp| 16 | captured_bindings << tp.binding 17 | end 18 | 19 | trace.enable 20 | 21 | puts "Memory usage before exceptions: #{memory_usage} KB" 22 | 23 | iterations.times do 24 | begin 25 | # Use the large string within the local scope 26 | local_large_string = large_string 27 | raise 'Test exception' 28 | rescue => e 29 | raise e rescue nil # Suppress the exception to continue the loop 30 | end 31 | end 32 | 33 | puts "Memory usage after exceptions: #{memory_usage} KB" 34 | 35 | trace.disable 36 | end 37 | 38 | def test_without_tracepoint(iterations, large_string) 39 | puts "\nTest without TracePoint capturing bindings:" 40 | 41 | puts "Memory usage before exceptions: #{memory_usage} KB" 42 | 43 | iterations.times do 44 | begin 45 | # Use the large string within the local scope 46 | local_large_string = large_string 47 | raise 'Test exception' 48 | rescue => e 49 | raise e rescue nil 50 | end 51 | end 52 | 53 | puts "Memory usage after exceptions: #{memory_usage} KB" 54 | end 55 | 56 | def test_exception_with_large_variable(iterations, large_string) 57 | puts "\nTest with exceptions storing large variable:" 58 | 59 | puts "Memory usage before exceptions: #{memory_usage} KB" 60 | 61 | iterations.times do 62 | begin 63 | raise 'Test exception' 64 | rescue => e 65 | # Store a reference to the large string in the exception 66 | e.instance_variable_set(:@large_string, large_string) 67 | raise e rescue nil 68 | end 69 | end 70 | 71 | puts "Memory usage after exceptions: #{memory_usage} KB" 72 | end 73 | 74 | iterations = 10000 # Adjust iterations as needed 75 | 76 | test_with_tracepoint(iterations, large_string) 77 | test_without_tracepoint(iterations, large_string) 78 | test_exception_with_large_variable(iterations, large_string) 79 | -------------------------------------------------------------------------------- /benchmark/benchmark.rb: -------------------------------------------------------------------------------- 1 | require 'benchmark' 2 | require_relative '../lib/enhanced_errors' # Adjust the path if necessary 3 | 4 | #require 'profile' 5 | 6 | # In general, the results of this are--catching exception bindings is pretty cheap. 7 | # However, catching :call, :return, :b_return, :b_call are 100x more expensive. 8 | # Makes sense if you think about the relative frequency of things. 9 | 10 | 11 | # Define the number of iterations 12 | ITERATIONS = 1_000 13 | EXCEPTIONS_PER_BATCH = 100 14 | 15 | class Boo < StandardError; end 16 | 17 | def calculate_cost(time_in_seconds) 18 | milliseconds = time_in_seconds * 1000 19 | (milliseconds / (ITERATIONS / EXCEPTIONS_PER_BATCH)).round(2) 20 | end 21 | 22 | 23 | def raise_errors 24 | ITERATIONS.times do 25 | begin 26 | foo = 'bar' 27 | @boo = 'baz' 28 | raise 'Test exception without EnhancedErrors' 29 | rescue => e 30 | e.message 31 | end 32 | end 33 | end 34 | 35 | def when_capture_only_regexp_matched 36 | rxp = /Boo/ 37 | EnhancedErrors.enhance_exceptions!(debug: false) do 38 | eligible_for_capture { |exception| !!rxp.match(exception.class.to_s) } 39 | end 40 | 41 | ITERATIONS.times do 42 | begin 43 | foo = 'bar' 44 | @boo = 'baz' 45 | raise Boo.new('Test exception with EnhancedErrors') 46 | rescue => e 47 | e.message 48 | end 49 | end 50 | end 51 | 52 | def when_capture_only_regexp_did_not_match 53 | rxp = /Baz/ 54 | EnhancedErrors.enhance_exceptions!(override_messages: true) do 55 | eligible_for_capture { |exception| !!rxp.match(exception.class.to_s) } 56 | end 57 | 58 | ITERATIONS.times do 59 | begin 60 | foo = 'bar' 61 | @boo = 'baz' 62 | raise Boo.new('Test exception with EnhancedErrors') 63 | rescue => e 64 | e.message 65 | end 66 | end 67 | end 68 | 69 | puts "Cost Exploration\n" 70 | Benchmark.bm(35) do |x| 71 | without_time = x.report('Baseline 1k (NO EnhancedErrors, tight error raise loop):') { raise_errors } 72 | end 73 | 74 | puts "\n" 75 | EnhancedErrors.enhance_exceptions!(debug: false) 76 | 77 | Benchmark.bm(35) do |x| 78 | with_time = x.report('Stress 1k EnhancedErrors (Tight error raising loop w/ EnhancedErrors):') { raise_errors } 79 | puts "Cost per 100 raised exceptions: #{calculate_cost(with_time.real)} ms" 80 | end 81 | 82 | 83 | # puts "\nProof that if you only match the classes you care about, the cost is nominal\n" 84 | # Benchmark.bm(35) do |x| 85 | # matched_time = x.report('1k capture_only_regexp match (same as always-on) :') { when_capture_only_regexp_matched } 86 | # not_matched_time = x.report('1k capture_only_regexp not matching (low-cost):') { when_capture_only_regexp_did_not_match } 87 | # 88 | # puts "\nCost per 100 exceptions (Capture Only Match): #{calculate_cost(matched_time.real)} ms" 89 | # puts "Cost per 100 exceptions (No Match): #{calculate_cost(not_matched_time.real)} ms" 90 | # end 91 | -------------------------------------------------------------------------------- /doc/Enhanced/Integrations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Enhanced::Integrations 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Enhanced::Integrations 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/enhanced/integrations/rspec_error_failure_message.rb
82 |
83 | 84 |
85 | 86 |

Defined Under Namespace

87 |

88 | 89 | 90 | Modules: RSpecErrorFailureMessage 91 | 92 | 93 | 94 | 95 |

96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 | 107 | 112 | 113 |
114 | 115 | -------------------------------------------------------------------------------- /doc/Exception.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Exception: Exception 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Exception: Exception 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 |
75 |
Includes:
76 |
Enhanced::ExceptionBindingInfos
77 |
78 | 79 | 80 | 81 | 82 | 83 | 84 |
85 |
Defined in:
86 |
lib/enhanced/exception.rb
87 |
88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |

Method Summary

106 | 107 |

Methods included from Enhanced::ExceptionBindingInfos

108 |

#binding_infos, #captured_variables

109 | 110 | 111 |
112 | 113 | 118 | 119 |
120 | 121 | -------------------------------------------------------------------------------- /doc/Enhanced.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Enhanced 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Enhanced 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 |
Included in:
79 |
EnhancedErrors
80 |
81 | 82 | 83 | 84 |
85 |
Defined in:
86 |
lib/enhanced/colors.rb,
87 | lib/enhanced_errors.rb,
lib/enhanced/context.rb,
lib/enhanced/exception.rb,
lib/enhanced/exception_context.rb
88 |
89 |
90 | 91 |
92 | 93 |

Defined Under Namespace

94 |

95 | 96 | 97 | Modules: ExceptionBindingInfos, ExceptionContext 98 | 99 | 100 | 101 | Classes: Colors, Context 102 | 103 | 104 |

105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
115 | 116 | 121 | 122 |
123 | 124 | -------------------------------------------------------------------------------- /doc/class_list.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Class List 19 | 20 | 21 | 22 |
23 |
24 |

Class List

25 |
26 | 27 | 28 | Classes 29 | 30 | 31 | 32 | Methods 33 | 34 | 35 | 36 | Files 37 | 38 | 39 |
40 | 41 | 45 |
46 | 47 | 52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/top-level-namespace.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Top Level Namespace 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Top Level Namespace 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 | 81 |

Defined Under Namespace

82 |

83 | 84 | 85 | Modules: Enhanced, Minitest 86 | 87 | 88 | 89 | Classes: EnhancedErrors, Exception 90 | 91 | 92 |

93 | 94 | 95 |

96 | Constant Summary 97 | collapse 98 |

99 | 100 |
101 | 102 |
IGNORED_EXCEPTIONS = 103 |
104 |
105 | 106 |

Exceptions we could handle but overlook for other reasons. These class constants are not always loaded and generally are only be available when ‘required`, so we detect them by strings.

107 | 108 | 109 |
110 |
111 |
112 | 113 | 114 |
115 |
116 |
%w[JSON::ParserError Zlib::Error OpenSSL::SSL::SSLError Psych::BadAlias]
117 | 118 |
IGNORED_RSPEC_EXCEPTIONS = 119 | 120 |
121 |
%w[RSpec::Expectations::ExpectationNotMetError RSpec::Matchers::BuiltIn::RaiseError]
122 | 123 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 | 136 | 141 | 142 |
143 | 144 | -------------------------------------------------------------------------------- /doc/_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Documentation by YARD 0.9.37 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 |
34 | 54 | 55 |

Documentation by YARD 0.9.37

56 |
57 |

Alphabetic Index

58 | 59 |

File Listing

60 |
    61 | 62 | 63 |
  • README
  • 64 | 65 | 66 |
67 | 68 |
69 |

Namespace Listing A-Z

70 | 71 | 72 | 73 | 74 | 75 | 76 | 151 | 152 |
77 | 78 | 79 |
    80 |
  • C
  • 81 |
      82 | 83 |
    • 84 | Colors 85 | 86 | (Enhanced) 87 | 88 |
    • 89 | 90 |
    • 91 | Context 92 | 93 | (Enhanced) 94 | 95 |
    • 96 | 97 |
    98 |
99 | 100 | 101 | 136 | 137 | 138 |
    139 |
  • M
  • 140 |
      141 | 142 |
    • 143 | Minitest 144 | 145 |
    • 146 | 147 |
    148 |
149 | 150 |
153 | 154 |
155 | 156 |
157 | 158 | 163 | 164 |
165 | 166 | -------------------------------------------------------------------------------- /doc/css/full_list.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif; 4 | font-size: 13px; 5 | height: 101%; 6 | overflow-x: hidden; 7 | background: #fafafa; 8 | } 9 | 10 | h1 { padding: 12px 10px; padding-bottom: 0; margin: 0; font-size: 1.4em; } 11 | .clear { clear: both; } 12 | .fixed_header { position: fixed; background: #fff; width: 100%; padding-bottom: 10px; margin-top: 0; top: 0; z-index: 9999; height: 70px; } 13 | #search { position: absolute; right: 5px; top: 9px; padding-left: 24px; } 14 | #content.insearch #search, #content.insearch #noresults { background: url(data:image/gif;base64,R0lGODlhEAAQAPYAAP///wAAAPr6+pKSkoiIiO7u7sjIyNjY2J6engAAAI6OjsbGxjIyMlJSUuzs7KamppSUlPLy8oKCghwcHLKysqSkpJqamvT09Pj4+KioqM7OzkRERAwMDGBgYN7e3ujo6Ly8vCoqKjY2NkZGRtTU1MTExDw8PE5OTj4+PkhISNDQ0MrKylpaWrS0tOrq6nBwcKysrLi4uLq6ul5eXlxcXGJiYoaGhuDg4H5+fvz8/KKiohgYGCwsLFZWVgQEBFBQUMzMzDg4OFhYWBoaGvDw8NbW1pycnOLi4ubm5kBAQKqqqiQkJCAgIK6urnJyckpKSjQ0NGpqatLS0sDAwCYmJnx8fEJCQlRUVAoKCggICLCwsOTk5ExMTPb29ra2tmZmZmhoaNzc3KCgoBISEiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCAAAACwAAAAAEAAQAAAHaIAAgoMgIiYlg4kACxIaACEJCSiKggYMCRselwkpghGJBJEcFgsjJyoAGBmfggcNEx0flBiKDhQFlIoCCA+5lAORFb4AJIihCRbDxQAFChAXw9HSqb60iREZ1omqrIPdJCTe0SWI09GBACH5BAkIAAAALAAAAAAQABAAAAdrgACCgwc0NTeDiYozCQkvOTo9GTmDKy8aFy+NOBA7CTswgywJDTIuEjYFIY0JNYMtKTEFiRU8Pjwygy4ws4owPyCKwsMAJSTEgiQlgsbIAMrO0dKDGMTViREZ14kYGRGK38nHguHEJcvTyIEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDAggPg4iJAAMJCRUAJRIqiRGCBI0WQEEJJkWDERkYAAUKEBc4Po1GiKKJHkJDNEeKig4URLS0ICImJZAkuQAhjSi/wQyNKcGDCyMnk8u5rYrTgqDVghgZlYjcACTA1sslvtHRgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCQARAtOUoQRGRiFD0kJUYWZhUhKT1OLhR8wBaaFBzQ1NwAlkIszCQkvsbOHL7Y4q4IuEjaqq0ZQD5+GEEsJTDCMmIUhtgk1lo6QFUwJVDKLiYJNUd6/hoEAIfkECQgAAAAsAAAAABAAEAAAB2iAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4uen4ICCA+IkIsDCQkVACWmhwSpFqAABQoQF6ALTkWFnYMrVlhWvIKTlSAiJiVVPqlGhJkhqShHV1lCW4cMqSkAR1ofiwsjJyqGgQAh+QQJCAAAACwAAAAAEAAQAAAHZ4AAgoOEhYaCJSWHgxGDJCSMhREZGIYYGY2ElYebi56fhyWQniSKAKKfpaCLFlAPhl0gXYNGEwkhGYREUywag1wJwSkHNDU3D0kJYIMZQwk8MjPBLx9eXwuETVEyAC/BOKsuEjYFhoEAIfkECQgAAAAsAAAAABAAEAAAB2eAAIKDhIWGgiUlh4MRgyQkjIURGRiGGBmNhJWHm4ueICImip6CIQkJKJ4kigynKaqKCyMnKqSEK05StgAGQRxPYZaENqccFgIID4KXmQBhXFkzDgOnFYLNgltaSAAEpxa7BQoQF4aBACH5BAkIAAAALAAAAAAQABAAAAdogACCg4SFggJiPUqCJSWGgkZjCUwZACQkgxGEXAmdT4UYGZqCGWQ+IjKGGIUwPzGPhAc0NTewhDOdL7Ykji+dOLuOLhI2BbaFETICx4MlQitdqoUsCQ2vhKGjglNfU0SWmILaj43M5oEAOwAAAAAAAAAAAA==) no-repeat center left; } 15 | #full_list { padding: 0; list-style: none; margin-left: 0; margin-top: 80px; font-size: 1.1em; } 16 | #full_list ul { padding: 0; } 17 | #full_list li { padding: 0; margin: 0; list-style: none; } 18 | #full_list li .item { padding: 5px 5px 5px 12px; } 19 | #noresults { padding: 7px 12px; background: #fff; } 20 | #content.insearch #noresults { margin-left: 7px; } 21 | li.collapsed ul { display: none; } 22 | li a.toggle { cursor: default; position: relative; left: -5px; top: 4px; text-indent: -999px; width: 10px; height: 9px; margin-left: -10px; display: block; float: left; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK8AAACvABQqw0mAAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTM5jWRgMAAAAVdEVYdENyZWF0aW9uIFRpbWUAMy8xNC8wOeNZPpQAAAE2SURBVDiNrZTBccIwEEXfelIAHUA6CZ24BGaWO+FuzZAK4k6gg5QAdGAq+Bxs2Yqx7BzyL7Llp/VfzZeQhCTc/ezuGzKKnKSzpCxXJM8fwNXda3df5RZETlIt6YUzSQDs93sl8w3wBZxCCE10GM1OcWbWjB2mWgEH4Mfdyxm3PSepBHibgQE2wLe7r4HjEidpnXMYdQPKEMJcsZ4zs2POYQOcaPfwMVOo58zsAdMt18BuoVDPxUJRacELbXv3hUIX2vYmOUvi8C8ydz/ThjXrqKqqLbDIAdsCKBd+Wo7GWa7o9qzOQHVVVXeAbs+yHHCH4aTsaCOQqunmUy1yBUAXkdMIfMlgF5EXLo2OpV/c/Up7jG4hhHcYLgWzAZXUc2b2ixsfvc/RmNNfOXD3Q/oeL9axJE1yT9IOoUu6MGUkAAAAAElFTkSuQmCC) no-repeat bottom left; } 23 | li.collapsed a.toggle { cursor: default; background-position: top left; } 24 | li { color: #666; cursor: pointer; } 25 | li.deprecated { text-decoration: line-through; font-style: italic; } 26 | li.odd { background: #f0f0f0; } 27 | li.even { background: #fafafa; } 28 | .item:hover { background: #ddd; } 29 | li small:before { content: "("; } 30 | li small:after { content: ")"; } 31 | li small.search_info { display: none; } 32 | a, a:visited { text-decoration: none; color: #05a; } 33 | li.clicked > .item { background: #05a; color: #ccc; } 34 | li.clicked > .item a, li.clicked > .item a:visited { color: #eee; } 35 | li.clicked > .item a.toggle { opacity: 0.5; background-position: bottom right; } 36 | li.collapsed.clicked a.toggle { background-position: top right; } 37 | #search input { border: 1px solid #bbb; border-radius: 3px; } 38 | #full_list_nav { margin-left: 10px; font-size: 0.9em; display: block; color: #aaa; } 39 | #full_list_nav a, #nav a:visited { color: #358; } 40 | #full_list_nav a:hover { background: transparent; color: #5af; } 41 | #full_list_nav span:after { content: ' | '; } 42 | #full_list_nav span:last-child:after { content: ''; } 43 | 44 | #content h1 { margin-top: 0; } 45 | li { white-space: nowrap; cursor: normal; } 46 | li small { display: block; font-size: 0.8em; } 47 | li small:before { content: ""; } 48 | li small:after { content: ""; } 49 | li small.search_info { display: none; } 50 | #search { width: 170px; position: static; margin: 3px; margin-left: 10px; font-size: 0.9em; color: #666; padding-left: 0; padding-right: 24px; } 51 | #content.insearch #search { background-position: center right; } 52 | #search input { width: 110px; } 53 | 54 | #full_list.insearch ul { display: block; } 55 | #full_list.insearch .item { display: none; } 56 | #full_list.insearch .found { display: block; padding-left: 11px !important; } 57 | #full_list.insearch li a.toggle { display: none; } 58 | #full_list.insearch li small.search_info { display: block; } 59 | -------------------------------------------------------------------------------- /doc/Enhanced/Integrations/RSpecErrorFailureMessage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Enhanced::Integrations::RSpecErrorFailureMessage 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Enhanced::Integrations::RSpecErrorFailureMessage 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/enhanced/integrations/rspec_error_failure_message.rb
82 |
83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |

95 | Instance Method Summary 96 | collapse 97 |

98 | 99 | 124 | 125 | 126 | 127 | 128 |
129 |

Instance Method Details

130 | 131 | 132 |
133 |

134 | 135 | #execution_resultObject 136 | 137 | 138 | 139 | 140 | 141 |

142 | 143 | 155 | 166 | 167 |
144 |
145 | 
146 | 
147 | 4
148 | 5
149 | 6
150 | 7
151 | 8
152 | 9
153 | 10
154 |
156 |
# File 'lib/enhanced/integrations/rspec_error_failure_message.rb', line 4
157 | 
158 | def execution_result
159 |   result = super
160 |   if result.exception
161 |     EnhancedErrors.override_exception_message(result.exception, self.[:expect_binding])
162 |   end
163 |   result
164 | end
165 |
168 |
169 | 170 |
171 | 172 |
173 | 174 | 179 | 180 |
181 | 182 | -------------------------------------------------------------------------------- /doc/Context.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Context 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Context 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/enhanced/context.rb
98 |
99 | 100 |
101 | 102 | 103 | 104 | 105 | 106 |

Instance Attribute Summary collapse

107 | 137 | 138 | 139 | 140 | 141 | 142 |

143 | Instance Method Summary 144 | collapse 145 |

146 | 147 | 176 | 177 | 178 |
179 |

Constructor Details

180 | 181 |
182 |

183 | 184 | #initializeContext 185 | 186 | 187 | 188 | 189 | 190 |

191 |
192 | 193 |

Returns a new instance of Context.

194 | 195 | 196 |
197 |
198 |
199 | 200 | 201 |
202 | 203 | 211 | 218 | 219 |
204 |
205 | 
206 | 
207 | 4
208 | 5
209 | 6
210 |
212 |
# File 'lib/enhanced/context.rb', line 4
213 | 
214 | def initialize
215 |   @binding_infos = []
216 | end
217 |
220 |
221 | 222 |
223 | 224 |
225 |

Instance Attribute Details

226 | 227 | 228 | 229 |
230 |

231 | 232 | #binding_infosObject 233 | 234 | 235 | 236 | 237 | 238 |

239 |
240 | 241 |

Returns the value of attribute binding_infos.

242 | 243 | 244 |
245 |
246 |
247 | 248 | 249 |
250 | 251 | 259 | 266 | 267 |
252 |
253 | 
254 | 
255 | 2
256 | 3
257 | 4
258 |
260 |
# File 'lib/enhanced/context.rb', line 2
261 | 
262 | def binding_infos
263 |   @binding_infos
264 | end
265 |
268 |
269 | 270 |
271 | 272 | 273 |
274 | 275 | 280 | 281 |
282 | 283 | -------------------------------------------------------------------------------- /doc/Enhanced/Context.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Enhanced::Context 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Enhanced::Context 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/enhanced/context.rb
98 |
99 | 100 |
101 | 102 | 103 | 104 | 105 | 106 |

Instance Attribute Summary collapse

107 | 137 | 138 | 139 | 140 | 141 | 142 |

143 | Instance Method Summary 144 | collapse 145 |

146 | 147 | 176 | 177 | 178 |
179 |

Constructor Details

180 | 181 |
182 |

183 | 184 | #initializeContext 185 | 186 | 187 | 188 | 189 | 190 |

191 |
192 | 193 |

Returns a new instance of Context.

194 | 195 | 196 |
197 |
198 |
199 | 200 | 201 |
202 | 203 | 211 | 218 | 219 |
204 |
205 | 
206 | 
207 | 5
208 | 6
209 | 7
210 |
212 |
# File 'lib/enhanced/context.rb', line 5
213 | 
214 | def initialize
215 |   @binding_infos = []
216 | end
217 |
220 |
221 | 222 |
223 | 224 |
225 |

Instance Attribute Details

226 | 227 | 228 | 229 |
230 |

231 | 232 | #binding_infosObject 233 | 234 | 235 | 236 | 237 | 238 |

239 |
240 | 241 |

Returns the value of attribute binding_infos.

242 | 243 | 244 |
245 |
246 |
247 | 248 | 249 |
250 | 251 | 259 | 266 | 267 |
252 |
253 | 
254 | 
255 | 3
256 | 4
257 | 5
258 |
260 |
# File 'lib/enhanced/context.rb', line 3
261 | 
262 | def binding_infos
263 |   @binding_infos
264 | end
265 |
268 |
269 | 270 |
271 | 272 | 273 |
274 | 275 | 280 | 281 |
282 | 283 | -------------------------------------------------------------------------------- /doc/js/full_list.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var $clicked = $(null); 4 | var searchTimeout = null; 5 | var searchCache = []; 6 | var caseSensitiveMatch = false; 7 | var ignoreKeyCodeMin = 8; 8 | var ignoreKeyCodeMax = 46; 9 | var commandKey = 91; 10 | 11 | RegExp.escape = function(text) { 12 | return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 13 | } 14 | 15 | function escapeShortcut() { 16 | $(document).keydown(function(evt) { 17 | if (evt.which == 27) { 18 | window.parent.postMessage('navEscape', '*'); 19 | } 20 | }); 21 | } 22 | 23 | function navResizer() { 24 | $(window).mousemove(function(e) { 25 | window.parent.postMessage({ 26 | action: 'mousemove', event: {pageX: e.pageX, which: e.which} 27 | }, '*'); 28 | }).mouseup(function(e) { 29 | window.parent.postMessage({action: 'mouseup'}, '*'); 30 | }); 31 | window.parent.postMessage("navReady", "*"); 32 | } 33 | 34 | function clearSearchTimeout() { 35 | clearTimeout(searchTimeout); 36 | searchTimeout = null; 37 | } 38 | 39 | function enableLinks() { 40 | // load the target page in the parent window 41 | $('#full_list li').on('click', function(evt) { 42 | $('#full_list li').removeClass('clicked'); 43 | $clicked = $(this); 44 | $clicked.addClass('clicked'); 45 | evt.stopPropagation(); 46 | 47 | if (evt.target.tagName === 'A') return true; 48 | 49 | var elem = $clicked.find('> .item .object_link a')[0]; 50 | var e = evt.originalEvent; 51 | var newEvent = new MouseEvent(evt.originalEvent.type); 52 | newEvent.initMouseEvent(e.type, e.canBubble, e.cancelable, e.view, e.detail, e.screenX, e.screenY, e.clientX, e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.button, e.relatedTarget); 53 | elem.dispatchEvent(newEvent); 54 | evt.preventDefault(); 55 | return false; 56 | }); 57 | } 58 | 59 | function enableToggles() { 60 | // show/hide nested classes on toggle click 61 | $('#full_list a.toggle').on('click', function(evt) { 62 | evt.stopPropagation(); 63 | evt.preventDefault(); 64 | $(this).parent().parent().toggleClass('collapsed'); 65 | $(this).attr('aria-expanded', function (i, attr) { 66 | return attr == 'true' ? 'false' : 'true' 67 | }); 68 | highlight(); 69 | }); 70 | 71 | // navigation of nested classes using keyboard 72 | $('#full_list a.toggle').on('keypress',function(evt) { 73 | // enter key is pressed 74 | if (evt.which == 13) { 75 | evt.stopPropagation(); 76 | evt.preventDefault(); 77 | $(this).parent().parent().toggleClass('collapsed'); 78 | $(this).attr('aria-expanded', function (i, attr) { 79 | return attr == 'true' ? 'false' : 'true' 80 | }); 81 | highlight(); 82 | } 83 | }); 84 | } 85 | 86 | function populateSearchCache() { 87 | $('#full_list li .item').each(function() { 88 | var $node = $(this); 89 | var $link = $node.find('.object_link a'); 90 | if ($link.length > 0) { 91 | searchCache.push({ 92 | node: $node, 93 | link: $link, 94 | name: $link.text(), 95 | fullName: $link.attr('title').split(' ')[0] 96 | }); 97 | } 98 | }); 99 | } 100 | 101 | function enableSearch() { 102 | $('#search input').keyup(function(event) { 103 | if (ignoredKeyPress(event)) return; 104 | if (this.value === "") { 105 | clearSearch(); 106 | } else { 107 | performSearch(this.value); 108 | } 109 | }); 110 | 111 | $('#full_list').after(""); 112 | } 113 | 114 | function ignoredKeyPress(event) { 115 | if ( 116 | (event.keyCode > ignoreKeyCodeMin && event.keyCode < ignoreKeyCodeMax) || 117 | (event.keyCode == commandKey) 118 | ) { 119 | return true; 120 | } else { 121 | return false; 122 | } 123 | } 124 | 125 | function clearSearch() { 126 | clearSearchTimeout(); 127 | $('#full_list .found').removeClass('found').each(function() { 128 | var $link = $(this).find('.object_link a'); 129 | $link.text($link.text()); 130 | }); 131 | $('#full_list, #content').removeClass('insearch'); 132 | $clicked.parents().removeClass('collapsed'); 133 | highlight(); 134 | } 135 | 136 | function performSearch(searchString) { 137 | clearSearchTimeout(); 138 | $('#full_list, #content').addClass('insearch'); 139 | $('#noresults').text('').hide(); 140 | partialSearch(searchString, 0); 141 | } 142 | 143 | function partialSearch(searchString, offset) { 144 | var lastRowClass = ''; 145 | var i = null; 146 | for (i = offset; i < Math.min(offset + 50, searchCache.length); i++) { 147 | var item = searchCache[i]; 148 | var searchName = (searchString.indexOf('::') != -1 ? item.fullName : item.name); 149 | var matchString = buildMatchString(searchString); 150 | var matchRegexp = new RegExp(matchString, caseSensitiveMatch ? "" : "i"); 151 | if (searchName.match(matchRegexp) == null) { 152 | item.node.removeClass('found'); 153 | item.link.text(item.link.text()); 154 | } 155 | else { 156 | item.node.addClass('found'); 157 | item.node.removeClass(lastRowClass).addClass(lastRowClass == 'r1' ? 'r2' : 'r1'); 158 | lastRowClass = item.node.hasClass('r1') ? 'r1' : 'r2'; 159 | item.link.html(item.name.replace(matchRegexp, "$&")); 160 | } 161 | } 162 | if(i == searchCache.length) { 163 | searchDone(); 164 | } else { 165 | searchTimeout = setTimeout(function() { 166 | partialSearch(searchString, i); 167 | }, 0); 168 | } 169 | } 170 | 171 | function searchDone() { 172 | searchTimeout = null; 173 | highlight(); 174 | var found = $('#full_list li:visible').size(); 175 | if (found === 0) { 176 | $('#noresults').text('No results were found.'); 177 | } else { 178 | // This is read out to screen readers 179 | $('#noresults').text('There are ' + found + ' results.'); 180 | } 181 | $('#noresults').show(); 182 | $('#content').removeClass('insearch'); 183 | } 184 | 185 | function buildMatchString(searchString, event) { 186 | caseSensitiveMatch = searchString.match(/[A-Z]/) != null; 187 | var regexSearchString = RegExp.escape(searchString); 188 | if (caseSensitiveMatch) { 189 | regexSearchString += "|" + 190 | $.map(searchString.split(''), function(e) { return RegExp.escape(e); }). 191 | join('.+?'); 192 | } 193 | return regexSearchString; 194 | } 195 | 196 | function highlight() { 197 | $('#full_list li:visible').each(function(n) { 198 | $(this).removeClass('even odd').addClass(n % 2 == 0 ? 'odd' : 'even'); 199 | }); 200 | } 201 | 202 | /** 203 | * Expands the tree to the target element and its immediate 204 | * children. 205 | */ 206 | function expandTo(path) { 207 | var $target = $(document.getElementById('object_' + path)); 208 | $target.addClass('clicked'); 209 | $target.removeClass('collapsed'); 210 | $target.parentsUntil('#full_list', 'li').removeClass('collapsed'); 211 | 212 | $target.find('a.toggle').attr('aria-expanded', 'true') 213 | $target.parentsUntil('#full_list', 'li').each(function(i, el) { 214 | $(el).find('> div > a.toggle').attr('aria-expanded', 'true'); 215 | }); 216 | 217 | if($target[0]) { 218 | window.scrollTo(window.scrollX, $target.offset().top - 250); 219 | highlight(); 220 | } 221 | } 222 | 223 | function windowEvents(event) { 224 | var msg = event.data; 225 | if (msg.action === "expand") { 226 | expandTo(msg.path); 227 | } 228 | return false; 229 | } 230 | 231 | window.addEventListener("message", windowEvents, false); 232 | 233 | $(document).ready(function() { 234 | escapeShortcut(); 235 | navResizer(); 236 | enableLinks(); 237 | enableToggles(); 238 | populateSearchCache(); 239 | enableSearch(); 240 | }); 241 | 242 | })(); 243 | -------------------------------------------------------------------------------- /doc/ExceptionBindingInfos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: ExceptionBindingInfos 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: ExceptionBindingInfos 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 |
Included in:
79 |
Exception
80 |
81 | 82 | 83 | 84 |
85 |
Defined in:
86 |
lib/enhanced/exception.rb
87 |
88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |

100 | Instance Method Summary 101 | collapse 102 |

103 | 104 | 151 | 152 | 153 | 154 | 155 |
156 |

Instance Method Details

157 | 158 | 159 |
160 |

161 | 162 | #binding_infosObject 163 | 164 | 165 | 166 | 167 | 168 |

169 | 170 | 183 | 195 | 196 |
171 |
172 | 
173 | 
174 | 5
175 | 6
176 | 7
177 | 8
178 | 9
179 | 10
180 | 11
181 | 12
182 |
184 |
# File 'lib/enhanced/exception.rb', line 5
185 | 
186 | def binding_infos
187 |   ctx = Enhanced::ExceptionContext.context_for(self)
188 |   unless ctx
189 |     ctx = Context.new
190 |     Enhanced::ExceptionContext.store_context(self, ctx)
191 |   end
192 |   ctx.binding_infos
193 | end
194 |
197 |
198 | 199 |
200 |

201 | 202 | #captured_variablesObject 203 | 204 | 205 | 206 | 207 | 208 |

209 | 210 | 225 | 239 | 240 |
211 |
212 | 
213 | 
214 | 14
215 | 15
216 | 16
217 | 17
218 | 18
219 | 19
220 | 20
221 | 21
222 | 22
223 | 23
224 |
226 |
# File 'lib/enhanced/exception.rb', line 14
227 | 
228 | def captured_variables
229 |   if binding_infos.any?
230 |     bindings_of_interest = select_binding_infos
231 |     EnhancedErrors.format(bindings_of_interest)
232 |   else
233 |     ''
234 |   end
235 | rescue
236 |   ''
237 | end
238 |
241 |
242 | 243 |
244 | 245 |
246 | 247 | 252 | 253 |
254 | 255 | -------------------------------------------------------------------------------- /doc/Enhanced/ExceptionBindingInfos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Enhanced::ExceptionBindingInfos 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Enhanced::ExceptionBindingInfos 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 |
Included in:
79 |
Exception
80 |
81 | 82 | 83 | 84 |
85 |
Defined in:
86 |
lib/enhanced/exception.rb
87 |
88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |

100 | Instance Method Summary 101 | collapse 102 |

103 | 104 | 151 | 152 | 153 | 154 | 155 |
156 |

Instance Method Details

157 | 158 | 159 |
160 |

161 | 162 | #binding_infosObject 163 | 164 | 165 | 166 | 167 | 168 |

169 | 170 | 183 | 195 | 196 |
171 |
172 | 
173 | 
174 | 6
175 | 7
176 | 8
177 | 9
178 | 10
179 | 11
180 | 12
181 | 13
182 |
184 |
# File 'lib/enhanced/exception.rb', line 6
185 | 
186 | def binding_infos
187 |   ctx = Enhanced::ExceptionContext.context_for(self)
188 |   unless ctx
189 |     ctx = Context.new
190 |     Enhanced::ExceptionContext.store_context(self, ctx)
191 |   end
192 |   ctx.binding_infos
193 | end
194 |
197 |
198 | 199 |
200 |

201 | 202 | #captured_variablesObject 203 | 204 | 205 | 206 | 207 | 208 |

209 | 210 | 222 | 233 | 234 |
211 |
212 | 
213 | 
214 | 15
215 | 16
216 | 17
217 | 18
218 | 19
219 | 20
220 | 21
221 |
223 |
# File 'lib/enhanced/exception.rb', line 15
224 | 
225 | def captured_variables
226 |   return '' unless binding_infos&.any?
227 |   bindings_of_interest = select_binding_infos
228 |   EnhancedErrors.format(bindings_of_interest)
229 | rescue
230 |   ''
231 | end
232 |
235 |
236 | 237 |
238 | 239 |
240 | 241 | 246 | 247 |
248 | 249 | -------------------------------------------------------------------------------- /doc/Minitest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Module: Minitest 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Module: Minitest 63 | 64 | 65 | 66 |

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
80 |
Defined in:
81 |
lib/enhanced/minitest_patch.rb
82 |
83 | 84 |
85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |

95 | Class Method Summary 96 | collapse 97 |

98 | 99 | 146 | 147 | 148 | 149 | 150 |
151 |

Class Method Details

152 | 153 | 154 |
155 |

156 | 157 | .original_run_one_methodObject 158 | 159 | 160 | 161 | 162 | 163 |

164 | 165 | 171 | 176 | 177 |
166 |
167 | 
168 | 
169 | 3
170 |
172 |
# File 'lib/enhanced/minitest_patch.rb', line 3
173 | 
174 | alias_method :original_run_one_method, :run_one_method
175 |
178 |
179 | 180 |
181 |

182 | 183 | .run_one_method(klass, method_name) ⇒ Object 184 | 185 | 186 | 187 | 188 | 189 |

190 | 191 | 208 | 224 | 225 |
192 |
193 | 
194 | 
195 | 5
196 | 6
197 | 7
198 | 8
199 | 9
200 | 10
201 | 11
202 | 12
203 | 13
204 | 14
205 | 15
206 | 16
207 |
209 |
# File 'lib/enhanced/minitest_patch.rb', line 5
210 | 
211 | def run_one_method(klass, method_name)
212 |   EnhancedErrors.start_minitest_binding_capture
213 |   result = original_run_one_method(klass, method_name)
214 | ensure
215 |   begin
216 |     binding_infos = EnhancedErrors.stop_minitest_binding_capture
217 |     EnhancedErrors.override_exception_message(result.failures.last, binding_infos) if result.failures.any?
218 |     Enhanced::ExceptionContext.clear_all
219 |   rescue => e
220 |     puts "Ignored error during error enhancement: #{e}"
221 |   end
222 | end
223 |
226 |
227 | 228 |
229 | 230 |
231 | 232 | 237 | 238 |
239 | 240 | -------------------------------------------------------------------------------- /doc/Enhanced/Colors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Class: Enhanced::Colors 8 | 9 | — Documentation by YARD 0.9.37 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 34 | 35 |
36 | 61 | 62 |

Class: Enhanced::Colors 63 | 64 | 65 | 66 |

67 |
68 | 69 |
70 |
Inherits:
71 |
72 | Object 73 | 74 |
    75 |
  • Object
  • 76 | 77 | 78 | 79 |
80 | show all 81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 |
Defined in:
97 |
lib/enhanced/colors.rb
98 |
99 | 100 |
101 | 102 | 103 | 104 |

105 | Constant Summary 106 | collapse 107 |

108 | 109 |
110 | 111 |
COLORS = 112 | 113 |
114 |
{ red: 31, green: 32, yellow: 33, blue: 34, purple: 35, cyan: 36, white: 0 }.freeze
115 | 116 |
RESET_CODE = 117 | 118 |
119 |
"\e[0m".freeze
120 | 121 |
122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |

132 | Class Method Summary 133 | collapse 134 |

135 | 136 | 227 | 228 | 229 | 230 | 231 |
232 |

Class Method Details

233 | 234 | 235 |
236 |

237 | 238 | .code(num) ⇒ Object 239 | 240 | 241 | 242 | 243 | 244 |

245 | 246 | 254 | 261 | 262 |
247 |
248 | 
249 | 
250 | 20
251 | 21
252 | 22
253 |
255 |
# File 'lib/enhanced/colors.rb', line 20
256 | 
257 | def code(num)
258 |   "\e[#{num}m".freeze
259 | end
260 |
263 |
264 | 265 |
266 |

267 | 268 | .color(num, string) ⇒ Object 269 | 270 | 271 | 272 | 273 | 274 |

275 | 276 | 285 | 293 | 294 |
277 |
278 | 
279 | 
280 | 15
281 | 16
282 | 17
283 | 18
284 |
286 |
# File 'lib/enhanced/colors.rb', line 15
287 | 
288 | def color(num, string)
289 |   return string unless @enabled
290 |   "#{code(num)}#{string}#{RESET_CODE}"
291 | end
292 |
295 |
296 | 297 |
298 |

299 | 300 | .enabled=(value) ⇒ Object 301 | 302 | 303 | 304 | 305 | 306 |

307 | 308 | 316 | 323 | 324 |
309 |
310 | 
311 | 
312 | 11
313 | 12
314 | 13
315 |
317 |
# File 'lib/enhanced/colors.rb', line 11
318 | 
319 | def enabled=(value)
320 |   @enabled = value
321 | end
322 |
325 |
326 | 327 |
328 |

329 | 330 | .enabled?Boolean 331 | 332 | 333 | 334 | 335 | 336 |

337 |
338 | 339 | 340 |
341 |
342 |
343 | 344 |

Returns:

345 |
    346 | 347 |
  • 348 | 349 | 350 | (Boolean) 351 | 352 | 353 | 354 |
  • 355 | 356 |
357 | 358 |
359 | 360 | 368 | 375 | 376 |
361 |
362 | 
363 | 
364 | 7
365 | 8
366 | 9
367 |
369 |
# File 'lib/enhanced/colors.rb', line 7
370 | 
371 | def enabled?
372 |   @enabled
373 | end
374 |
377 |
378 | 379 |
380 | 381 |
382 | 383 | 388 | 389 |
390 | 391 | -------------------------------------------------------------------------------- /doc/js/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var localStorage = {}, 3 | sessionStorage = {}; 4 | try { 5 | localStorage = window.localStorage; 6 | } catch (e) {} 7 | try { 8 | sessionStorage = window.sessionStorage; 9 | } catch (e) {} 10 | 11 | function createSourceLinks() { 12 | $(".method_details_list .source_code").before( 13 | "[View source]" 14 | ); 15 | $(".toggleSource").toggle( 16 | function () { 17 | $(this).parent().nextAll(".source_code").slideDown(100); 18 | $(this).text("Hide source"); 19 | }, 20 | function () { 21 | $(this).parent().nextAll(".source_code").slideUp(100); 22 | $(this).text("View source"); 23 | } 24 | ); 25 | } 26 | 27 | function createDefineLinks() { 28 | var tHeight = 0; 29 | $(".defines").after(" more..."); 30 | $(".toggleDefines").toggle( 31 | function () { 32 | tHeight = $(this).parent().prev().height(); 33 | $(this).prev().css("display", "inline"); 34 | $(this).parent().prev().height($(this).parent().height()); 35 | $(this).text("(less)"); 36 | }, 37 | function () { 38 | $(this).prev().hide(); 39 | $(this).parent().prev().height(tHeight); 40 | $(this).text("more..."); 41 | } 42 | ); 43 | } 44 | 45 | function createFullTreeLinks() { 46 | var tHeight = 0; 47 | $(".inheritanceTree").toggle( 48 | function () { 49 | tHeight = $(this).parent().prev().height(); 50 | $(this).parent().toggleClass("showAll"); 51 | $(this).text("(hide)"); 52 | $(this).parent().prev().height($(this).parent().height()); 53 | }, 54 | function () { 55 | $(this).parent().toggleClass("showAll"); 56 | $(this).parent().prev().height(tHeight); 57 | $(this).text("show all"); 58 | } 59 | ); 60 | } 61 | 62 | function searchFrameButtons() { 63 | $(".full_list_link").click(function () { 64 | toggleSearchFrame(this, $(this).attr("href")); 65 | return false; 66 | }); 67 | window.addEventListener("message", function (e) { 68 | if (e.data === "navEscape") { 69 | $("#nav").slideUp(100); 70 | $("#search a").removeClass("active inactive"); 71 | $(window).focus(); 72 | } 73 | }); 74 | 75 | $(window).resize(function () { 76 | if ($("#search:visible").length === 0) { 77 | $("#nav").removeAttr("style"); 78 | $("#search a").removeClass("active inactive"); 79 | $(window).focus(); 80 | } 81 | }); 82 | } 83 | 84 | function toggleSearchFrame(id, link) { 85 | var frame = $("#nav"); 86 | $("#search a").removeClass("active").addClass("inactive"); 87 | if (frame.attr("src") === link && frame.css("display") !== "none") { 88 | frame.slideUp(100); 89 | $("#search a").removeClass("active inactive"); 90 | } else { 91 | $(id).addClass("active").removeClass("inactive"); 92 | if (frame.attr("src") !== link) frame.attr("src", link); 93 | frame.slideDown(100); 94 | } 95 | } 96 | 97 | function linkSummaries() { 98 | $(".summary_signature").click(function () { 99 | document.location = $(this).find("a").attr("href"); 100 | }); 101 | } 102 | 103 | function summaryToggle() { 104 | $(".summary_toggle").click(function (e) { 105 | e.preventDefault(); 106 | localStorage.summaryCollapsed = $(this).text(); 107 | $(".summary_toggle").each(function () { 108 | $(this).text($(this).text() == "collapse" ? "expand" : "collapse"); 109 | var next = $(this).parent().parent().nextAll("ul.summary").first(); 110 | if (next.hasClass("compact")) { 111 | next.toggle(); 112 | next.nextAll("ul.summary").first().toggle(); 113 | } else if (next.hasClass("summary")) { 114 | var list = $('