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.
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.path.to_s) + "\n")
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 STDOUT if config.log_path.nil?
71
+ return $stdout if config.log_path.nil?
71
72
 
72
73
  @output_file ||= File.open(config.log_path, mode: 'a')
73
74
  end
@@ -6,7 +6,7 @@ require 'postmortem/adapters/mail'
6
6
  require 'postmortem/adapters/pony'
7
7
 
8
8
  module Postmortem
9
- # Adapters for various email senders (e.g. ActionMailer).
9
+ # Adapters for various email senders (e.g. Mail, Pony).
10
10
  module Adapters
11
11
  end
12
12
  end
@@ -7,53 +7,18 @@ module Postmortem
7
7
  private
8
8
 
9
9
  def adapted
10
- {
11
- from: mail.from,
12
- reply_to: mail.reply_to,
13
- to: mail.to,
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 text?
45
- return true unless mail.has_content_type?
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, @data.public_send(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: @data.text_part&.decoded, html_body: @data.html_part&.decoded })
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
 
@@ -20,6 +20,10 @@ module Postmortem
20
20
  @identity_path.write(identity.content)
21
21
  end
22
22
 
23
+ def uri
24
+ "file://#{path}##{@mail.id}"
25
+ end
26
+
23
27
  private
24
28
 
25
29
  def index
@@ -35,7 +35,7 @@ module Postmortem
35
35
  end
36
36
 
37
37
  def encoded_mail
38
- Base64.encode64(mail_data.merge(id: Digest::MD5.hexdigest(mail_data.to_json)).to_json).split("\n").join
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
@@ -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
- # SMTP delivery is handled by Mail plugin further down the stack
10
- return _original_mail(options) if options[:via].to_s == 'smtp'
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
- result = _original_mail(options) unless Postmortem.config.mail_skip_delivery
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Postmortem
4
- VERSION = '0.2.3'
4
+ VERSION = '0.3.1'
5
5
  end
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.3.0')
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', '~> 0.88.0'
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.2.3
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-02-24 00:00:00.000000000 Z
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: 0.88.0
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: 0.88.0
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.3.0
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