exception_handling 1.0.3 → 1.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 455910720fe5a8b91de548665df5c639c97dcf22
4
- data.tar.gz: 866669be2cb461553af17b502307e19a1af3b880
3
+ metadata.gz: 07258fd3838c1f4ad88afcc407b5a48c515cc1bb
4
+ data.tar.gz: 6bc4a796d301b5739a71b6436a9a3c348f03f2f3
5
5
  SHA512:
6
- metadata.gz: c9d3e9e02a983357bbec9211b096a6904447b1669ef12db296b1d9a277394b7d713e9fab8fa0fa9f21e4603c18e16a1b71b20fce64b7cf4335ba46fd0e7e19fe
7
- data.tar.gz: e0cc1d06e1aa28ffe22b9add5ef2c0ce4dd48fa9fa7d9894460cd50a756b4661c48a3a1ab2a6bd09a9c118250e87f8cbfc9f72dd1b98ff7e01b8a226acbdfc7c
6
+ metadata.gz: 866f948f04a3ac4c2abf81d221c148a6c236692242dbc318743344dc4f8bc5fb14b39ece122895438ba4c7e48305baef130a489c1b5d712399822b9ff5bad448
7
+ data.tar.gz: 82e94f9f3eb8030a852a93f341cc7b7694070267edc3773fa74aa3e7e3ac66fbe145a5be242816b20202912016836ebfef6898ea31433b02295920c6e2c35f90
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- exception_handling (1.0.3)
4
+ exception_handling (1.0.4)
5
5
  actionmailer (~> 3.2)
6
6
  actionpack (~> 3.2)
7
7
  activesupport (~> 3.2)
@@ -1,4 +1,9 @@
1
1
  --- !map:HashWithIndifferentAccess
2
+ Test BS:
3
+ error: "Some BS"
4
+ send_email: true
5
+ notes: "this is used by a test"
6
+
2
7
  All script kiddies:
3
8
  error: "ScriptKiddie suspected because of HTTP request without a referer"
4
9
 
@@ -0,0 +1,40 @@
1
+ module ExceptionHandling
2
+ class ExceptionCatalog
3
+
4
+ def initialize(filter_path)
5
+ @filter_path = filter_path
6
+ @filters = { }
7
+ @filters_last_modified_time = nil
8
+ end
9
+
10
+ def find(exception_data)
11
+ refresh_filters
12
+ @filters.values.find { |filter| filter.match?(exception_data) }
13
+ end
14
+
15
+ private
16
+
17
+ def refresh_filters
18
+ mtime = last_modified_time
19
+ if @filters_last_modified_time.nil? || mtime != @filters_last_modified_time
20
+ ExceptionHandling.logger.info("Reloading filter list from: #{@filter_path}. Last loaded time: #{@filters_last_modified_time}. Last modified time: #{mtime}")
21
+ load_file
22
+ end
23
+
24
+ rescue => ex # any exceptions
25
+ ExceptionHandling::log_error(ex, "ExceptionRegexes::refresh_filters: #{@filter_path}", nil, true)
26
+ end
27
+
28
+ def load_file
29
+ @filters_last_modified_time = last_modified_time # make race condition fall on the side of reloading unnecessarily next time rather than missing a set of changes
30
+
31
+ filters = YAML::load_file(@filter_path)
32
+ filter_hash_values = filters.map { |filter_name, regexes| [filter_name.to_sym, ExceptionDescription.new(filter_name.to_sym, regexes.symbolize_keys)] }
33
+ @filters = Hash[ filter_hash_values ]
34
+ end
35
+
36
+ def last_modified_time
37
+ File.mtime(@filter_path)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+ module ExceptionHandling
2
+ class ExceptionDescription
3
+ MATCH_SECTIONS = [:error, :request, :session, :environment, :backtrace, :event_response]
4
+
5
+ CONFIGURATION_SECTIONS = {
6
+ send_email: false, # should email be sent?
7
+ send_metric: true, # should the metric be sent.
8
+ metric_name: nil, # Will be derived from section name if not passed
9
+ notes: nil # Will be included in exception email if set, used to keep notes and relevant links
10
+ }
11
+
12
+ attr_reader :filter_name, :send_email, :send_metric, :metric_name, :notes
13
+
14
+ def initialize(filter_name, configuration)
15
+ @filter_name = filter_name
16
+
17
+ invalid_sections = configuration.except(*(CONFIGURATION_SECTIONS.keys + MATCH_SECTIONS))
18
+ invalid_sections.empty? or raise ArgumentError, "Unknown section: #{invalid_sections.keys.join(",")}"
19
+
20
+ @configuration = CONFIGURATION_SECTIONS.merge(configuration)
21
+ @send_email = @configuration[:send_email]
22
+ @send_metric = @configuration[:send_metric]
23
+ @metric_name = (@configuration[:metric_name] || @filter_name ).to_s.gsub(" ","_")
24
+ @notes = @configuration[:notes]
25
+
26
+ regex_config = @configuration.reject { |k,v| k.in?(CONFIGURATION_SECTIONS.keys) || v.blank? }
27
+
28
+ @regexes = Hash[regex_config.map { |section, regex| [section, Regexp.new(regex, 'i') ] }]
29
+
30
+ !@regexes.empty? or raise ArgumentError, "Filter #{filter_name} has all blank regexes: #{configuration.inspect}"
31
+ end
32
+
33
+ def exception_data
34
+ {
35
+ "send_metric" => send_metric,
36
+ "metric_name" => metric_name,
37
+ "notes" => notes
38
+ }
39
+ end
40
+
41
+ def match?(exception_data)
42
+ @regexes.all? do |section, regex|
43
+ case target = exception_data[section.to_s] || exception_data[section]
44
+ when String
45
+ regex =~ target
46
+ when Array
47
+ target.any? { |row| row =~ regex }
48
+ when Hash
49
+ target[:to_s] =~ regex
50
+ when NilClass
51
+ false
52
+ else
53
+ raise "Unexpected class #{exception_data[section].class.name}"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,3 @@
1
1
  module ExceptionHandling
