simple_email_exception_notifier 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +163 -0
- data/Rakefile +2 -0
- data/lib/exception_notifier/simple_email_notifier.rb +85 -0
- data/lib/simple_email_exception_notifier.rb +6 -0
- data/lib/simple_email_exception_notifier/formatter.rb +29 -0
- data/lib/simple_email_exception_notifier/message.rb +44 -0
- data/lib/simple_email_exception_notifier/version.rb +3 -0
- data/simple_email_exception_notifier.gemspec +28 -0
- data/spec/integration/deliver_notification_in_rack_app_spec.rb +37 -0
- data/spec/lib/exception_notifier/simple_email_notifier_spec.rb +186 -0
- data/spec/lib/simple_email_exception_notifier/formatter_spec.rb +34 -0
- data/spec/lib/simple_email_exception_notifier/message_spec.rb +68 -0
- data/spec/spec_helper.rb +21 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cb949b38914440af13f4c0f87d40a0ec1747289c
|
4
|
+
data.tar.gz: 1302dfb7df66c0a1168a67460031382b07e1cffc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 225f72ea8dd0a6652e7b1e0e6a1a24afc16cd2c9f4da8d156c697fe2719dca9fa8d022c20bd1f7b11c2f63997c4042297a1a0aa0f69c16d59fd941d49909441f
|
7
|
+
data.tar.gz: fc5903aba335a9b7beac2819d266ba531921c8d1b5281ba99ed84c3fc308900e10669c24fdd137beae29a046e156386b5ab9a6a18444c5c3187b6afd66645e83
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Artur Hebda
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# SimpleEmailExceptionNotifier
|
2
|
+
|
3
|
+
Email notifier for [exception_notification](https://github.com/smartinez87/exception_notification) that does not rely on ActionMailer and can be used outside Rails, i.e. with Grape. As of now it supports only text emails. As a delivery method it can use either [Mail](https://github.com/mikel/mail) or [Pony](https://github.com/benprew/pony) or custom method you define.
|
4
|
+
|
5
|
+
## Why?
|
6
|
+
|
7
|
+
I have created a few apps using Rails in conjuction with Exception Notification and I liked it. Then I have started a new app which consists of API and backend processing only. It's based on [Grape](https://github.com/intridea/grape) and [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord). Once I have added `require 'exception_notifier/email_notifier'` to configure it app stopped to boot, because this file requires ActionMailer, which in turn does `require 'active_support/rails'`. That makes ActiveRecord start thinking that there is a Rails app and raises `NoMethodError: undefined method 'env' for Rails:Module`.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'simple_email_exception_notifier'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install simple_email_exception_notifier
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
### Migrating from Rails
|
28
|
+
|
29
|
+
In Rails with built-in email notifier you would do like this:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# config/initializers/smtp.rb
|
33
|
+
Rails.application.config.action_mailer.delivery_method = :smtp
|
34
|
+
Rails.application.config.action_mailer.smtp_settings = {
|
35
|
+
# See: http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration-for-gmail
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# config/initializers/exception_notification.rb
|
41
|
+
require 'exception_notification/rails'
|
42
|
+
|
43
|
+
ExceptionNotification.configure do |config|
|
44
|
+
config.ignore_if do |exception, options|
|
45
|
+
Rails.env.development? || Rails.env.test?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Notifiers =================================================================
|
49
|
+
|
50
|
+
# Email notifier sends notifications by email.
|
51
|
+
config.add_notifier :email, {
|
52
|
+
:email_prefix => "[ERROR] ",
|
53
|
+
:sender_address => "no-reply@example.com",
|
54
|
+
:exception_recipients => %w[artur@boostcommedia.no]
|
55
|
+
}
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
Let's think about the config above as a reference point to highlight what is different in Rack app. Check out the config below.
|
60
|
+
|
61
|
+
### Rack app
|
62
|
+
|
63
|
+
I tend to keep structure of the directories similar to what Rails offers, therefore I'll keep the same filenames as in the Rails config above. For Grape and Sinatra it means that those files have to required manually.
|
64
|
+
|
65
|
+
In Rack app with this notifier you would do similar to this:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# Gemfile
|
69
|
+
gem 'simple_email_exception_notifier'
|
70
|
+
gem 'mail'
|
71
|
+
```
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
# config/initializers/smtp.rb
|
75
|
+
require 'mail'
|
76
|
+
|
77
|
+
Mail.defaults do
|
78
|
+
delivery_method :smtp, {
|
79
|
+
# See: http://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration-for-gmail
|
80
|
+
}
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# config/initializers/exception_notification.rb
|
86
|
+
require 'exception_notification/rack'
|
87
|
+
require 'simple_email_exception_notifier'
|
88
|
+
|
89
|
+
ExceptionNotification.configure do |config|
|
90
|
+
config.ignore_if do |exception, options|
|
91
|
+
env = ENV['RACK_ENV'] || 'development'
|
92
|
+
%w(development test).include?(env)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Notifiers =================================================================
|
96
|
+
|
97
|
+
# Email notifier sends notifications by email.
|
98
|
+
config.add_notifier :simple_email, {
|
99
|
+
:email_prefix => "[ERROR] ",
|
100
|
+
:sender_address => "no-reply@example.com",
|
101
|
+
:exception_recipients => %w[artur@boostcommedia.no]
|
102
|
+
}
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
### Include ExceptionNotification in Rack stack
|
107
|
+
|
108
|
+
Let's say that what you usually do is to run a Rack app with `run App`.
|
109
|
+
In such case you would do as follows:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
run Rack::Builder.new do
|
113
|
+
use ExceptionNotification::Rack
|
114
|
+
run App # your app
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
### Pony instead of Mail
|
119
|
+
|
120
|
+
If you would like to use Pony instead of Mail, it's as simple as:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
# Gemfile
|
124
|
+
gem 'simple_email_exception_notifier'
|
125
|
+
gem 'pony'
|
126
|
+
```
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
# config/initializers/smtp.rb
|
130
|
+
require 'pony'
|
131
|
+
|
132
|
+
Pony.options = {
|
133
|
+
:via => :smtp,
|
134
|
+
:via_options => {
|
135
|
+
# See: https://github.com/benprew/pony
|
136
|
+
# As far as I have tested options are identical to those for Mail
|
137
|
+
}
|
138
|
+
}
|
139
|
+
```
|
140
|
+
|
141
|
+
### Custom delivery method
|
142
|
+
|
143
|
+
You can customize delivery method if you like. If not defined, at the moment of notifying the lookup is as follows: Mail, Pony, raising error.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
# config/initializers/exception_notification.rb
|
147
|
+
config.add_notifier :simple_email, {
|
148
|
+
:email_prefix => "[ERROR] ",
|
149
|
+
:sender_address => "no-reply@example.com",
|
150
|
+
:exception_recipients => %w[artur@boostcommedia.no],
|
151
|
+
:delivery_method => ->(params) {
|
152
|
+
# do something with :from, :to, :subject and :body.
|
153
|
+
}
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
## Contributing
|
158
|
+
|
159
|
+
1. Fork it ( https://github.com/aenain/simple_email_exception_notifier/fork )
|
160
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
161
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
162
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
163
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'simple_email_exception_notifier/message'
|
2
|
+
|
3
|
+
module ExceptionNotifier
|
4
|
+
class SimpleEmailNotifier
|
5
|
+
class UndefinedDeliveryError < StandardError; end
|
6
|
+
|
7
|
+
ENV_DATA_KEY = 'exception_notifier.exception_data'.freeze
|
8
|
+
|
9
|
+
def self.default_options
|
10
|
+
{
|
11
|
+
:sender_address => %("Exception Notifier" <exception.notifier@example.com>),
|
12
|
+
:exception_recipients => [],
|
13
|
+
:email_prefix => "[ERROR] ",
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(options)
|
18
|
+
@options = self.class.default_options.merge(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(exception, options = {})
|
22
|
+
env = extract_env_from_options!(options)
|
23
|
+
|
24
|
+
delivery_method.call(
|
25
|
+
from: @options.fetch(:sender_address),
|
26
|
+
to: @options.fetch(:exception_recipients),
|
27
|
+
subject: compose_subject(exception, env, options),
|
28
|
+
body: compose_message(exception, env, options)
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def delivery_method
|
33
|
+
@options.fetch(:delivery_method) do
|
34
|
+
if defined?(Mail)
|
35
|
+
Mail.method(:deliver)
|
36
|
+
elsif defined?(Pony)
|
37
|
+
Pony.method(:mail)
|
38
|
+
else
|
39
|
+
raise UndefinedDeliveryError.new "Undefined :delivery_method!\n" +
|
40
|
+
" You can add gem 'mail' or 'pony' to Gemfile or provide custom method.\n" +
|
41
|
+
" It is supposed to send message on #call.\n" +
|
42
|
+
" Arguments are :from, :to, :subject and :body.\n" +
|
43
|
+
" Examples: Mail.method(:deliver), Pony.method(:mail)"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def compose_subject(exception, env, options)
|
49
|
+
[
|
50
|
+
@options.fetch(:email_prefix),
|
51
|
+
env['PATH_INFO'].to_s + ' ',
|
52
|
+
exception.class.name,
|
53
|
+
].join.squeeze(' ')
|
54
|
+
end
|
55
|
+
|
56
|
+
def compose_message(exception, env, options)
|
57
|
+
SimpleEmailExceptionNotifier::Message.compose do |m|
|
58
|
+
m.print_summary(exception)
|
59
|
+
m.print_section('Backtrace', exception.backtrace)
|
60
|
+
|
61
|
+
unless env.empty?
|
62
|
+
m.print_request(Rack::Request.new(env)) if defined?(Rack::Request)
|
63
|
+
m.print_section('Environment', filter_env(env))
|
64
|
+
end
|
65
|
+
|
66
|
+
m.print_section('Data', extract_data(env, options))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def extract_env_from_options!(options)
|
73
|
+
options.delete(:env) || {}
|
74
|
+
end
|
75
|
+
|
76
|
+
def filter_env(env)
|
77
|
+
env.reject { |k, _| k == ENV_DATA_KEY }
|
78
|
+
end
|
79
|
+
|
80
|
+
def extract_data(env, options)
|
81
|
+
env_data = env.fetch(ENV_DATA_KEY, {})
|
82
|
+
env_data.merge(options.fetch(:data, {}))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module SimpleEmailExceptionNotifier
|
4
|
+
class Formatter
|
5
|
+
def text(text)
|
6
|
+
text.to_s
|
7
|
+
end
|
8
|
+
|
9
|
+
def section(title, details)
|
10
|
+
[
|
11
|
+
nil,
|
12
|
+
'-------------------------------',
|
13
|
+
"#{title}:",
|
14
|
+
'-------------------------------',
|
15
|
+
nil,
|
16
|
+
pretty_inspect(details),
|
17
|
+
].join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def pretty_inspect(object)
|
23
|
+
out = StringIO.new
|
24
|
+
PP.pp(object, out)
|
25
|
+
out.rewind
|
26
|
+
out.read.gsub(/(=[>]*)/m, ' \1 ')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'formatter'
|
2
|
+
|
3
|
+
module SimpleEmailExceptionNotifier
|
4
|
+
class Message
|
5
|
+
def initialize(formatter = Formatter.new)
|
6
|
+
@content = []
|
7
|
+
@formatter = formatter
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.compose(formatter = Formatter.new, &block)
|
11
|
+
message = self.new(formatter)
|
12
|
+
block.call(message)
|
13
|
+
message.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def print_summary(exception)
|
17
|
+
opening_line = "#{exception.class} occurred at #{Time.now.utc} :"
|
18
|
+
content << formatter.text(opening_line)
|
19
|
+
content << formatter.text(exception.message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def print_request(request)
|
23
|
+
print_section('Request',
|
24
|
+
url: request.url,
|
25
|
+
http_method: request.request_method,
|
26
|
+
ip_address: request.ip,
|
27
|
+
parameters: request.params,
|
28
|
+
server: Socket.gethostname
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def print_section(title, details)
|
33
|
+
content << formatter.section(title, details)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
content.flatten.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_reader :content, :formatter
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'simple_email_exception_notifier/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "simple_email_exception_notifier"
|
8
|
+
spec.version = SimpleEmailExceptionNotifier::VERSION
|
9
|
+
spec.authors = ["Artur Hebda"]
|
10
|
+
spec.email = ["arturhebda@gmail.com"]
|
11
|
+
spec.summary = %q{Email notifier for exception_notification that does not rely on ActionMailer and can be used with any Rack app.}
|
12
|
+
spec.description = %q{Plugin for exception_notification that can be used outside Rails, i.e. with Grape. As of now it supports only text emails. As a delivery method it can use either Mail or Pony or custom method you define.}
|
13
|
+
spec.homepage = "https://github.com/aenain/simple_email_exception_notifier"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
24
|
+
spec.add_development_dependency "rack", "~> 1.6"
|
25
|
+
spec.add_development_dependency "mail", "~> 2.6"
|
26
|
+
spec.add_development_dependency "pony", "~> 1.11"
|
27
|
+
spec.add_development_dependency "exception_notification", "~> 4.0"
|
28
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'rack/test'
|
4
|
+
require 'rack/builder'
|
5
|
+
require 'exception_notifier'
|
6
|
+
require 'exception_notification/rack'
|
7
|
+
|
8
|
+
RSpec.describe 'error in rack app' do
|
9
|
+
include Rack::Test::Methods
|
10
|
+
|
11
|
+
def app
|
12
|
+
Rack::Builder.new do
|
13
|
+
use ExceptionNotification::Rack,
|
14
|
+
simple_email: {
|
15
|
+
sender_address: 'notifier@example.com',
|
16
|
+
exception_recipients: %w(developers@example.com),
|
17
|
+
}
|
18
|
+
run ->(env) { raise TestError.new('Test message') }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'delivered email' do
|
23
|
+
context 'with mail' do
|
24
|
+
include Mail::Matchers
|
25
|
+
|
26
|
+
before(:each) do
|
27
|
+
Mail::TestMailer.deliveries.clear
|
28
|
+
get '/missing' rescue TestError
|
29
|
+
end
|
30
|
+
|
31
|
+
it { should have_sent_email.from('notifier@example.com') }
|
32
|
+
it { should have_sent_email.to('developers@example.com') }
|
33
|
+
it { should have_sent_email.matching_subject(/TestError/) }
|
34
|
+
it { should have_sent_email.matching_body(/PATH_INFO.*\/missing/) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'exception_notifier/simple_email_notifier'
|
3
|
+
|
4
|
+
RSpec.describe ExceptionNotifier::SimpleEmailNotifier, '#delivery_method' do
|
5
|
+
let(:exception) { TestError.new }
|
6
|
+
|
7
|
+
context 'when Mail defined' do
|
8
|
+
it 'returns Mail.deliver' do
|
9
|
+
hide_const('Pony')
|
10
|
+
mail = class_double('Mail').as_stubbed_const
|
11
|
+
allow(mail).to receive(:deliver)
|
12
|
+
|
13
|
+
method = described_class.new({}).delivery_method
|
14
|
+
|
15
|
+
expect(method).to eq Mail.method(:deliver)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when Pony defined' do
|
20
|
+
it 'returns Pony.mail' do
|
21
|
+
hide_const('Mail')
|
22
|
+
pony = class_double('Pony').as_stubbed_const
|
23
|
+
allow(pony).to receive(:mail)
|
24
|
+
|
25
|
+
method = described_class.new({}).delivery_method
|
26
|
+
|
27
|
+
expect(method).to eq Pony.method(:mail)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with custom :delivery_method' do
|
32
|
+
it 'returns provided method' do
|
33
|
+
delivery = double(:delivery, call: nil)
|
34
|
+
|
35
|
+
method = described_class.new(delivery_method: delivery).delivery_method
|
36
|
+
|
37
|
+
expect(method).to eq delivery
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with no delivery method available' do
|
42
|
+
it 'raises an error' do
|
43
|
+
hide_const('Mail')
|
44
|
+
hide_const('Pony')
|
45
|
+
|
46
|
+
expect {
|
47
|
+
described_class.new({}).delivery_method
|
48
|
+
}.to raise_error(ExceptionNotifier::SimpleEmailNotifier::UndefinedDeliveryError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
RSpec.describe ExceptionNotifier::SimpleEmailNotifier, '#call' do
|
54
|
+
describe 'subject' do
|
55
|
+
let(:exception) { TestError.new }
|
56
|
+
|
57
|
+
context 'with custom :email_prefix' do
|
58
|
+
it 'consists of :email_prefix and exception class name' do
|
59
|
+
delivery = double(:delivery, call: nil)
|
60
|
+
|
61
|
+
described_class.new(
|
62
|
+
email_prefix: 'ERROR ',
|
63
|
+
delivery_method: delivery
|
64
|
+
).call(exception)
|
65
|
+
|
66
|
+
expect(delivery).to have_received(:call)
|
67
|
+
.with(hash_including(subject: 'ERROR TestError'))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with :env' do
|
72
|
+
it 'contains request path' do
|
73
|
+
delivery = double(:delivery, call: nil)
|
74
|
+
env = Rack::MockRequest.env_for('/missing')
|
75
|
+
|
76
|
+
described_class.new(
|
77
|
+
delivery_method: delivery
|
78
|
+
).call(exception, env: env)
|
79
|
+
|
80
|
+
expect(delivery).to have_received(:call)
|
81
|
+
.with(hash_including(subject: '[ERROR] /missing TestError'))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'sender' do
|
87
|
+
let(:exception) { TestError.new }
|
88
|
+
|
89
|
+
context 'with custom :sender_address' do
|
90
|
+
it 'uses the address as :from' do
|
91
|
+
delivery = double(:delivery, call: nil)
|
92
|
+
|
93
|
+
described_class.new(
|
94
|
+
sender_address: 'no-reply@example.com',
|
95
|
+
delivery_method: delivery
|
96
|
+
).call(exception)
|
97
|
+
|
98
|
+
expect(delivery).to have_received(:call)
|
99
|
+
.with(hash_including(from: 'no-reply@example.com'))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'recipients' do
|
105
|
+
let(:exception) { TestError.new }
|
106
|
+
|
107
|
+
context 'with custom :exception_recipients' do
|
108
|
+
it 'uses list of addresses as :to' do
|
109
|
+
delivery = double(:delivery, call: nil)
|
110
|
+
|
111
|
+
described_class.new(
|
112
|
+
exception_recipients: %w(developers@example.com),
|
113
|
+
delivery_method: delivery
|
114
|
+
).call(exception)
|
115
|
+
|
116
|
+
expect(delivery).to have_received(:call)
|
117
|
+
.with(hash_including(to: %w(developers@example.com)))
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'message' do
|
123
|
+
it 'includes backtrace' do
|
124
|
+
delivery = ->(args) { @passed_body = args.fetch(:body) }
|
125
|
+
exception = instance_double('TestError',
|
126
|
+
message: nil,
|
127
|
+
backtrace: %w(some_file.rb:1 other_file.rb:2)
|
128
|
+
)
|
129
|
+
|
130
|
+
described_class.new(delivery_method: delivery).call(exception)
|
131
|
+
|
132
|
+
expect(@passed_body).to include exception.backtrace[0]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'includes exception message' do
|
136
|
+
delivery = ->(args) { @passed_body = args.fetch(:body) }
|
137
|
+
exception = instance_double('TestError',
|
138
|
+
message: 'Some helpful message',
|
139
|
+
backtrace: []
|
140
|
+
)
|
141
|
+
|
142
|
+
described_class.new(delivery_method: delivery).call(exception)
|
143
|
+
|
144
|
+
expect(@passed_body).to include exception.message
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'includes :env' do
|
148
|
+
delivery = ->(args) { @passed_body = args.fetch(:body) }
|
149
|
+
env = Rack::MockRequest.env_for('/missing')
|
150
|
+
|
151
|
+
described_class.new(delivery_method: delivery)
|
152
|
+
.call(TestError.new, env: env)
|
153
|
+
|
154
|
+
expect(@passed_body).to match /PATH_INFO.*\/missing/
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
RSpec.describe ExceptionNotifier::SimpleEmailNotifier, 'naming requirements' do
|
160
|
+
it 'is defined directly in ExceptionNotifier' do
|
161
|
+
namespaces = described_class.name.split('::')
|
162
|
+
|
163
|
+
expect(namespaces.count).to eq 2
|
164
|
+
expect(namespaces[0]).to eq 'ExceptionNotifier'
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'has suffix of Notifier' do
|
168
|
+
expect(described_class.name).to match /Notifier$/
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
RSpec.describe ExceptionNotifier::SimpleEmailNotifier, 'interface' do
|
173
|
+
describe 'constructor' do
|
174
|
+
it 'has one argument' do
|
175
|
+
method = described_class.instance_method(:initialize)
|
176
|
+
expect(method.arity).to eq 1
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe '#call' do
|
181
|
+
it 'has one required and one optional argument' do
|
182
|
+
method = described_class.instance_method(:call)
|
183
|
+
expect(method.arity).to eq -2
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'simple_email_exception_notifier/formatter'
|
3
|
+
|
4
|
+
RSpec.describe SimpleEmailExceptionNotifier::Formatter, '#text' do
|
5
|
+
it 'converts argument to string' do
|
6
|
+
formatted = described_class.new.text(3)
|
7
|
+
|
8
|
+
expect(formatted).to eq '3'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec.describe SimpleEmailExceptionNotifier::Formatter, '#section' do
|
13
|
+
it 'makes title stand out' do
|
14
|
+
separator = /\n[-]+\n/
|
15
|
+
|
16
|
+
formatted = described_class.new.section('title', 'details')
|
17
|
+
|
18
|
+
expect(formatted).to match /\A#{separator}title:#{separator}/m
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'makes details readable' do
|
22
|
+
lots_of_data = {
|
23
|
+
long_key: 3124124,
|
24
|
+
longer_key: 412415,
|
25
|
+
very_long_key: 525156111,
|
26
|
+
super_long_key: 6124125111,
|
27
|
+
extremely_long_key: 721415125125,
|
28
|
+
}
|
29
|
+
|
30
|
+
formatted = described_class.new.section('_', lots_of_data)
|
31
|
+
|
32
|
+
expect(formatted).to match /:long_key => \d+,\n\s+:longer_key/m
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'simple_email_exception_notifier/message'
|
3
|
+
|
4
|
+
RSpec.describe SimpleEmailExceptionNotifier::Message do
|
5
|
+
describe '.compose' do
|
6
|
+
it 'composes message from parts' do
|
7
|
+
content = described_class.compose do |m|
|
8
|
+
m.print_section('First', '_')
|
9
|
+
m.print_section('Second', '_')
|
10
|
+
end
|
11
|
+
|
12
|
+
expect(content).to include('First')
|
13
|
+
expect(content).to include('Second')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#print_summary' do
|
18
|
+
it 'includes class name and message' do
|
19
|
+
message = described_class.new
|
20
|
+
message.print_summary(TestError.new('message'))
|
21
|
+
content = message.to_s
|
22
|
+
|
23
|
+
expect(content).to include('TestError')
|
24
|
+
expect(content).to include('message')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#print_request' do
|
29
|
+
let(:request) do
|
30
|
+
instance_double('Rack::Request',
|
31
|
+
url: 'http://example.com/missing',
|
32
|
+
request_method: 'GET',
|
33
|
+
ip: '238.214.27.14',
|
34
|
+
params: {
|
35
|
+
name: 'John Cage',
|
36
|
+
}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'includes request url' do
|
41
|
+
content = described_class.new.print_request(request).to_s
|
42
|
+
expect(content).to include(request.url)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'includes http method' do
|
46
|
+
content = described_class.new.print_request(request).to_s
|
47
|
+
expect(content).to include(request.request_method)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'includes ip address' do
|
51
|
+
content = described_class.new.print_request(request).to_s
|
52
|
+
expect(content).to include(request.params.fetch(:name))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#print_section' do
|
57
|
+
it 'includes section' do
|
58
|
+
message = described_class.new
|
59
|
+
|
60
|
+
message.print_section('Backtrace', %w(some_file.rb:1 other_file.rb:2))
|
61
|
+
content = message.to_s
|
62
|
+
|
63
|
+
expect(content).to include('Backtrace')
|
64
|
+
expect(content).to include('some_file.rb:1')
|
65
|
+
expect(content).to include('other_file.rb:2')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
3
|
+
require 'rack/mock'
|
4
|
+
require 'mail'
|
5
|
+
require 'pony'
|
6
|
+
|
7
|
+
class TestError < RuntimeError; end
|
8
|
+
|
9
|
+
Mail.defaults do
|
10
|
+
delivery_method :test
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.expect_with :rspec do |expectations|
|
15
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
16
|
+
end
|
17
|
+
|
18
|
+
config.mock_with :rspec do |mocks|
|
19
|
+
mocks.verify_partial_doubles = true
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_email_exception_notifier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Artur Hebda
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.1'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mail
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.6'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pony
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.11'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.11'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: exception_notification
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.0'
|
111
|
+
description: Plugin for exception_notification that can be used outside Rails, i.e.
|
112
|
+
with Grape. As of now it supports only text emails. As a delivery method it can
|
113
|
+
use either Mail or Pony or custom method you define.
|
114
|
+
email:
|
115
|
+
- arturhebda@gmail.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- ".gitignore"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- lib/exception_notifier/simple_email_notifier.rb
|
126
|
+
- lib/simple_email_exception_notifier.rb
|
127
|
+
- lib/simple_email_exception_notifier/formatter.rb
|
128
|
+
- lib/simple_email_exception_notifier/message.rb
|
129
|
+
- lib/simple_email_exception_notifier/version.rb
|
130
|
+
- simple_email_exception_notifier.gemspec
|
131
|
+
- spec/integration/deliver_notification_in_rack_app_spec.rb
|
132
|
+
- spec/lib/exception_notifier/simple_email_notifier_spec.rb
|
133
|
+
- spec/lib/simple_email_exception_notifier/formatter_spec.rb
|
134
|
+
- spec/lib/simple_email_exception_notifier/message_spec.rb
|
135
|
+
- spec/spec_helper.rb
|
136
|
+
homepage: https://github.com/aenain/simple_email_exception_notifier
|
137
|
+
licenses:
|
138
|
+
- MIT
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: '0'
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 2.2.2
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: Email notifier for exception_notification that does not rely on ActionMailer
|
160
|
+
and can be used with any Rack app.
|
161
|
+
test_files:
|
162
|
+
- spec/integration/deliver_notification_in_rack_app_spec.rb
|
163
|
+
- spec/lib/exception_notifier/simple_email_notifier_spec.rb
|
164
|
+
- spec/lib/simple_email_exception_notifier/formatter_spec.rb
|
165
|
+
- spec/lib/simple_email_exception_notifier/message_spec.rb
|
166
|
+
- spec/spec_helper.rb
|