postmortem 0.2.1 → 0.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f42efab8900862f405e8f2d297dcc4b64880cfbfd354cacd829743b98d745bd
4
- data.tar.gz: bee3ff5412d6998399a3139744f80b0c97afa497c0d3e837c743d559fef6da16
3
+ metadata.gz: e0231d0fd15050a1535451df10e0a700f2218345be118d911c495285c35548c3
4
+ data.tar.gz: 5427f04480ae4b3d59fc169949429ab17e8451cff4a7b008b84c5457766fde6b
5
5
  SHA512:
6
- metadata.gz: 3e4152b134d8e9f5dcf6ef0eda380e89322135ca552b70ef8fea65970568b4116ebc1a97b709acd157fef68a0b9002d2cdb47a1f452b714da197af837beb75df
7
- data.tar.gz: fa587024f07200df9d44eb9d457d19d57a500bab0f5ecd29f310af5ee700ffda888df069c322d6dd366fe072147d975d62b10d347d1798af0d423d68d12535b9
6
+ metadata.gz: e7568bba76a5439455684d7691216eb01e3e18165693ef649d85e981ed4cb88ab9fcfbb34abcc0d801e2080dcbd6ec4b6e10534221998c227a821e01c33d0593
7
+ data.tar.gz: 7f7467751d3d4975d908e322f58c2cf1c0948c213ef6a01e28c5071db52f682fcec41c515fe21067cae95cd4d4924e1dac5682e069a5d4f734d0abb9db0707d9
data/README.md CHANGED
@@ -23,7 +23,7 @@ Add the gem to your application's Gemfile:
23
23
 
24
24
  ```ruby
25
25
  group :development, :test do
26
- gem 'postmortem', '~> 0.2.1'
26
+ gem 'postmortem', '~> 0.2.3'
27
27
  end
28
28
  ```
29
29
 
@@ -62,7 +62,8 @@
62
62
  </div>
63
63
  </div>
64
64
  </div>
65
- <iframe id="index-iframe" src="index.html"></iframe>
65
+ <iframe id="index-iframe" src="postmortem_index.html"></iframe>
66
+ <iframe id="identity-iframe" src="postmortem_identity.html"></iframe>
66
67
  <script type="text/javascript">
67
68
  <%= javascript_dependencies %>
68
69
  </script>
data/layout/layout.css CHANGED
@@ -147,7 +147,7 @@
147
147
  height: 100%;
148
148
  }
149
149
 
150
- #index-iframe {
150
+ #index-iframe, #identity-iframe {
151
151
  display: none;
152
152
  }
153
153
 