2
- VERSION = "1.0.3"
2
+ VERSION = "1.0.4"
3
3
  end
@@ -7,6 +7,8 @@ require 'invoca/utils'
7
7
  require "exception_handling/mailer"
8
8
  require "exception_handling/methods"
9
9
  require "exception_handling/log_stub_error"
10
+ require "exception_handling/exception_description"
11
+ require "exception_handling/exception_catalog"
10
12
 
11
13
  _ = ActiveSupport::HashWithIndifferentAccess
12
14
 
@@ -351,22 +353,18 @@ EOF
351
353
 
352
354
  SECTIONS.each { |section| add_to_s( data[section] ) if data[section].is_a? Hash }
353
355
 
354
- if exception_filters.filtered?( data )
355
- return
356
- end
357
-
358
- if summarize_exception( data ) == :Summarized
359
- return
360
- end
356
+ exception_description = exception_catalog.find( data )
357
+ merged_data = exception_description ? ActiveSupport::HashWithIndifferentAccess.new(exception_description.exception_data.merge(data)) : data
361
358
 
362
- deliver(ExceptionHandling::Mailer.exception_notification(data))
363
-
364
- if defined?(Errplane)
365
- Errplane.transmit(exc, :custom_data => data) unless exc.is_a?(Warning)
359
+ if exception_description && !exception_description.send_email
360
+ ExceptionHandling.logger.warn( "Filtered exception using '#{exception_description.filter_name}'; not sending email to notify" )
361
+ else
362
+ if summarize_exception( merged_data ) != :Summarized
363
+ deliver(ExceptionHandling::Mailer.exception_notification(merged_data))
364
+ end
366
365
  end
367
366
 
368
- execute_custom_log_error_callback(data, exc)
369
-
367
+ execute_custom_log_error_callback(merged_data, exc)
370
368
  nil
371
369
  end
372
370
 
@@ -470,8 +468,8 @@ EOF
470
468
  end.compact ]
471
469
  end
472
470
 
