postmortem 0.2.3 → 0.3.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 +4 -4
- data/.gitignore +2 -0
- data/.rubocop.yml +4 -46
- data/.ruby-version +1 -1
- data/Gemfile +0 -2
- data/Makefile +4 -0
- data/README.md +21 -12
- data/layout/default.html.erb +51 -12
- data/layout/dependencies.css +0 -4
- data/layout/dependencies.js +3 -1
- data/layout/favicon.b64 +1 -0
- data/layout/layout.css +155 -15
- data/layout/layout.js +286 -81
- data/lib/postmortem.rb +3 -2
- data/lib/postmortem/adapters.rb +1 -1
- data/lib/postmortem/adapters/action_mailer.rb +6 -41
- data/lib/postmortem/adapters/base.rb +35 -2
- data/lib/postmortem/adapters/mail.rb +3 -3
- data/lib/postmortem/adapters/pony.rb +3 -6
- data/lib/postmortem/delivery.rb +4 -0
- data/lib/postmortem/index.rb +2 -1
- data/lib/postmortem/layout.rb +8 -0
- data/lib/postmortem/plugins/action_mailer.rb +7 -0
- data/lib/postmortem/plugins/pony.rb +7 -3
- data/lib/postmortem/version.rb +1 -1
- data/postmortem.gemspec +6 -3
- metadata +49 -7
- data/doc/screenshot.png +0 -0
data/lib/postmortem.rb
CHANGED
@@ -9,6 +9,7 @@ require 'erb'
|
|
9
9
|
require 'json'
|
10
10
|
require 'cgi'
|
11
11
|
require 'digest'
|
12
|
+
require 'securerandom'
|
12
13
|
|
13
14
|
require 'postmortem/version'
|
14
15
|
require 'postmortem/adapters'
|
@@ -56,7 +57,7 @@ module Postmortem
|
|
56
57
|
private
|
57
58
|
|
58
59
|
def log_delivery(delivery)
|
59
|
-
output_file.write(colorized(delivery.
|
60
|
+
output_file.write("#{colorized(delivery.uri)}\n")
|
60
61
|
output_file.flush
|
61
62
|
end
|
62
63
|
|
@@ -67,7 +68,7 @@ module Postmortem
|
|
67
68
|
end
|
68
69
|
|
69
70
|
def output_file
|
70
|
-
return
|
71
|
+
return $stdout if config.log_path.nil?
|
71
72
|
|
72
73
|
@output_file ||= File.open(config.log_path, mode: 'a')
|
73
74
|
end
|
data/lib/postmortem/adapters.rb
CHANGED
@@ -7,53 +7,18 @@ module Postmortem
|
|
7
7
|
private
|
8
8
|
|
9
9
|
def adapted
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
cc: mail.cc,
|
15
|
-
bcc: normalized_bcc,
|
16
|
-
subject: mail.subject,
|
17
|
-
text_body: text_part,
|
18
|
-
html_body: html_part
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
def text_part
|
23
|
-
return nil unless text?
|
24
|
-
return mail.body.decoded unless mail.text_part
|
25
|
-
|
26
|
-
mail.text_part.decoded
|
27
|
-
end
|
28
|
-
|
29
|
-
def html_part
|
30
|
-
return nil unless html?
|
31
|
-
return mail.body.decoded unless mail.html_part
|
32
|
-
|
33
|
-
mail.html_part.decoded
|
34
|
-
end
|
35
|
-
|
36
|
-
def mail
|
37
|
-
@mail ||= ::Mail.new(@data[:mail])
|
10
|
+
%i[from reply_to to cc subject message_id]
|
11
|
+
.map { |field| [field, mail.public_send(field)] }
|
12
|
+
.to_h
|
13
|
+
.merge({ text_body: text_part, html_body: html_part, bcc: normalized_bcc })
|
38
14
|
end
|
39
15
|
|
40
16
|
def normalized_bcc
|
41
17
|
::Mail.new(to: @data[:bcc]).to
|
42
18
|
end
|
43
19
|
|
44
|
-
def
|
45
|
-
|
46
|
-
return true if mail.content_type.include?('text/plain')
|
47
|
-
return true if mail.multipart? && mail.text_part
|
48
|
-
|
49
|
-
false
|
50
|
-
end
|
51
|
-
|
52
|
-
def html?
|
53
|
-
return true if mail.has_content_type? && mail.content_type.include?('text/html')
|
54
|
-
return true if mail.multipart? && mail.html_part
|
55
|
-
|
56
|
-
false
|
20
|
+
def mail
|
21
|
+
@mail ||= ::Mail.new(@data[:mail])
|
57
22
|
end
|
58
23
|
end
|
59
24
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Postmortem
|
4
4
|
module Adapters
|
5
|
-
FIELDS = %i[from reply_to to cc bcc subject text_body html_body].freeze
|
5
|
+
FIELDS = %i[from reply_to to cc bcc subject text_body html_body message_id].freeze
|
6
6
|
|
7
7
|
# Base interface implementation for all Postmortem adapters.
|
8
8
|
class Base
|
@@ -16,7 +16,11 @@ module Postmortem
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def serializable
|
19
|
-
FIELDS.map { |field| [camelize(field.to_s), public_send(field)] }.to_h
|
19
|
+
(%i[id] + FIELDS).map { |field| [camelize(field.to_s), public_send(field)] }.to_h
|
20
|
+
end
|
21
|
+
|
22
|
+
def id
|
23
|
+
@id ||= SecureRandom.uuid
|
20
24
|
end
|
21
25
|
|
22
26
|
FIELDS.each do |method_name|
|
@@ -46,6 +50,35 @@ module Postmortem
|
|
46
50
|
.map { |substring, index| index.zero? ? substring : substring.capitalize }
|
47
51
|
.join
|
48
52
|
end
|
53
|
+
|
54
|
+
def text_part
|
55
|
+
return nil unless text?
|
56
|
+
return mail.body.decoded unless mail.text_part
|
57
|
+
|
58
|
+
mail.text_part.decoded
|
59
|
+
end
|
60
|
+
|
61
|
+
def html_part
|
62
|
+
return nil unless html?
|
63
|
+
return mail.body.decoded unless mail.html_part
|
64
|
+
|
65
|
+
mail.html_part.decoded
|
66
|
+
end
|
67
|
+
|
68
|
+
def text?
|
69
|
+
return true unless mail.has_content_type?
|
70
|
+
return true if mail.content_type.include?('text/plain')
|
71
|
+
return true if mail.multipart? && mail.text_part
|
72
|
+
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
def html?
|
77
|
+
return true if mail.has_content_type? && mail.content_type.include?('text/html')
|
78
|
+
return true if mail.multipart? && mail.html_part
|
79
|
+
|
80
|
+
false
|
81
|
+
end
|
49
82
|
end
|
50
83
|
end
|
51
84
|
end
|
@@ -7,10 +7,10 @@ module Postmortem
|
|
7
7
|
private
|
8
8
|
|
9
9
|
def adapted
|
10
|
-
%i[from reply_to to cc bcc subject]
|
11
|
-
.map { |field| [field,
|
10
|
+
%i[from reply_to to cc bcc subject message_id]
|
11
|
+
.map { |field| [field, mail.public_send(field)] }
|
12
12
|
.to_h
|
13
|
-
.merge({ text_body:
|
13
|
+
.merge({ text_body: text_part, html_body: html_part })
|
14
14
|
end
|
15
15
|
|
16
16
|
def mail
|
@@ -8,14 +8,11 @@ module Postmortem
|
|
8
8
|
|
9
9
|
def adapted
|
10
10
|
{
|
11
|
-
from: mail.from,
|
12
|
-
reply_to: mail.reply_to,
|
13
|
-
to: mail.to,
|
14
|
-
cc: mail.cc,
|
15
|
-
bcc: mail.bcc,
|
11
|
+
from: mail.from, reply_to: mail.reply_to, to: mail.to, cc: mail.cc, bcc: mail.bcc,
|
16
12
|
subject: mail.subject,
|
17
13
|
text_body: @data[:body],
|
18
|
-
html_body: @data[:html_body]
|
14
|
+
html_body: @data[:html_body],
|
15
|
+
message_id: mail.message_id # We use a synthetic Mail instance so this is a bit useless.
|
19
16
|
}
|
20
17
|
end
|
21
18
|
|
data/lib/postmortem/delivery.rb
CHANGED
data/lib/postmortem/index.rb
CHANGED
@@ -35,7 +35,7 @@ module Postmortem
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def encoded_mail
|
38
|
-
Base64.encode64(mail_data.
|
38
|
+
Base64.encode64(mail_data.to_json).split("\n").join
|
39
39
|
end
|
40
40
|
|
41
41
|
def mail_data
|
@@ -43,6 +43,7 @@ module Postmortem
|
|
43
43
|
subject: @mail.subject || '(no subject)',
|
44
44
|
timestamp: timestamp,
|
45
45
|
path: @mail_path,
|
46
|
+
id: @mail.id,
|
46
47
|
content: @mail.serializable
|
47
48
|
}
|
48
49
|
end
|
data/lib/postmortem/layout.rb
CHANGED
@@ -37,6 +37,14 @@ module Postmortem
|
|
37
37
|
default_layout_directory.join('headers_template.html').read
|
38
38
|
end
|
39
39
|
|
40
|
+
def favicon_b64
|
41
|
+
default_layout_directory.join('favicon.b64').read
|
42
|
+
end
|
43
|
+
|
44
|
+
def upload_url
|
45
|
+
ENV.fetch('POSTMORTEM_DELIVERY_URL', 'https://postmortem.delivery/emails')
|
46
|
+
end
|
47
|
+
|
40
48
|
private
|
41
49
|
|
42
50
|
def default_layout_directory
|
@@ -1,5 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
ActiveSupport::Notifications.subscribe 'deliver.action_mailer' do |*args|
|
4
|
+
delivery_method = Rails.try(:application)
|
5
|
+
&.try(:config)
|
6
|
+
&.try(:action_mailer)
|
7
|
+
&.try(:delivery_method)
|
8
|
+
next if delivery_method.nil?
|
9
|
+
next if %i[sendmail smtp].include?(delivery_method&.to_sym) # Delegate to Mail plugin.
|
10
|
+
|
4
11
|
Postmortem.record_delivery(Postmortem::Adapters::ActionMailer.new(args.extract_options!))
|
5
12
|
end
|
@@ -6,10 +6,14 @@ module Pony
|
|
6
6
|
alias _original_mail mail
|
7
7
|
|
8
8
|
def mail(options)
|
9
|
-
|
10
|
-
|
9
|
+
strategy = options[:via].to_s
|
10
|
+
# Pony uses the Mail gem for smtp delivery so we catch these further down the stack to
|
11
|
+
# avoid duplicating deliveries.
|
12
|
+
return _original_mail(options) if strategy == 'smtp'
|
11
13
|
|
12
|
-
|
14
|
+
# When delivery method is "test" we do not want to interfere as ActionMailer.deliveries
|
15
|
+
# (which delegates to Mail::TestMailer) is typically used in tests.
|
16
|
+
result = _original_mail(options) if strategy == 'test' || !Postmortem.config.mail_skip_delivery
|
13
17
|
Postmortem.record_delivery(Postmortem::Adapters::Pony.new(options))
|
14
18
|
result
|
15
19
|
end
|
data/lib/postmortem/version.rb
CHANGED
data/postmortem.gemspec
CHANGED
@@ -12,14 +12,14 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = 'Preview HTML emails in your browser during development'
|
13
13
|
spec.homepage = 'https://github.com/bobf/postmortem'
|
14
14
|
spec.license = 'MIT'
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
16
|
|
17
17
|
spec.metadata['homepage_uri'] = spec.homepage
|
18
18
|
spec.metadata['source_code_uri'] = spec.homepage
|
19
19
|
spec.metadata['changelog_uri'] = 'https://github.com/bobf/postmortem/blob/master/README.md'
|
20
20
|
|
21
21
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|doc)/}) }
|
23
23
|
end
|
24
24
|
spec.bindir = 'bin'
|
25
25
|
spec.executables = []
|
@@ -28,10 +28,13 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_runtime_dependency 'mail', '~> 2.7'
|
29
29
|
|
30
30
|
spec.add_development_dependency 'actionmailer', '~> 6.0'
|
31
|
+
spec.add_development_dependency 'devpack', '~> 0.3.2'
|
32
|
+
spec.add_development_dependency 'faker', '~> 2.17'
|
31
33
|
spec.add_development_dependency 'pony', '~> 1.13'
|
32
34
|
spec.add_development_dependency 'rspec', '~> 3.9'
|
33
35
|
spec.add_development_dependency 'rspec-its', '~> 1.3'
|
34
|
-
spec.add_development_dependency 'rubocop', '~>
|
36
|
+
spec.add_development_dependency 'rubocop', '~> 1.10'
|
37
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.2'
|
35
38
|
spec.add_development_dependency 'strong_versions', '~> 0.4.5'
|
36
39
|
spec.add_development_dependency 'timecop', '~> 0.9.1'
|
37
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: postmortem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Farrell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mail
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '6.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: devpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.3.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.3.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faker
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.17'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.17'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: pony
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +114,28 @@ dependencies:
|
|
86
114
|
requirements:
|
87
115
|
- - "~>"
|
88
116
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
117
|
+
version: '1.10'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.10'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.2'
|
90
132
|
type: :development
|
91
133
|
prerelease: false
|
92
134
|
version_requirements: !ruby/object:Gem::Requirement
|
93
135
|
requirements:
|
94
136
|
- - "~>"
|
95
137
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
138
|
+
version: '2.2'
|
97
139
|
- !ruby/object:Gem::Dependency
|
98
140
|
name: strong_versions
|
99
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,10 +182,10 @@ files:
|
|
140
182
|
- Rakefile
|
141
183
|
- bin/console
|
142
184
|
- bin/setup
|
143
|
-
- doc/screenshot.png
|
144
185
|
- layout/default.html.erb
|
145
186
|
- layout/dependencies.css
|
146
187
|
- layout/dependencies.js
|
188
|
+
- layout/favicon.b64
|
147
189
|
- layout/headers_template.html
|
148
190
|
- layout/layout.css
|
149
191
|
- layout/layout.js
|
@@ -180,7 +222,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
180
222
|
requirements:
|
181
223
|
- - ">="
|
182
224
|
- !ruby/object:Gem::Version
|
183
|
-
version: 2.
|
225
|
+
version: 2.5.0
|
184
226
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
227
|
requirements:
|
186
228
|
- - ">="
|
@@ -188,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
230
|
version: '0'
|
189
231
|
requirements: []
|
190
232
|
rubyforge_project:
|
191
|
-
rubygems_version: 2.7.6
|
233
|
+
rubygems_version: 2.7.6.2
|
192
234
|
signing_key:
|
193
235
|
specification_version: 4
|
194
236
|
summary: Development HTML Email Inspection Tool
|
data/doc/screenshot.png
DELETED
Binary file
|