exception_no 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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDg1YTk5NDNkNmRhNTA4NGVmNzE1ZTU3YzcwZDM3MGRlOWUyZWFiZg==
5
+ data.tar.gz: !binary |-
6
+ Yjc4Mzk0NjQzZWFjNjI4MWRjMDQ1OTA2Y2FiOGY3MWNjZjA2MjAyNA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YjQ1NGJmNjY1YWM2ZDc1OGJkNjMzYWQzZGZjNmJjNTE2YmNlZDBmMzdjYTgy
10
+ NDM1M2EzNmJkOWZiOTU0MzRkNWZlZDQyNTExZmU0NzY3MGJjMDgzYmJiYzMw
11
+ NTA0NDI5MjFmYWZiNjRjMDExZjZhMjRmMGZmNDkzZjA2MmYwZTI=
12
+ data.tar.gz: !binary |-
13
+ MjVhNzkxY2E2ZDFhY2UzNzUzMjJkMTIzNmY0MmZmY2NmNDdjOTU4ODVmOGRm
14
+ NDYwYjU1NTgyMmZlNWYwNGJhMmIwNTFlZDRiODk0M2FjMTVjOTQ1M2E1N2E0
15
+ ODU4ZGQxM2FmNTczNGE4NjI3NzY4MzRkM2M5Y2MzYTJlNTAzODg=
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .gs
data/Makefile ADDED
@@ -0,0 +1,4 @@
1
+ test:
2
+ cutest test/*.rb
3
+
4
+ .PHONY: test
@@ -0,0 +1,16 @@
1
+ require File.expand_path("lib/exception_no", File.dirname(__FILE__))
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "exception_no"
5
+ s.version = ExceptionNo::VERSION
6
+ s.summary = "Truly basic exception notification."
7
+ s.authors = ["Educabilia", "Damian Janowski"]
8
+ s.email = ["opensource@educabilia.com", "djanowski@dimaion.com"]
9
+ s.homepage = "https://github.com/djanowski/exception_no"
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
13
+
14
+ s.add_development_dependency "mini-smtp-server"
15
+ s.add_development_dependency "cutest"
16
+ end
@@ -0,0 +1,81 @@
1
+ require "net/smtp"
2
+ require "erb"
3
+
4
+ class ExceptionNo
5
+ VERSION = "0.0.1"
6
+
7
+ attr_accessor :backtrace_filter
8
+
9
+ def initialize(config = {})
10
+ @config = config
11
+ @template = ERB.new(TEMPLATE)
12
+
13
+ @backtrace_filter = -> line { true }
14
+ end
15
+
16
+ def _notify(exception, options = {})
17
+ body = @template.result(binding)
18
+
19
+ Net::SMTP.start(@config.fetch(:host), @config.fetch(:port, 25)) do |smtp|
20
+ smtp.send_message(body, @config.fetch(:from), @config.fetch(:to))
21
+ end
22
+ end
23
+
24
+ def notify(exception, options = {})
25
+ begin
26
+ _notify(exception, options)
27
+ rescue => notification_error
28
+ $stderr.write("*** FAILED SENDING ERROR NOTIFICATION\n")
29
+ $stderr.write("*** #{notification_error.class}: #{notification_error}\n")
30
+ $stderr.write("*** #{exception.class}: #{exception.message}\n")
31
+
32
+ exception.backtrace.each do |line|
33
+ $stderr.write("*** #{line}\n")
34
+ end
35
+ end
36
+ end
37
+
38
+ TEMPLATE = (<<-'EMAIL').gsub(/^ {2}/, '')
39
+ From: <%= @config[:from_alias] %> <<%= @config[:from] %>>
40
+ To: <<%= @config[:to] %>>
41
+ Subject: <%= exception.class %>: <%= exception.message.split.join(" ") %>
42
+
43
+ <%= options[:body] %>
44
+
45
+ <%= "~" * 80 %>
46
+
47
+ A <%= exception.class.to_s %> occured: <%= exception.to_s %>
48
+
49
+ <%= exception.backtrace.select { |line| @backtrace_filter.call(line) }.join("\n") if exception.backtrace %>
50
+ EMAIL
51
+
52
+ class Middleware
53
+ def initialize(app, config = {})
54
+ @app = app
55
+ @notifier = ExceptionNo.new(config)
56
+ end
57
+
58
+ def call(env)
59
+ begin
60
+ @app.call(env)
61
+ rescue Exception => e
62
+ @notifier.notify(e, body: extract_env(env))
63
+
64
+ raise e
65
+ end
66
+ end
67
+
68
+ def extract_env(env)
69
+ req = Rack::Request.new(env)
70
+
71
+ parts = []
72
+
73
+ parts << "#{req.request_method} #{req.url}"
74
+ parts << "User-Agent: #{req.user_agent}" if req.user_agent
75
+ parts << "Referrer: #{req.referrer}" if req.referrer
76
+ parts << "Cookie: #{req.env["HTTP_COOKIE"]}" if req.cookies.size > 0
77
+
78
+ parts.join("\n")
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,67 @@
1
+ require_relative "prelude"
2
+
3
+ setup do
4
+ ExceptionNo.new(
5
+ host: "127.0.0.1",
6
+ port: 2525,
7
+ to: "root@localhost",
8
+ from: "service@localhost"
9
+ )
10
+ end
11
+
12
+ test "deliver exception notification" do |notifier|
13
+ ex = ArgumentError.new("Really bad argument")
14
+
15
+ notifier.notify(ex)
16
+
17
+ email = $smtp.outbox.last
18
+
19
+ assert_equal email[:to], "<root@localhost>"
20
+ assert_equal email[:from], "<service@localhost>"
21
+
22
+ headers, body = parse_email(email[:data])
23
+
24
+ assert_equal headers["Subject"], "ArgumentError: Really bad argument"
25
+ assert body.include?("A ArgumentError occured: Really bad argument")
26
+ end
27
+
28
+ test "exception messages with multiple lines" do |notifier|
29
+ notifier.notify(ArgumentError.new("A really\nbad\nargument"))
30
+
31
+ headers, body = parse_email($smtp.outbox.last[:data])
32
+
33
+ assert_equal headers["Subject"], "ArgumentError: A really bad argument"
34
+ end
35
+
36
+ test "includes backtrace information" do |notifier|
37
+ begin
38
+ raise ArgumentError, "A bad argument"
39
+ rescue Exception => ex
40
+ notifier.notify(ex)
41
+ end
42
+
43
+ headers, body = parse_email($smtp.outbox.last[:data])
44
+
45
+ assert body.include?(__FILE__)
46
+ assert body.include?(Gem.path.first)
47
+ end
48
+
49
+ test "allows to filter the backtrace" do |notifier|
50
+ notifier.backtrace_filter = -> line do
51
+ !line.include?(Gem.path.first)
52
+ end
53
+
54
+ begin
55
+ raise ArgumentError, "A bad argument"
56
+ rescue Exception => ex
57
+ notifier.notify(ex)
58
+ end
59
+
60
+ headers, body = parse_email($smtp.outbox.last[:data])
61
+
62
+ assert body.include?(__FILE__)
63
+ assert !body.include?(Gem.path.first)
64
+ end
65
+
66
+ $smtp.stop
67
+ $smtp.join
@@ -0,0 +1,66 @@
1
+ require_relative "prelude"
2
+
3
+ require "rack"
4
+
5
+ setup do
6
+ Rack::Builder.new do |builder|
7
+ builder.use ExceptionNo::Middleware,
8
+ host: "127.0.0.1",
9
+ port: 2525,
10
+ to: "root@localhost",
11
+ from: "service@localhost"
12
+
13
+ builder.run(-> env { 1 / 0 })
14
+ end
15
+ end
16
+
17
+ test "re-raises exceptions" do |app|
18
+ assert_raise(ZeroDivisionError) { app.call(Rack::MockRequest.env_for("/")) }
19
+ end
20
+
21
+ test "extracts interesting stuff from the request" do |app|
22
+ env = Rack::MockRequest.env_for(
23
+ "/baz",
24
+ "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)",
25
+ "HTTP_REFERER" => "/other",
26
+ "HTTP_COOKIE" => "foo=bar",
27
+ )
28
+
29
+ begin
30
+ app.call(env)
31
+ rescue ZeroDivisionError
32
+ end
33
+
34
+ headers, body = parse_email($smtp.outbox.last[:data])
35
+
36
+ assert_equal headers["Subject"], "ZeroDivisionError: divided by 0"
37
+ assert body.include?("GET http://example.org/baz")
38
+ assert body.include?("User-Agent: Mozilla/4.0 (compatible)")
39
+ assert body.include?("Referrer: /other")
40
+ assert body.include?("Cookie: foo=bar")
41
+ end
42
+
43
+ test "doesn't raise when the notification fails" do |app|
44
+ app = Rack::Builder.new do |builder|
45
+ builder.use ExceptionNo::Middleware,
46
+ host: "127.0.0.1",
47
+ port: 2526,
48
+ to: "root@localhost",
49
+ from: "service@localhost"
50
+
51
+ builder.run(-> env { 1 / 0 })
52
+ end
53
+
54
+ env = Rack::MockRequest.env_for(
55
+ "/baz",
56
+ "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)",
57
+ "HTTP_REFERER" => "/other",
58
+ "HTTP_COOKIE" => "foo=bar",
59
+ )
60
+
61
+ assert_raise(ZeroDivisionError) do
62
+ capture_stderr do
63
+ app.call(env)
64
+ end
65
+ end
66
+ end
data/test/prelude.rb ADDED
@@ -0,0 +1,62 @@
1
+ require "cutest"
2
+ require "mini-smtp-server"
3
+
4
+ require_relative "../lib/exception_no"
5
+
6
+ class SMTPServer < MiniSmtpServer
7
+ def outbox
8
+ @outbox ||= []
9
+ end
10
+
11
+ def new_message_event(message)
12
+ outbox << message
13
+ true
14
+ end
15
+ end
16
+
17
+ def parse_email(raw)
18
+ headers = {}
19
+ body = []
20
+ reading_body = false
21
+
22
+ raw.each_line do |line|
23
+ if !reading_body && line == "\r\n"
24
+ reading_body = true
25
+ next
26
+ end
27
+
28
+ if reading_body
29
+ body << line
30
+ else
31
+ key, value = line.split(": ", 2)
32
+ headers[key] = value && value.chomp
33
+ end
34
+ end
35
+
36
+ body.pop
37
+
38
+ return headers, body.join("").chomp
39
+ end
40
+
41
+ def capture_stderr
42
+ old, $stderr = $stderr, StringIO.new
43
+
44
+ begin
45
+ yield
46
+ ensure
47
+ $stderr = old
48
+ end
49
+ end
50
+
51
+ $smtp = SMTPServer.new(2525, "127.0.0.1")
52
+ $smtp.start
53
+
54
+ until SMTPServer.in_service?(2525)
55
+ end
56
+
57
+ at_exit do
58
+ if $smtp
59
+ $smtp.stop
60
+ $smtp.join
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exception_no
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Educabilia
8
+ - Damian Janowski
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mini-smtp-server
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: cutest
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ description:
43
+ email:
44
+ - opensource@educabilia.com
45
+ - djanowski@dimaion.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - .gitignore
51
+ - Makefile
52
+ - exception_no.gemspec
53
+ - lib/exception_no.rb
54
+ - test/exception_no.rb
55
+ - test/middleware.rb
56
+ - test/prelude.rb
57
+ homepage: https://github.com/djanowski/exception_no
58
+ licenses: []
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.0.0
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Truly basic exception notification.
80
+ test_files: []