473
- def exception_filters
474
- @exception_filters ||= ExceptionFilters.new( ExceptionHandling.filter_list_filename )
471
+ def exception_catalog
472
+ @exception_catalog ||= ExceptionCatalog.new( ExceptionHandling.filter_list_filename )
475
473
  end
476
474
 
477
475
  def clean_backtrace(exception)
@@ -594,81 +592,4 @@ EOF
594
592
  result
595
593
  end
596
594
  end
597
-
598
- class ExceptionFilters
599
- class Filter
600
- def initialize filter_name, regexes
601
- @regexes = Hash[ regexes.map do |section, regex|
602
- section = section.to_sym
603
- raise "Unknown section: #{section}" unless section == :error || section.in?( ExceptionHandling::SECTIONS )
604
- [section, (Regexp.new(regex, 'i') unless regex.blank?)]
605
- end ]
606
-
607
- raise "Filter #{filter_name} has all blank regexes: #{regexes.inspect}" if @regexes.all? { |section, regex| regex.nil? }
608
- end
609
-
610
- def match?(exception_data)
611
- @regexes.all? do |section, regex|
612
- regex.nil? ||
613
- case exception_data[section.to_s]
614
- when String
615
- regex =~ exception_data[section]
616
- when Array
617
- exception_data[section].any? { |row| row =~ regex }
618
- when Hash
619
- exception_data[section] && exception_data[section][:to_s] =~ regex
620
- when NilClass
621
- false
622
- else
623
- raise "Unexpected class #{exception_data[section].class.name}"
624
- end
625
- end
626
- end
627
- end
628
-
629
- def initialize( filter_path )
630
- @filter_path = filter_path
631
- @filters = { }
632
- @filters_last_modified_time = nil
633
- end
634
-
635
- def filtered?( exception_data )
636
- refresh_filters
637
-
638
- @filters.any? do |name, filter|
639
- if ( match = filter.match?( exception_data ) )
640
- ExceptionHandling.logger.warn( "Filtered exception using '#{name}'; not sending email to notify" )
641
- end
642
- match
643
- end
644
- end
645
-
646
- private
647
-
648
- def refresh_filters
649
- mtime = last_modified_time
650
- if @filters_last_modified_time.nil? || mtime != @filters_last_modified_time
651
- ExceptionHandling.logger.info( "Reloading filter list from: #{@filter_path}. Last loaded time: #{@filters_last_modified_time}. Last modified time: #{mtime}" )
652
- @filters_last_modified_time = mtime # make race condition fall on the side of reloading unnecessarily next time rather than missing a set of changes
653
-
654
- @filters = load_file
655
- end
656
-
657
- rescue => ex # any exceptions
658
- ExceptionHandling::log_error( ex, "ExceptionRegexes::refresh_filters: #{@filter_path}", nil, true)
659
- end
660
-
661
- def load_file
662
- # store all strings from YAML file into regex's on initial load, instead of converting to regex on every exception that is logged
663
- filters = YAML::load_file( @filter_path )
664
- Hash[ filters.map do |filter_name, regexes|
665
- [filter_name, Filter.new( filter_name, regexes )]
666
- end ]
667
- end
668
-
669
- def last_modified_time
670
- File.mtime( @filter_path )
671
- end
672
-
673
- end
674
595
  end
@@ -0,0 +1,65 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module ExceptionHandling
4
+ class ExceptionCatalogTest < ActiveSupport::TestCase
5
+
6
+ context "With stubbed yaml content" do
7
+ setup do
8
+ filter_list = { :exception1 => { error: "my error message" },
9
+ :exception2 => { error: "some other message", session: "misc data" } }
10
+ stub(YAML).load_file { filter_list }
11
+
12
+ # bump modified time up to get the above filter loaded
13
+ stub(File).mtime { incrementing_mtime }
14
+ end
15
+
16
+ context "with loaded data" do
17
+ setup do
18
+ stub(File).mtime { incrementing_mtime }
19
+ @exception_catalog = ExceptionCatalog.new( ExceptionHandling.filter_list_filename )
20
+ @exception_catalog.send :load_file
21
+ end
22
+
23
+ should "have loaded filters" do
24
+ assert_equal 2, @exception_catalog.instance_eval("@filters").size
25
+ end
26
+
27
+ should "find messages in the catalog" do
28
+ assert !@exception_catalog.find( error: "Scott says unlikely to ever match" )
29
+ end
30
+
31
+ should "find matching data" do
32
+ exception_description = @exception_catalog.find(error: "this is my error message, which should match something")
33
+ assert exception_description
34
+ assert_equal :exception1, exception_description.filter_name
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ context "with live yaml content" do
41
+ setup do
42
+ @filename = File.expand_path('../../../../config/exception_filters.yml', __FILE__)
43
+ @exception_catalog = ExceptionCatalog.new( @filename )
44
+ assert_nothing_raised "Loading the exception filter should not raise" do
45
+ @exception_catalog.send :load_file
46
+ end
47
+ end
48
+
49
+ should "load the filter data" do
50
+ assert !@exception_catalog.find( error: "Scott says unlikely to ever match" )
51
+ assert !@exception_catalog.find( error: "Scott says unlikely to ever match" )
52
+ end
53
+
54
+
55
+ end
56
+
57
+ private
58
+
59
+ def incrementing_mtime
60
+ @mtime ||= Time.now
61
+ @mtime += 1.day
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,71 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ module ExceptionHandling
4
+ class ExceptionDescriptionTest < ActiveSupport::TestCase
5
+
6
+ context "Filter" do
7
+
8
+ should "allow direct matching of strings" do
9
+ @f = ExceptionDescription.new(:filter1, :error => "my error message" )
10
+ assert @f.match?( 'error' => "my error message")
11
+ end
12
+
13
+ should "allow direct matching of strings on with symbol keys" do
14
+ @f = ExceptionDescription.new(:filter1, :error => "my error message" )
15
+ assert @f.match?( :error => "my error message")
16
+ end
17
+
18
+ should "complain when no regexps have a value" do
19
+ assert_raise(ArgumentError, "has all blank regexe") { ExceptionDescription.new(:filter1, error: nil) }
20
+ end
21
+
22
+ should "report when an invalid key is passed" do
23
+ assert_raise(ArgumentError, "Unknown section: not_a_parameter") { ExceptionDescription.new(:filter1, error: "my error message", not_a_parameter: false) }
24
+ end
25
+
26
+ should "allow send email to be specified" do
27
+ assert !ExceptionDescription.new(:filter1, error: "my error message", send_email: false ).send_email
28
+ assert ExceptionDescription.new(:filter1, error: "my error message", send_email: true ).send_email
29
+ assert !ExceptionDescription.new(:filter1, error: "my error message" ).send_email
30
+ end
31
+
32
+ should "allow send_metric to be configured" do
33
+ assert !ExceptionDescription.new(:filter1, error: "my error message", send_metric: false ).send_metric
34
+ assert ExceptionDescription.new(:filter1, error: "my error message", send_email: true ).send_metric
35
+ assert ExceptionDescription.new(:filter1, error: "my error message" ).send_metric
36
+ end
37
+
38
+ should "provide metric name" do
39
+ assert_equal "filter1", ExceptionDescription.new(:filter1, error: "my error message" ).metric_name
40
+ assert_equal "some_other_metric_name", ExceptionDescription.new(:filter1, error: "my error message", metric_name: :some_other_metric_name ).metric_name
41
+ end
42
+
43
+ should "replace spaces in metric name" do
44
+ @f = ExceptionDescription.new(:"filter has spaces", :error => "my error message" )
45
+ assert_equal "filter_has_spaces", @f.metric_name
46
+ end
47
+
48
+ should "allow notes to be recorded" do
49
+ assert_equal nil, ExceptionDescription.new(:filter1, error: "my error message" ).notes
50
+ assert_equal "a long string", ExceptionDescription.new(:filter1, error: "my error message", notes: "a long string" ).notes
51
+ end
52
+
53
+ should "not consider config options in the filter set" do
54
+ assert ExceptionDescription.new(:filter1, error: "my error message", send_email: false ).match?( :error => "my error message")
55
+ assert ExceptionDescription.new(:filter1, error: "my error message", send_metric: false ).match?( :error => "my error message")
56
+ assert ExceptionDescription.new(:filter1, error: "my error message", metric_name: "false" ).match?( :error => "my error message")
57
+ assert ExceptionDescription.new(:filter1, error: "my error message", notes: "hey" ).match?( :error => "my error message")
58
+ end
59
+
60
+ should "provide exception details" do
61
+ exception_description = ExceptionDescription.new(:filter1, error: "my error message", notes: "hey" )
62
+
63
+ expected = {"send_metric" => true, "metric_name" => "filter1", "notes" => "hey"}
64
+
65
+ assert_equal expected, exception_description.exception_data
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
@@ -48,7 +48,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
48
48
  end
49
49
 
50
50
  context "configuration" do
51
- def append_organization_info(data)
51
+ def append_organization_info_config(data)
52
52
  begin
53
53
  data[:user_details] = {}
54
54
  data[:user_details][:username] = "CaryP"
@@ -58,7 +58,8 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
58
58
  end
59
59
  end
60
60
 
61
- def log_error_callback(data, ex)
61
+ def log_error_callback_config(data, ex)
62
+ @callback_data = data
62
63
  @fail_count += 1
63
64
  end
64
65
 
@@ -71,17 +72,23 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
71
72
  end
72
73
 
73
74
  should "support a custom_data_hook" do
74
- ExceptionHandling.custom_data_hook = method(:append_organization_info)
75
+ ExceptionHandling.custom_data_hook = method(:append_organization_info_config)
75
76
  ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
76
77
  assert_match(/Invoca Engineering Dept./, ActionMailer::Base.deliveries[-1].body.to_s)
77
78
  ExceptionHandling.custom_data_hook = nil
78
79
  end
79
80
 
80
- should "support a log_error hook" do
81
- ExceptionHandling.post_log_error_hook = method(:log_error_callback)
82
- ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
83
- assert_equal 1, @fail_count
84
- ExceptionHandling.post_log_error_hook = nil
81
+ should "support a log_error hook and pass exception data to it" do
82
+ begin
83
+ ExceptionHandling.post_log_error_hook = method(:log_error_callback_config)
84
+ ExceptionHandling.ensure_safe("mooo") { raise "Some BS" }
85
+ assert_equal 1, @fail_count
86
+ ensure
87
+ ExceptionHandling.post_log_error_hook = nil
88
+ end
89
+
90
+ assert_equal "this is used by a test", @callback_data["notes"]
91
+ assert_match(/this is used by a test/, ActionMailer::Base.deliveries[-1].body.to_s)
85
92
  end
86
93
 
87
94
  should "support rescue exceptions from a log_error hook" do
@@ -90,6 +97,7 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
90
97
  assert_equal 0, @fail_count
91
98
  ExceptionHandling.post_log_error_hook = nil
92
99
  end
100
+
93
101
  end
94
102
 
95
103
  context "Exception Handling" do
@@ -98,18 +106,6 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
98
106
  ExceptionHandling.send(:clear_exception_summary)
99
107
  end
100
108
 
101
- context "exception filter parsing and loading" do
102
- should "happen without an error" do
103
- stub(File).mtime { incrementing_mtime }
104
- exception_filters = ExceptionHandling.send( :exception_filters )
105
- assert( exception_filters.is_a?( ExceptionHandling::ExceptionFilters ) )
106
- assert_nothing_raised "Loading the exception filter should not raise" do
107
- exception_filters.send :load_file
108
- end
109
- assert !exception_filters.filtered?( "Scott says unlikely to ever match" )
110
- end
111
- end
112
-
113
109
  context "ExceptionHandling.ensure_safe" do
114
110
  should "log an exception if an exception is raised." do
115
111
  mock(ExceptionHandling.logger).fatal(/\(blah\):\n.*exception_handling_test\.rb/)
@@ -232,6 +228,11 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
232
228
  Time.now_override = Time.parse( '1986-5-21 4:17 am UTC' )
233
229
  end
234
230
 
231
+ def log_error_callback(data, ex)
232
+ @fail_count += 1
233
+ end
234
+
235
+
235
236
  should "include the timestamp when the exception is logged" do
