exception_handling 0.1.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,63 @@
1
+ _ = ActiveSupport::Testing::SetupAndTeardown::ForClassicTestUnit
2
+ module ActiveSupport::Testing::SetupAndTeardown::ForClassicTestUnit
3
+ # This redefinition is unfortunate but test/unit shows us no alternative.
4
+ # Doubly unfortunate: hax to support Mocha's hax.
5
+ # Triply unfortunate to be monkey patching it here. -Colin
6
+ def run(result)
7
+ return if @method_name.to_s == "default_test"
8
+
9
+ mocha_counter = retrieve_mocha_counter(self, result)
10
+ yield(Test::Unit::TestCase::STARTED, name)
11
+ @_result = result
12
+
13
+ begin
14
+ begin
15
+ run_callbacks :setup do
16
+ setup
17
+ __send__(@method_name)
18
+ mocha_verify(mocha_counter) if mocha_counter
19
+ end
20
+ rescue Mocha::ExpectationError => e
21
+ add_failure(e.message, e.backtrace)
22
+ rescue Test::Unit::AssertionFailedError => e
23
+ add_failure(e.message, e.backtrace)
24
+ rescue Exception => e
25
+ raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
26
+ add_error(e)
27
+ ensure
28
+ begin
29
+ teardown
30
+ run_callbacks :teardown
31
+ rescue Mocha::ExpectationError => e
32
+ add_failure(e.message, e.backtrace)
33
+ rescue Test::Unit::AssertionFailedError => e
34
+ add_failure(e.message, e.backtrace)
35
+ rescue Exception => e
36
+ raise if PASSTHROUGH_EXCEPTIONS.include?(e.class)
37
+ add_error(e)
38
+ end
39
+ end
40
+ ensure
41
+ mocha_teardown if mocha_counter
42
+ end
43
+
44
+ result.add_run
45
+ yield(Test::Unit::TestCase::FINISHED, name)
46
+ end
47
+
48
+ protected
49
+
50
+ def retrieve_mocha_counter(test_case, result) #:nodoc:
51
+ if respond_to?(:mocha_verify) # using mocha
52
+ if defined?(Mocha::TestCaseAdapter::AssertionCounter)
53
+ Mocha::TestCaseAdapter::AssertionCounter.new(result)
54
+ elsif defined?(Mocha::Integration::TestUnit::AssertionCounter)
55
+ Mocha::Integration::TestUnit::AssertionCounter.new(result)
56
+ elsif defined?(Mocha::MonkeyPatching::TestUnit::AssertionCounter)
57
+ Mocha::MonkeyPatching::TestUnit::AssertionCounter.new(result)
58
+ else
59
+ Mocha::Integration::AssertionCounter.new(test_case)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,93 @@
1
+ require 'active_support'
2
+ require 'active_support/time'
3
+ require 'active_support/test_case'
4
+ require 'action_mailer'
5
+ require 'shoulda'
6
+ require 'mocha/setup'
7
+ require 'test/mocha_patch'
8
+
9
+ ActionMailer::Base.delivery_method = :test
10
+
11
+ _ = ActiveSupport
12
+ _ = ActiveSupport::TestCase
13
+
14
+ class ActiveSupport::TestCase
15
+ @@constant_overrides = []
16
+
17
+ setup do
18
+ unless @@constant_overrides.nil? || @@constant_overrides.empty?
19
+ raise "Uh-oh! constant_overrides left over: #{@@constant_overrides.inspect}"
20
+ end
21
+
22
+ Time.now_override = nil
23
+
24
+ ActionMailer::Base.deliveries.clear
25
+ end
26
+
27
+ teardown do
28
+ @@constant_overrides && @@constant_overrides.reverse.each do |parent_module, k, v|
29
+ ExceptionHandling.ensure_safe "constant cleanup #{k.inspect}, #{parent_module}(#{parent_module.class})::#{v.inspect}(#{v.class})" do
30
+ silence_warnings do
31
+ if v == :never_defined
32
+ parent_module.send(:remove_const, k)
33
+ else
34
+ parent_module.const_set(k, v)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ @@constant_overrides = []
40
+ end
41
+
42
+ def set_test_const(const_name, value)
43
+ const_name.is_a?(Symbol) and const_name = const_name.to_s
44
+ const_name.is_a?(String) or raise "Pass the constant name, not its value!"
45
+
46
+ final_parent_module = final_const_name = nil
47
+ original_value =
48
+ const_name.split('::').reduce(Object) do |parent_module, nested_const_name|
49
+ parent_module == :never_defined and raise "You need to set each parent constant earlier! #{nested_const_name}"
50
+ final_parent_module = parent_module
51
+ final_const_name = nested_const_name
52
+ parent_module.const_get(nested_const_name) rescue :never_defined
53
+ end
54
+
55
+ @@constant_overrides << [final_parent_module, final_const_name, original_value]
56
+
57
+ silence_warnings { final_parent_module.const_set(final_const_name, value) }
58
+ end
59
+
60
+ def assert_emails(expected, message = nil)
61
+ if block_given?
62
+ original_count = ActionMailer::Base.deliveries.size
63
+ yield
64
+ else
65
+ original_count = 0
66
+ end
67
+ assert_equal expected, ActionMailer::Base.deliveries.size - original_count, "wrong number of emails#{ ': ' + message.to_s if message}"
68
+ end
69
+ end
70
+
71
+ class Time
72
+ class << self
73
+ attr_reader :now_override
74
+
75
+ def now_override= override_time
76
+ if ActiveSupport::TimeWithZone === override_time
77
+ override_time = override_time.localtime
78
+ else
79
+ override_time.nil? || Time === override_time or raise "override_time should be a Time object, but was a #{override_time.class.name}"
80
+ end
81
+ @now_override = override_time
82
+ end
83
+
84
+ unless defined? @@_old_now_defined
85
+ alias old_now now
86
+ @@_old_now_defined = true
87
+ end
88
+ end
89
+
90
+ def self.now
91
+ now_override ? now_override.dup : old_now
92
+ end
93
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <title>Exception Escalation</title>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
+ </head>
8
+ <body>
9
+ <h3><%=h @summary %></h3>
10
+ <b>Error #:</b> <%= h(@cleaned_data[:timestamp]).presence || '<i>no error #</i>'.html_safe -%><br />
11
+ <b>Exception:</b> <%= h(@cleaned_data[:error_string]).gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %><br />
12
+ <b>Server:</b> <%= h(@server).presence || '<i>hostname not set</i>'.html_safe -%><br />
13
+ </body>
14
+ </html>
@@ -0,0 +1,81 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <title>Exception Email</title>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
+ </head>
8
+ <body>
9
+
10
+ <% if @cleaned_data[:first_seen_at] %>
11
+ <p> This exception occurred <%= @cleaned_data[:occurrences] %> times since <%= @cleaned_data[:first_seen_at] %>.</p>
12
+ <% end %>
13
+
14
+ <b>Error #:</b> <%= @cleaned_data[:timestamp] -%><br />
15
+ <b>Server:</b> <%= @cleaned_data[:server].presence || '<i>hostname not set</i>'.html_safe -%><br />
16
+
17
+ <b>URL:</b><br />
18
+ <% if (request = @cleaned_data[:request]) %>
19
+ <%= request[:url] || '<i>no URL in request data</i>'.html_safe %> <br />
20
+ Referred from: <%= @cleaned_data[:environment]['HTTP_REFERER'] || '<i>no referrer</i>'.html_safe -%><br/>
21
+ <% else %>
22
+ <i>no URL accessed</i>
23
+ <% end %>
24
+ <br />
25
+ <br />
26
+
27
+ <b>User summary:</b><br />
28
+ <% if (user_details = @cleaned_data[:user_details]) && ( user_details[:user] || user_details[:organization] ) %>
29
+ User: <%= user_details[:user] %> (<%= user_details[:username] %>)<br />
30
+ Organization: <%= user_details[:organization] %> <br />
31
+
32
+ <% if user_details[:impersonated_organization] %>
33
+ <br />
34
+ <b>Impersonating:</b><br />
35
+ Organization: <%= user_details[:impersonated_organization] %>
36
+ <% end %>
37
+ <% else %>
38
+ <i>No user logged in.</i>
39
+ <% end %>
40
+
41
+ <br />
42
+ <br />
43
+ <hr />
44
+
45
+ <h3>Exception:</h3>
46
+ <%= h(@cleaned_data[:error]).gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %>
47
+
48
+ <br />
49
+ <br />
50
+
51
+ <h3>Where:</h3>
52
+ <% location = @cleaned_data[:location] %>
53
+ <%= "#{location[:controller]}##{location[:action]}<br />" if location && location[:controller] -%>
54
+ <%= "#{location[:file]}, line #{location[:line]}<br />" if location && location[:file] -%>
55
+
56
+ <% for section in ExceptionHandling::SECTIONS %>
57
+ <% section_value = @cleaned_data[section] %>
58
+ <% if section_value %>
59
+ <h3><%= section.to_s.capitalize -%>:</h3>
60
+ <pre style="font-size: 12px; font-family: 'Courier New','Arial',sans-serif">
61
+ <%= case section_value
62
+ when Hash
63
+ section_value[:to_s]
64
+ when Array
65
+ section_value.join( "\n" )
66
+ when NilClass # omitted
67
+ else
68
+ if section_value.respond_to?(:to_s)
69
+ section_value.to_s
70
+ else
71
+ raise "Unexpected value #{section_value.inspect} for section #{section}"
72
+ end
73
+ end
74
+ -%>
75
+ </pre>
76
+ <% end %>
77
+
78
+ <% end %>
79
+
80
+ </body>
81
+ </html>
@@ -0,0 +1,82 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4
+ <head>
5
+ <title>Exception Email</title>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
+ </head>
8
+ <body>
9
+
10
+ <% if @cleaned_data[:first_seen_at] %>
11
+ <p> This exception occurred <%= @cleaned_data[:occurrences] %> times since <%= @cleaned_data[:first_seen_at] %>.</p>
12
+ <% end %>
13
+
14
+ <b>Error # </b><%= @cleaned_data[:timestamp] -%><br />
15
+
16
+ <b>URL:</b><br />
17
+ <% if (request = @cleaned_data[:request]) %>
18
+ <%= request[:url] || '<i>no URL in request data</i>'.html_safe %> <br />
19
+ Referred from: <%= (@cleaned_data[:environment]['HTTP_REFERER'] || '<i>no referrer</i>').html_safe %>
20
+ <% else %>
21
+ <i>no URL accessed</i>
22
+ <% end %>
23
+ <br />
24
+ <br />
25
+
26
+ <b>User summary:</b><br />
27
+ <% if (user_details = @cleaned_data[:user_details]) && ( user_details[:user] || user_details[:organization] ) %>
28
+ User: <%= h user_details[:user] %> (<%= h user_details[:username] %>)<br />
29
+ Organization: <%= user_details[:organization] %> <br />
30
+ <%= "Network: #{h user_details[:organization].network if user_details[:organization]}" if !user_details[:organization].is_a?(Network) %>
31
+
32
+ <% if @cleaned_data[:user_details][:impersonated_organization] %>
33
+ <br />
34
+ <b>Impersonating:</b><br />
35
+ Organization: <%= h @cleaned_data[:user_details][:impersonated_organization] %>
36
+ <% end %>
37
+ <% else %>
38
+ <i>No user logged in.</i>
39
+ <% end %>
40
+
41
+ <br />
42
+ <br />
43
+ <hr />
44
+
45
+ <h3>Exception:</h3>
46
+ <span id="error">
47
+ <%= h(@cleaned_data[:error]).gsub("\n","<br/>\n").gsub(/ {2,}/) { |spaces| '&nbsp;'*spaces.size }.html_safe %>
48
+ </span>
49
+
50
+ <br />
51
+ <br />
52
+
53
+ <h3>Where:</h3>
54
+ <%= "#{ h location[:controller]}##{ h location[:action]}<br />".html_safe if (location = @cleaned_data[:location]) && location[:controller] -%>
55
+ <%= "#{ h location[:file]}, line #{ h location[:line]}<br />".html_safe if (location = @cleaned_data[:location]) && location[:file] -%>
56
+
57
+ <br />
58
+
59
+
60
+
61
+ <% for section in ExceptionHandling::SECTIONS %>
62
+ <% section_value = @cleaned_data[section] %>
63
+ <% if section_value %>
64
+ <h3><%= section.to_s.capitalize -%>:</h3>
65
+ <pre id="<%= section.to_s.capitalize -%>" style="font-size: 12px; font-family: 'Courier New',Arial,sans-serif">
66
+ <%= case section_value
67
+ when Hash
68
+ section_value[:to_s]
69
+ when Array
70
+ section_value.join( "\n" )
71
+ when NilClass # omitted
72
+ else
73
+ raise "Unexpected value #{section_value.inspect} for section #{section}"
74
+ end
75
+ -%>
76
+ </pre>
77
+ <% end %>
78
+
79
+ <% end %>
80
+
81
+ </body>
82
+ </html>
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exception_handling
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Colin Kelley
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2013-08-26 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ type: :runtime
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ segments:
27
+ - 0
28
+ - 12
29
+ - 10
30
+ version: 0.12.10
31
+ name: eventmachine
32
+ requirement: *id001
33
+ prerelease: false
34
+ - !ruby/object:Gem::Dependency
35
+ type: :runtime
36
+ version_requirements: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 3
42
+ - 2
43
+ - 13
44
+ version: 3.2.13
45
+ name: activesupport
46
+ requirement: *id002
47
+ prerelease: false
48
+ - !ruby/object:Gem::Dependency
49
+ type: :runtime
50
+ version_requirements: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 3
56
+ - 2
57
+ - 13
58
+ version: 3.2.13
59
+ name: actionpack
60
+ requirement: *id003
61
+ prerelease: false
62
+ - !ruby/object:Gem::Dependency
63
+ type: :runtime
64
+ version_requirements: &id004 !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 3
70
+ - 2
71
+ - 13
72
+ version: 3.2.13
73
+ name: actionmailer
74
+ requirement: *id004
75
+ prerelease: false
76
+ - !ruby/object:Gem::Dependency
77
+ type: :development
78
+ version_requirements: &id005 !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ segments:
83
+ - 0
84
+ - 9
85
+ version: "0.9"
86
+ name: rake
87
+ requirement: *id005
88
+ prerelease: false
89
+ - !ruby/object:Gem::Dependency
90
+ type: :development
91
+ version_requirements: &id006 !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "="
94
+ - !ruby/object:Gem::Version
95
+ segments:
96
+ - 3
97
+ - 1
98
+ - 1
99
+ version: 3.1.1
100
+ name: shoulda
101
+ requirement: *id006
102
+ prerelease: false
103
+ - !ruby/object:Gem::Dependency
104
+ type: :development
105
+ version_requirements: &id007 !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "="
108
+ - !ruby/object:Gem::Version
109
+ segments:
110
+ - 0
111
+ - 13
112
+ - 0
113
+ version: 0.13.0
114
+ name: mocha
115
+ requirement: *id007
116
+ prerelease: false
117
+ description: Exception handling logger/emailer
118
+ email:
119
+ - colindkelley@gmail.com
120
+ executables: []
121
+
122
+ extensions: []
123
+
124
+ extra_rdoc_files: []
125
+
126
+ files:
127
+ - .gitignore
128
+ - Gemfile
129
+ - Gemfile.lock
130
+ - LICENSE
131
+ - README
132
+ - Rakefile
133
+ - config/exception_filters.yml
134
+ - exception_handling.gemspec
135
+ - lib/exception_handling.rb
136
+ - lib/exception_handling/version.rb
137
+ - lib/exception_handling_mailer.rb
138
+ - test/exception_handling_test.rb
139
+ - test/mocha_patch.rb
140
+ - test/test_helper.rb
141
+ - views/exception_handling/mailer/escalation_notification.html.erb
142
+ - views/exception_handling/mailer/exception_notification.html.erb
143
+ - views/exception_handling/mailer/log_parser_exception_notification.html.erb
144
+ has_rdoc: true
145
+ homepage: https://github.com/RingRevenue/exception_handling
146
+ licenses: []
147
+
148
+ post_install_message:
149
+ rdoc_options: []
150
+
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ segments:
165
+ - 0
166
+ version: "0"
167
+ requirements: []
168
+
169
+ rubyforge_project:
170
+ rubygems_version: 1.3.6
171
+ signing_key:
172
+ specification_version: 3
173
+ summary: RingRevenue's exception handling logger/emailer layer, based on exception_notifier. Works with Rails or EventMachine or EventMachine+Synchrony.
174
+ test_files:
175
+ - test/exception_handling_test.rb
176
+ - test/mocha_patch.rb
177
+ - test/test_helper.rb