data/layout/layout.js CHANGED
@@ -1,11 +1,14 @@
1
1
  (function () {
2
2
  let previousInbox;
3
3
  let currentView;
4
+ let reloadIdentityIframeTimeout;
5
+ let identityUuid;
4
6
  let indexUuid;
5
7
  let inboxInitialized = false;
6
8
 
7
9
  var htmlIframeDocument = document.querySelector("#html-iframe").contentDocument;
8
10
  var indexIframe = document.querySelector("#index-iframe");
11
+ var identityIframe = document.querySelector("#identity-iframe");
9
12
  var textIframeDocument = document.querySelector("#text-iframe").contentDocument;
10
13
  var sourceIframeDocument = document.querySelector("#source-iframe").contentDocument;
11
14
  var sourceHighlightBundle = [
@@ -22,16 +25,21 @@
22
25
  const initialize = () => {
23
26
  loadMail(initialData);
24
27
 
25
- let reloadIframeTimeout = setTimeout(() => indexIframe.src += '', 3000);
28
+ reloadIdentityIframeTimeout = setTimeout(() => identityIframe.src += '', 3000);
26
29
 
27
30
  window.addEventListener('message', function (ev) {
28
- clearTimeout(reloadIframeTimeout);
29
- reloadIframeTimeout = setTimeout(() => indexIframe.src += '', 3000);
30
- if (indexUuid !== ev.data.uuid) renderInbox(ev.data.mails);
31
- indexUuid = ev.data.uuid;
31
+ switch (ev.data.type) {
32
+ case 'index':
33
+ renderInbox(ev.data.uuid, ev.data.mails);
34
+ break;
35
+ case 'identity':
36
+ compareIdentity(ev.data.uuid);
37
+ break;
38
+ };
32
39
  });
33
40
 
34
41
  setInterval(function () { indexIframe.contentWindow.postMessage('HELO', '*'); }, 1000);
42
+ setInterval(function () { identityIframe.contentWindow.postMessage('HELO', '*'); }, 1000);
35
43
 
36
44
  toolbar.html.onclick = function (ev) { setView('html', ev); };
37
45
  toolbar.text.onclick = function (ev) { setView('text', ev); };
@@ -215,25 +223,36 @@
215
223
  $("#download-link").attr('href', uri);
216
224
  };
217
225
 
218
- const renderInbox = function (mails) {
219
- const html = mails.map((mail, invertedIndex) => {
220
- const index = (mails.length - 1) - invertedIndex;
226
+ const compareIdentity = (uuid) => {
227
+ clearTimeout(reloadIdentityIframeTimeout);
228
+ reloadIdentityIframeTimeout = setTimeout(() => identityIframe.src += '', 3000);
229
+ if (identityUuid !== uuid) indexIframe.src += '';
230
+ identityUuid = uuid;
231
+ };
232
+
233
+ const renderInbox = (uuid, mails) => {
234
+ if (uuid === indexUuid) {
235
+ return;
236
+ }
237
+ const mailsById = {};
238
+ const html = mails.map((mail, index) => {
221
239
  const parsedTimestamp = new Date(mail.timestamp);
222
240
  const timestampSpan = `<span class="timestamp">${parsedTimestamp.toLocaleString()}</span>`;
223
241
  const classes = ['list-group-item', 'inbox-item'];
224
- if (window.location.hash === '#' + index) classes.push('active');
225
- return `<li data-email-index="${index}" class="${classes.join(' ')}"><a title="${mail.subject}" href="javascript:void(0)">${mail.subject}</a>${timestampSpan}</li>`
242
+ if (window.location.hash === '#' + mail.id) classes.push('active');
243
+ mailsById[mail.id] = mail;
244
+ return `<li data-email-id="${mail.id}" class="${classes.join(' ')}"><a title="${mail.subject}" href="javascript:void(0)">${mail.subject}</a>${timestampSpan}</li>`
226
245
  });
227
246
  if (arrayIdentical(html, previousInbox)) return;
228
247
  previousInbox = html;
229
248
  $('#inbox').html('<ul class="list-group">' + html.join('\n') + '</ul>');
230
249
  $('.inbox-item').click((ev) => {
231
250
  const $target = $(ev.currentTarget);
232
- const index = $target.data('email-index');
251
+ const id = $target.data('email-id');
233
252
  $('.inbox-item').removeClass('active');
234
253
  $target.addClass('active');
235
- window.location.hash = index;
236
- setTimeout(() => loadMail(mails[index].content), 0);
254
+ window.location.hash = id;
255
+ setTimeout(() => loadMail(mailsById[id].content), 0);
237
256
  });
238
257
 
239
258
  if (!inboxInitialized) {
@@ -242,6 +261,7 @@
242
261
  setVisible(inbox, true);
243
262
  }
244
263
  inboxInitialized = true;
264
+ indexUuid = uuid;
245
265
  };
246
266
 
247
267
  initialize();
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+ <div data-uuid="<%= uuid %>" id="identity">
5
+ </div>
6
+ <script>
7
+ window.addEventListener('message', function (ev) {
8
+ const identity = document.querySelector('#identity');
9
+ const uuid = identity.dataset.uuid;
10
+ const type = 'identity';
11
+ ev.source.postMessage({ uuid, type }, '*');
12
+ });
13
+
14
+ setInterval(function () {
15
+ window.location.reload();
16
+ }, 3000);
17
+
18
+ </script>
19
+ </body>
20
+ </html>
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <body>
4
- <div data-uuid="<%= SecureRandom.uuid %>" id="index">
4
+ <div data-uuid="<%= uuid %>" id="index">
5
5
  ### INDEX START
6
6
  <% encoded_index.each do |encoded| -%>
7
7
  <%= encoded %>
@@ -13,16 +13,13 @@
13
13
  const filter = (line) => line !== '' && !line.startsWith('### INDEX');
14
14
  const index = document.querySelector('#index');
15
15
  const uuid = index.dataset.uuid;
16
+ const type = 'index';
16
17
  const mails = index.innerText
17
18
  .split('\n')
18
19
  .filter(filter)
19
20
  .map(line => JSON.parse(atob(line)));
20
- ev.source.postMessage({ uuid, mails }, '*');
21
+ ev.source.postMessage({ uuid, type, mails }, '*');
21
22
  });
22
-
23
- setInterval(function () {
24
- window.location.reload();
25
- }, 3000);
26
23
  </script>
27
24
  </body>
28
25
  </html>
data/lib/postmortem.rb CHANGED
@@ -8,12 +8,14 @@ require 'mail'
8
8
  require 'erb'
9
9
  require 'json'
10
10
  require 'cgi'
11
+ require 'digest'
11
12
 
12
13
  require 'postmortem/version'
13
14
  require 'postmortem/adapters'
14
15
  require 'postmortem/delivery'
15
16
  require 'postmortem/layout'
16
17
  require 'postmortem/configuration'
18
+ require 'postmortem/identity'
17
19
  require 'postmortem/index'
18
20
 
19
21
  # HTML email inspection tool.
@@ -10,7 +10,7 @@ module Postmortem
10
10
  %i[from reply_to to cc bcc subject]
11
11
  .map { |field| [field, @data.public_send(field)] }
12
12
  .to_h
13
- .merge({ text_body: @data.text_part, html_body: @data.html_part })
13
+ .merge({ text_body: @data.text_part&.decoded, html_body: @data.html_part&.decoded })
14
14
  end
15
15
 
16
16
  def mail
@@ -7,8 +7,9 @@ module Postmortem
7
7
 
8
8
  def initialize(mail)
9
9
  @mail = mail
10
- @path = Postmortem.config.preview_directory.join('emails.html')
11
- @index_path = Postmortem.config.preview_directory.join('index.html')
10
+ @path = Postmortem.config.preview_directory.join('index.html')
11
+ @index_path = Postmortem.config.preview_directory.join('postmortem_index.html')
12
+ @identity_path = Postmortem.config.preview_directory.join('postmortem_identity.html')
12
13
  end
13
14
 
14
15
  def record
@@ -16,20 +17,17 @@ module Postmortem
16
17
  content = Layout.new(@mail).content
17
18
  path.write(content)
18
19
  index_path.write(index.content)
20
+ @identity_path.write(identity.content)
19
21
  end
20
22
 
21
23
  private
22
24
 
23
25
  def index
24
- @index ||= Index.new(index_path, path, timestamp, @mail)
26
+ @index ||= Index.new(index_path, path, @mail)
25
27
  end
26
28
 
27
- def timestamp
28
- @timestamp ||= Time.now
29
- end
30
-
31
- def token
32
- SecureRandom.hex(4)
29
+ def identity
30
+ @identity ||= Identity.new
33
31
  end
34
32
 
35
33
  def subject
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Postmortem
4
+ # Provides an HTML document that announces a unique ID to a parent page via JS message events.
5
+ class Identity
6
+ def content
7
+ ERB.new(File.read(path), nil, '-').result(binding)
8
+ end
9
+
10
+ private
11
+
12
+ def uuid
13
+ @uuid ||= SecureRandom.uuid
14
+ end
15
+
16
+ def path
17
+ File.expand_path(File.join(__dir__, '..', '..', 'layout', 'postmortem_identity.html.erb'))
18
+ end
19
+ end
20
+ end
@@ -3,10 +3,9 @@
3
3
  module Postmortem
4
4
  # Generates and parses an index of previously-sent emails.
5
5
  class Index
6
- def initialize(index_path, mail_path, timestamp, mail)
6
+ def initialize(index_path, mail_path, mail)
7
7
  @index_path = index_path
8
8
  @mail_path = mail_path
9
- @timestamp = timestamp.iso8601
10
9
  @mail = mail
11
10
  end
12
11
 
@@ -21,6 +20,14 @@ module Postmortem
21
20
 
22
21
  private
23
22
 
23
+ def uuid
24
+ @uuid ||= SecureRandom.uuid
25
+ end
26
+
27
+ def timestamp
28
+ Time.now.iso8601
29
+ end
30
+
24
31
  def encoded_index
25
32
  return [encoded_mail] unless @index_path.file?
26
33
 
@@ -28,13 +35,13 @@ module Postmortem
28
35
  end
29
36
 
30
37
  def encoded_mail
31
- Base64.encode64(mail_data.to_json).split("\n").join
38
+ Base64.encode64(mail_data.merge(id: Digest::MD5.hexdigest(mail_data.to_json)).to_json).split("\n").join
32
39
  end
33
40
 
34
41
  def mail_data
35
42
  {
36
43
  subject: @mail.subject || '(no subject)',
37
- timestamp: @timestamp,
44
+ timestamp: timestamp,
38
45
  path: @mail_path,
39
46
  content: @mail.serializable
40
47
  }
@@ -54,7 +61,7 @@ module Postmortem
54
61
  end
55
62
 
56
63
  def template_path
57
- File.expand_path(File.join(__dir__, '..', '..', 'layout', 'index.html.erb'))
64
+ File.expand_path(File.join(__dir__, '..', '..', 'layout', 'postmortem_index.html.erb'))
58
65
  end
59
66
  end
60
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Postmortem
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.3'
5
5
  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.1
4
+ version: 0.2.3
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-01-12 00:00:00.000000000 Z
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mail
@@ -145,9 +145,10 @@ files:
145
145
  - layout/dependencies.css
146
146
  - layout/dependencies.js
147
147
  - layout/headers_template.html
148
- - layout/index.html.erb
149
148
  - layout/layout.css
150
149
  - layout/layout.js
150
+ - layout/postmortem_identity.html.erb
151
+ - layout/postmortem_index.html.erb
151
152
  - lib/postmortem.rb
152
153
  - lib/postmortem/adapters.rb
153
154
  - lib/postmortem/adapters/action_mailer.rb
@@ -156,6 +157,7 @@ files:
156
157
  - lib/postmortem/adapters/pony.rb
157
158
  - lib/postmortem/configuration.rb
158
159
  - lib/postmortem/delivery.rb
160
+ - lib/postmortem/identity.rb
159
161
  - lib/postmortem/index.rb
160
162
  - lib/postmortem/layout.rb
161
163
  - lib/postmortem/plugins/action_mailer.rb