236
237
  mock(ExceptionHandling.logger).fatal(/\(Error:517033020\) ArgumentError mooo \(blah\):\n.*exception_handling_test\.rb/)
237
238
  b = ExceptionHandling.ensure_safe("mooo") { raise ArgumentError.new("blah") }
@@ -252,12 +253,15 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
252
253
  assert_match(/Exception 2/, ActionMailer::Base.deliveries[-1].subject)
253
254
  end
254
255
 
255
- should "only send 5 of a repeated error" do
256
+ should "only send 5 of a repeated error, but call post hook for every exception" do
257
+ @fail_count = 0
258
+ ExceptionHandling.post_log_error_hook = method(:log_error_callback)
256
259
  assert_emails 5 do
257
260
  10.times do
258
261
  ExceptionHandling.log_error(exception_1)
259
262
  end
260
263
  end
264
+ assert_equal 10, @fail_count
261
265
  end
262
266
 
263
267
  should "only send 5 of a repeated error but don't send summary if 6th is different" do
@@ -671,25 +675,6 @@ class ExceptionHandlingTest < ActiveSupport::TestCase
671
675
  end
672
676
  end
673
677
 
674
- context "Errplane" do
675
- module ErrplaneStub
676
- end
677
-
678
- setup do
679
- set_test_const('Errplane', ErrplaneStub)
680
- end
681
-
682
- should "forward exceptions" do
683
- mock(Errplane).transmit(exception_1, anything)
684
- ExceptionHandling.log_error(exception_1, "context")
685
- end
686
-
687
- should "not forward warnings" do
688
- stub(Errplane).transmit.times(0)
689
- ExceptionHandling.log_warning("warning message")
690
- end
691
- end
692
-
693
678
  private
694
679
 
695
680
  def incrementing_mtime
@@ -27,6 +27,14 @@
27
27
  <br />
28
28
  <br />
29
29
 
30
+ <% if @cleaned_data[:notes] %>
31
+ <b>Exception Notes:</b> (from config/exception.yml)<br />
32
+ <%= @cleaned_data[:notes].gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %>
33
+ <br />
34
+ <br />
35
+ <% end %>
36
+
37
+
30
38
  <b>User summary:</b><br />
31
39
  <% if (user_details = @cleaned_data[:user_details]) && ( user_details[:user] || user_details[:organization] ) %>
32
40
  User: <%= user_details[:user] %> (<%= user_details[:username] %>)<br />
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exception_handling
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Colin Kelley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-28 00:00:00.000000000 Z
11
+ date: 2014-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
@@ -167,12 +167,16 @@ files:
167
167
  - config/exception_filters.yml
168
168
  - exception_handling.gemspec
169
169
  - lib/exception_handling.rb
170
+ - lib/exception_handling/exception_catalog.rb
171
+ - lib/exception_handling/exception_description.rb
170
172
  - lib/exception_handling/log_stub_error.rb
171
173
  - lib/exception_handling/mailer.rb
172
174
  - lib/exception_handling/methods.rb
173
175
  - lib/exception_handling/testing.rb
174
176
  - lib/exception_handling/version.rb
175
177
  - test/test_helper.rb
178
+ - test/unit/exception_handling/exception_catalog_test.rb
179
+ - test/unit/exception_handling/exception_description_test.rb
176
180
  - test/unit/exception_handling/log_error_stub_test.rb
177
181
  - test/unit/exception_handling/mailer_test.rb
178
182
  - test/unit/exception_handling/methods_test.rb
@@ -206,6 +210,8 @@ summary: Invoca's exception handling logger/emailer layer, based on exception_no
206
210
  Works with Rails or EventMachine or EventMachine+Synchrony.
207
211
  test_files:
208
212
  - test/test_helper.rb
213
+ - test/unit/exception_handling/exception_catalog_test.rb
214
+ - test/unit/exception_handling/exception_description_test.rb
209
215
  - test/unit/exception_handling/log_error_stub_test.rb
210
216
  - test/unit/exception_handling/mailer_test.rb
211
217
  - test/unit/exception_handling/methods_test.rb