exception_details 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Eric Beland
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # Exception Details
2
+
3
+ Exception Details causes instances of Exception to capture a binding at creation-time.
4
+ It provides convenience methods on Exceptions to inspect all the variables and values
5
+ from the time of the Exception and adds an informative log string.
6
+
7
+ Features/benefits:
8
+
9
+ * Get detail (variables/values) about your Exception circumstances *without* reproducing the problem first.
10
+
11
+ * Reduced need for debug/logging code to find out what went wrong.
12
+
13
+ * Minimize ambiguous errors (which variable was nil?).
14
+
15
+ * A single statement method for a loggable string (avoid repeating exception log string creation)
16
+
17
+ * Get Pry like debug knowledge from cron job errors and running system logs without being there.
18
+
19
+ * Pry type information with less overhead/dependencies--binding_of_caller is the only dependency.
20
+
21
+
22
+ ## Example Usage
23
+
24
+ begin
25
+ greeting = 'hello'
26
+ @name = nil
27
+ puts greeting + name
28
+ rescue Exception => e
29
+ puts e.details
30
+ puts e.inspect_variables
31
+ end
32
+
33
+
34
+ e.details ->
35
+
36
+ Exception:
37
+ can't convert nil into String
38
+ Variables:
39
+ <String>greeting = "hello"
40
+ <TypeError>e = #<TypeError: can't convert nil into String>
41
+ <NilClass>@name = nil
42
+ Backtrace:
43
+ /Users/someguy/apps/exception_details/spec/exception_details_spec.rb:20:in `+'
44
+ /Users/someguy/apps/exception_details/spec/exception_details_spec.rb:20:in `block (3 levels) in <top (required)>'
45
+ /Users/someguy/.rvm/gems/ruby-1.9.3-p374/gems/rspec-core-2.14.3/lib/rspec/core/example.rb:114:in `instance_eval'
46
+ and so forth ...
47
+
48
+
49
+ e.inspect_variables ->
50
+
51
+ <String>greeting = "hello"
52
+ <TypeError>e = #<TypeError: can't convert nil into String>
53
+ <NilClass>@name = nil
54
+
55
+ Or access the variables in the binding yourself...
56
+
57
+ e.binding_during_exception.eval("puts #{greeting}")
58
+
59
+ ## Installation
60
+
61
+ Add to your Gemfile:
62
+
63
+ gem 'exception_details'
64
+
65
+ Execute:
66
+
67
+ $ bundle
68
+
69
+ Require it:
70
+
71
+ require 'exception_details'
72
+
73
+ ## Limitations
74
+ - This gem requires [binding\_of\_caller]:https://github.com/banister/binding_of_caller, so it should only work with MRI 1.9.2, 1.9.3, 2.0
75
+ and RBX (Rubinius). Does not work in 1.8.7, but there is a well known (continuation-based)
76
+ hack to get a Binding#of_caller there. There is some mention about binding of caller supporting
77
+ jruby, so feel free to try it out.
78
+
79
+ - Getting a binding from a NameError seems to be problematic.
80
+
81
+ - This gem is still new...
82
+
83
+ ## Contributing
84
+
85
+ 1. Fork it
86
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
87
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
88
+ 4. Push to the branch (`git push origin my-new-feature`)
89
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'exception_details/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "exception_details"
8
+ spec.version = ExceptionDetails::VERSION
9
+ spec.authors = ["Eric Beland"]
10
+ spec.email = ["ebeland@gmail.com"]
11
+ spec.description = %q{Inspect variables captured at exception-time to get info about your exceptions}
12
+ spec.summary = %q{Exception Details extends Exception to let you inspect variable values at exception-time for logging etc.}
13
+ spec.homepage = "https://github.com/ericbeland/exception_details"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "binding_of_caller"
26
+
27
+ end
@@ -0,0 +1,6 @@
1
+ require 'binding_of_caller'
2
+
3
+ require "exception_details/version"
4
+ require "exception_details/inspect_variables"
5
+ require "exception_details/log_color_helpers"
6
+ require "exception_details/exception"
@@ -0,0 +1,53 @@
1
+ require_relative '../exception_details'
2
+
3
+ # Extend the Ruby Exception class with our helper methods.
4
+ class Exception
5
+ include ExceptionDetails::InspectVariables
6
+ include ExceptionDetails::LogColorHelpers
7
+
8
+ # binding_during_exception lets you actually directly access
9
+ # the exception-time binding.
10
+ attr_accessor :binding_during_exception
11
+
12
+ # Provides a string with the variable names and values
13
+ # captured at exception time.
14
+ def inspect_variables(opts = {})
15
+ variable_inspect_string(binding_during_exception, opts)
16
+ end
17
+
18
+ # .details provides a fairly complete string for logging purposes.
19
+ # The message, variables in the exception's scope, and their current
20
+ # values are outputted, followed by the whole backtrace.
21
+ # * +options+ - options[:scope] default: [:local_variables, :instance_variables, :class_variables]
22
+ # - options[:colorize] true / false. Whether to add color to the output (for terminal/log)
23
+ def details(options = {})
24
+ options = {colorize: true}.merge(options)
25
+ inspect_results = inspect_variables(options)
26
+ parts = []
27
+ parts << (options[:colorize] ? red('Exception:') : 'Exception:')
28
+ parts << "\t" + message
29
+ parts << (options[:colorize] ? red('Variables:') : 'Variables:')
30
+ parts << inspect_results
31
+ parts << (options[:colorize] ? red('Backtrace:') : 'Backtrace:')
32
+ parts << "\t" + backtrace.to_a.join("\n")
33
+ parts.join("\n")
34
+ end
35
+
36
+ class << self
37
+ alias :__new__ :new
38
+
39
+ def inherited(subclass)
40
+ class << subclass
41
+ alias :new :__new__
42
+ end
43
+ end
44
+ end
45
+
46
+ # override the .new method on exception to grab the binding where the exception occurred.
47
+ def self.new(*args, &block)
48
+ e = __new__(*args)
49
+ e.binding_during_exception = binding.of_caller(1)
50
+ e
51
+ end
52
+
53
+ end
@@ -0,0 +1,50 @@
1
+ module ExceptionDetails
2
+
3
+ # Mixin that provides methods for dumping variables
4
+ module InspectVariables
5
+
6
+ SCOPES = [:local_variables, :instance_variables, :class_variables]
7
+
8
+ private
9
+
10
+ # For each variable scope, build an array of strings displaying
11
+ # type, name and value of each variable in that scope
12
+ # * +target_binding+ - Binding context to inspect.
13
+ # * +variable_scope+ - Variable scope to dump. One of :local_variables,
14
+ # :instance_variables, :class_variables, :global variables
15
+
16
+ def dump_variables(target_binding, variable_scope)
17
+ variable_strings = []
18
+ if target_binding.eval("respond_to?(:#{variable_scope}, true)")
19
+ variable_names = target_binding.eval(variable_scope.to_s)
20
+ variable_names.each do |vname|
21
+ value = target_binding.eval(vname.to_s)
22
+ variable_value_string = "\t<#{value.class}>#{vname} = #{value.inspect}"
23
+ variable_strings << variable_value_string
24
+ end
25
+ end
26
+ variable_strings.sort
27
+ end
28
+
29
+ # Dump all variables and values in selected scopes into a string
30
+ # * +target_binding+ - Binding context to inspect.
31
+ # * +options+ - options[:scope] default: [:local_variables, :instance_variables, :class_variables]
32
+ # - :global_variables is valid as well. Can be an array, or single scope-name symbol
33
+ # or an array
34
+ def variable_inspect_string(target_binding, options = {})
35
+ if options[:scopes]
36
+ scopes = [options[:scopes]].flatten
37
+ end
38
+ return ["exception_details: Variable inspection unavailable (no binding captured)"] if target_binding.nil?
39
+ variable_scopes = scopes || SCOPES
40
+ dump_arrays = []
41
+ variable_scopes.each do |variable_scope|
42
+ variable_string_array = dump_variables(target_binding, variable_scope.to_s)
43
+ dump_arrays << variable_string_array if variable_string_array.length > 0
44
+ end
45
+ dump_arrays.join("\n")
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,23 @@
1
+ # Helper methods for printing colored output into the log/terminal
2
+ module ExceptionDetails
3
+ module LogColorHelpers
4
+ def black(str); "\033[30m#{str}\033[0m" end
5
+ def red(str); "\033[31m#{str}\033[0m" end
6
+ def green(str); "\033[32m#{str}\033[0m" end
7
+ def brown(str); "\033[33m#{str}\033[0m" end
8
+ def blue(str); "\033[34m#{str}\033[0m" end
9
+ def magenta(str); "\033[35m#{str}\033[0m" end
10
+ def cyan(str); "\033[36m#{str}\033[0m" end
11
+ def gray(str); "\033[37m#{str}\033[0m" end
12
+ def bg_black(str); "\033[40m#{str}\0330m" end
13
+ def bg_red(str); "\033[41m#{str}\033[0m" end
14
+ def bg_green(str); "\033[42m#{str}\033[0m" end
15
+ def bg_brown(str); "\033[43m#{str}\033[0m" end
16
+ def bg_blue(str); "\033[44m#{str}\033[0m" end
17
+ def bg_magenta(str); "\033[45m#{str}\033[0m" end
18
+ def bg_cyan(str); "\033[46m#{str}\033[0m" end
19
+ def bg_gray(str); "\033[47m#{str}\033[0m" end
20
+ def bold(str); "\033[1m#{str}\033[22m" end
21
+ def reverse_color(str); "\033[7m#{str}\033[27m" end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module ExceptionDetails
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,107 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require 'exception_details'
4
+
5
+ describe "ExceptionDetails" do
6
+
7
+ before(:each) do
8
+ v = VariableScopeTestObject.new
9
+ @e = v.make_exception
10
+ @class_e = VariableScopeTestObject.make_class_level_exception
11
+ end
12
+
13
+ context ".details" do
14
+ it "should provide exception details method with back trace" do
15
+ @e.details.include?('.rb').should be_true
16
+ end
17
+
18
+ it "should provide exception message" do
19
+ @e.details.include?(@e.message).should be_true
20
+ end
21
+
22
+ it "should include inspect_variables output" do
23
+ @e.details.include?(@e.inspect_variables).should be_true
24
+ end
25
+
26
+ end
27
+
28
+ context "exception detail functions" do
29
+
30
+ it "should be available on exceptions created by raise and a string" do
31
+ begin
32
+ raise "Foo"
33
+ rescue Exception => e
34
+ e.binding_during_exception.should be_an_instance_of Binding
35
+ end
36
+ end
37
+
38
+ it "should be available on Exceptions" do
39
+ reality_check = true
40
+ e = Exception.new
41
+ e.binding_during_exception.should be_an_instance_of Binding
42
+ e.inspect_variables.include?("reality_check").should be_true
43
+ end
44
+
45
+ it "should be available on subclasses of exception like Standard Error" do
46
+ reality_check = true
47
+ e = StandardError.new
48
+ e.binding_during_exception.should be_an_instance_of Binding
49
+ e.inspect_variables.include?("reality_check").should be_true
50
+ # end
51
+ end
52
+
53
+ it "should be available on grandchild subclasses" do
54
+ begin
55
+ # cause an arugument error
56
+ should_be_in_output = true
57
+ "UP".downcase("", "")
58
+ rescue Exception => e
59
+ e.binding_during_exception.should be_an_instance_of Binding
60
+ e.inspect_variables.include?("should_be_in_output").should be_true
61
+ end
62
+ end
63
+
64
+ pending "Need to investigate why NameError won't capture a binding (any takers?)" do
65
+ it "should capture a binding for NameError" do
66
+ begin
67
+ reality_check = true
68
+ 'hello' + made_up_variable
69
+ rescue Exception =>e
70
+ e.binding_during_exception.should be_an_instance_of Binding
71
+ e.inspect_variables.include?("reality_check").should be_true
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ context "inspect variables" do
79
+
80
+ it "should include variable values" do
81
+ @e.inspect_variables.include?('1nstance').should be_true
82
+ @e.inspect_variables.include?('1ocal').should be_true
83
+
84
+ @class_e.inspect_variables.include?('c1ass').should be_true
85
+ end
86
+
87
+ it "should include variable names" do
88
+ names = %w{@instance local @@class}
89
+ @e.inspect_variables.include?('@instance').should be_true
90
+ @e.inspect_variables.include?('local').should be_true
91
+
92
+ @class_e.inspect_variables.include?('@@class').should be_true
93
+ end
94
+
95
+ it "should not include global variables by default" do
96
+ @e.details.include?('g1obal').should be_false
97
+ end
98
+
99
+ it "should let me select scopes" do
100
+ d = @e.details(:scopes => :local_variables)
101
+ @e.details(:scopes => :local_variables).include?('1nstance').should be_false
102
+ @e.details(:scopes => :class_variables).include?('g1obal').should be_false
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'exception_details'
5
+ require 'variable_scope_test_object'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir["#{File.dirname(__FILE__)}/helpers/**/*.rb"].each {|f| require f }
10
+
11
+ RSpec.configure do |config|
12
+
13
+
14
+ end
@@ -0,0 +1,24 @@
1
+ class VariableScopeTestObject
2
+
3
+ @@class = 'c1ass'
4
+ $global = 'g1obal'
5
+
6
+ def initialize
7
+ @instance = '1nstance'
8
+ end
9
+
10
+ def make_exception
11
+ local = '1ocal'
12
+ e = Exception.new("Foo")
13
+ e.set_backtrace(['somemadeupfileforbacktrace.rb:435'])
14
+ e
15
+ end
16
+
17
+ def self.make_class_level_exception
18
+ local = '1ocal'
19
+ e = Exception.new("Foo")
20
+ e.set_backtrace(['somemadeupfileforbacktrace.rb:435'])
21
+ e
22
+ end
23
+
24
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exception_details
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Eric Beland
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: binding_of_caller
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Inspect variables captured at exception-time to get info about your exceptions
79
+ email:
80
+ - ebeland@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - exception_details.gemspec
91
+ - lib/exception_details.rb
92
+ - lib/exception_details/exception.rb
93
+ - lib/exception_details/inspect_variables.rb
94
+ - lib/exception_details/log_color_helpers.rb
95
+ - lib/exception_details/version.rb
96
+ - spec/exception_details_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/variable_scope_test_object.rb
99
+ homepage: https://github.com/ericbeland/exception_details
100
+ licenses:
101
+ - MIT
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.25
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Exception Details extends Exception to let you inspect variable values at
124
+ exception-time for logging etc.
125
+ test_files:
126
+ - spec/exception_details_spec.rb
127
+ - spec/spec_helper.rb
128
+ - spec/variable_scope_test_object.rb
129
+ has_rdoc: