letter_opener_web 1.4.0 → 2.0.0

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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/brakeman-analysis.yml +46 -0
  3. data/.github/workflows/main.yml +34 -0
  4. data/.github/workflows/release-gem.yml +32 -0
  5. data/.rspec +1 -1
  6. data/.rubocop.yml +4 -13
  7. data/.rubocop_todo.yml +19 -0
  8. data/CHANGELOG.md +16 -1
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +1 -1
  11. data/README.md +31 -22
  12. data/app/controllers/letter_opener_web/application_controller.rb +1 -0
  13. data/app/controllers/letter_opener_web/letters_controller.rb +10 -6
  14. data/app/models/letter_opener_web/letter.rb +36 -5
  15. data/app/views/layouts/letter_opener_web/_javascripts.html.erb +26 -559
  16. data/app/views/layouts/letter_opener_web/_styles.html.erb +3 -63
  17. data/app/views/layouts/letter_opener_web/js/_favcount.html.erb +104 -0
  18. data/app/views/layouts/letter_opener_web/js/_jquery.html.erb +7 -0
  19. data/app/views/layouts/letter_opener_web/letters.html.erb +4 -3
  20. data/app/views/layouts/letter_opener_web/styles/_bootstrap.html.erb +9 -0
  21. data/app/views/layouts/letter_opener_web/styles/_icon.html.erb +2 -0
  22. data/app/views/layouts/letter_opener_web/styles/_letters.html.erb +70 -0
  23. data/app/views/letter_opener_web/letters/_item.html.erb +10 -0
  24. data/app/views/letter_opener_web/letters/index.html.erb +11 -46
  25. data/config/routes.rb +5 -5
  26. data/letter_opener_web.gemspec +11 -8
  27. data/lib/letter_opener_web/delivery_method.rb +1 -1
  28. data/lib/letter_opener_web/version.rb +1 -1
  29. data/lib/letter_opener_web.rb +1 -0
  30. data/spec/controllers/letter_opener_web/letters_controller_spec.rb +18 -7
  31. data/spec/dummy/app/assets/config/manifest.js +0 -2
  32. data/spec/dummy/app/assets/stylesheets/application.css +0 -16
  33. data/spec/dummy/app/channels/application_cable/channel.rb +6 -0
  34. data/spec/dummy/app/channels/application_cable/connection.rb +6 -0
  35. data/spec/dummy/app/controllers/application_controller.rb +0 -1
  36. data/spec/dummy/app/{assets/javascripts → javascript/packs}/application.js +2 -0
  37. data/spec/dummy/app/jobs/application_job.rb +9 -0
  38. data/spec/dummy/app/models/application_record.rb +5 -0
  39. data/spec/dummy/app/models/concerns/.keep +0 -0
  40. data/spec/dummy/app/views/layouts/application.html.erb +4 -7
  41. data/spec/dummy/bin/rails +2 -4
  42. data/spec/dummy/bin/rake +2 -4
  43. data/spec/dummy/bin/setup +7 -10
  44. data/spec/dummy/config/application.rb +21 -7
  45. data/spec/dummy/config/environments/development.rb +44 -6
  46. data/spec/dummy/config/environments/production.rb +51 -14
  47. data/spec/dummy/config/environments/test.rb +30 -6
  48. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -4
  49. data/spec/dummy/config/initializers/assets.rb +5 -5
  50. data/spec/dummy/config/initializers/backtrace_silencers.rb +5 -3
  51. data/spec/dummy/config/initializers/content_security_policy.rb +29 -0
  52. data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
  53. data/spec/dummy/config/initializers/permissions_policy.rb +12 -0
  54. data/spec/dummy/config/locales/en.yml +11 -1
  55. data/spec/dummy/config/puma.rb +18 -22
  56. data/spec/dummy/config/routes.rb +1 -4
  57. data/spec/dummy/config.ru +1 -0
  58. data/spec/dummy/public/404.html +6 -6
  59. data/spec/dummy/public/422.html +6 -6
  60. data/spec/dummy/public/500.html +6 -6
  61. data/spec/dummy/storage/.keep +0 -0
  62. data/spec/letter_opener_web_spec.rb +2 -2
  63. data/spec/models/letter_opener_web/letter_spec.rb +61 -16
  64. data/spec/spec_helper.rb +8 -0
  65. metadata +87 -43
  66. data/.travis.yml +0 -24
  67. data/spec/dummy/app/controllers/home_controller.rb +0 -10
  68. data/spec/dummy/app/mailers/contact_mailer.rb +0 -14
  69. data/spec/dummy/app/views/contact_mailer/new_message.html.erb +0 -21
  70. data/spec/dummy/app/views/contact_mailer/new_message.text.erb +0 -3
  71. data/spec/dummy/app/views/home/index.html.erb +0 -50
  72. data/spec/dummy/bin/bundle +0 -5
  73. data/spec/dummy/bin/update +0 -31
  74. data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -10
  75. data/spec/dummy/config/initializers/session_store.rb +0 -5
  76. data/spec/dummy/config/secrets.yml +0 -22
  77. data/spec/dummy/config/spring.rb +0 -8
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ # Be sure to restart your server when you modify this file.
3
+
4
+ # Define an application-wide content security policy
5
+ # For further information see the following documentation
6
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
7
+
8
+ # Rails.application.config.content_security_policy do |policy|
9
+ # policy.default_src :self, :https
10
+ # policy.font_src :self, :https, :data
11
+ # policy.img_src :self, :https, :data
12
+ # policy.object_src :none
13
+ # policy.script_src :self, :https
14
+ # policy.style_src :self, :https
15
+
16
+ # # Specify URI for violation reports
17
+ # # policy.report_uri "/csp-violation-report-endpoint"
18
+ # end
19
+
20
+ # If you are using UJS then enable automatic nonce generation
21
+ # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
22
+
23
+ # Set the nonce only to specific directives
24
+ # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
25
+
26
+ # Report CSP violations to a specified URI
27
+ # For further information see the following documentation:
28
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
29
+ # Rails.application.config.content_security_policy_report_only = true
@@ -3,4 +3,6 @@
3
3
  # Be sure to restart your server when you modify this file.
4
4
 
5
5
  # Configure sensitive parameters which will be filtered from the log file.
6
- Rails.application.config.filter_parameters += [:password]
6
+ Rails.application.config.filter_parameters += %i[
7
+ passw secret token _key crypt salt certificate otp ssn
8
+ ]
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # Define an application-wide HTTP permissions policy. For further
3
+ # information see https://developers.google.com/web/updates/2018/06/feature-policy
4
+ #
5
+ # Rails.application.config.permissions_policy do |f|
6
+ # f.camera :none
7
+ # f.gyroscope :none
8
+ # f.microphone :none
9
+ # f.usb :none
10
+ # f.fullscreen :self
11
+ # f.payment :self, "https://secure.example.com"
12
+ # end
@@ -16,8 +16,18 @@
16
16
  #
17
17
  # This would use the information in config/locales/es.yml.
18
18
  #
19
+ # The following keys must be escaped otherwise they will not be retrieved by
20
+ # the default I18n backend:
21
+ #
22
+ # true, false, on, off, yes, no
23
+ #
24
+ # Instead, surround them with single quotes.
25
+ #
26
+ # en:
27
+ # 'true': 'foo'
28
+ #
19
29
  # To learn more, please read the Rails Internationalization guide
20
- # available at http://guides.rubyonrails.org/i18n.html.
30
+ # available at https://guides.rubyonrails.org/i18n.html.
21
31
 
22
32
  en:
23
33
  hello: "Hello world"
@@ -1,24 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Puma can serve each request in a thread from an internal thread pool.
4
- # The `threads` method setting takes two numbers a minimum and maximum.
4
+ # The `threads` method setting takes two numbers: a minimum and maximum.
5
5
  # Any libraries that use thread pools should be configured to match
6
6
  # the maximum value specified for Puma. Default is set to 5 threads for minimum
7
- # and maximum, this matches the default thread size of Active Record.
7
+ # and maximum; this matches the default thread size of Active Record.
8
8
  #
9
- threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i
10
- threads threads_count, threads_count
9
+ max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
10
+ min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count }
11
+ threads min_threads_count, max_threads_count
11
12
 
12
- # Specifies the `port` that Puma will listen on to receive requests, default is 3000.
13
+ # Specifies the `worker_timeout` threshold that Puma will use to wait before
14
+ # terminating a worker in development environments.
13
15
  #
14
- port ENV.fetch('PORT') { 3000 }
16
+ worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development'
17
+
18
+ # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
19
+ #
20
+ port ENV.fetch('PORT', 3000)
15
21
 
16
22
  # Specifies the `environment` that Puma will run in.
17
23
  #
18
- environment ENV.fetch('RAILS_ENV') { 'development' }
24
+ environment ENV.fetch('RAILS_ENV', 'development')
25
+
26
+ # Specifies the `pidfile` that Puma will use.
27
+ pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid')
19
28
 
20
29
  # Specifies the number of `workers` to boot in clustered mode.
21
- # Workers are forked webserver processes. If using threads and workers together
30
+ # Workers are forked web server processes. If using threads and workers together
22
31
  # the concurrency of the application would be max `threads` * `workers`.
23
32
  # Workers do not work on JRuby or Windows (both of which do not support
24
33
  # processes).
@@ -28,22 +37,9 @@ environment ENV.fetch('RAILS_ENV') { 'development' }
28
37
  # Use the `preload_app!` method when specifying a `workers` number.
29
38
  # This directive tells Puma to first boot the application and load code
30
39
  # before forking the application. This takes advantage of Copy On Write
31
- # process behavior so workers use less memory. If you use this option
32
- # you need to make sure to reconnect any threads in the `on_worker_boot`
33
- # block.
40
+ # process behavior so workers use less memory.
34
41
  #
35
42
  # preload_app!
36
43
 
37
- # The code in the `on_worker_boot` will be called if you are using
38
- # clustered mode by specifying a number of `workers`. After each worker
39
- # process is booted this block will be run, if you are using `preload_app!`
40
- # option you will want to use this block to reconnect to any threads
41
- # or connections that may have been created at application boot, Ruby
42
- # cannot share connections between processes.
43
- #
44
- # on_worker_boot do
45
- # ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
46
- # end
47
-
48
44
  # Allow puma to be restarted by `rails restart` command.
49
45
  plugin :tmp_restart
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Rails.application.routes.draw do
4
- mount LetterOpenerWeb::Engine => '/letter_opener'
5
-
6
- root to: 'home#index'
7
- post '/', to: 'home#create'
4
+ mount LetterOpenerWeb::Engine => '/letter_opener_web'
8
5
  end
data/spec/dummy/config.ru CHANGED
@@ -5,3 +5,4 @@
5
5
  require_relative 'config/environment'
6
6
 
7
7
  run Rails.application
8
+ Rails.application.load_server
@@ -4,7 +4,7 @@
4
4
  <title>The page you were looking for doesn't exist (404)</title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <style>
7
- body {
7
+ .rails-default-error-page {
8
8
  background-color: #EFEFEF;
9
9
  color: #2E2F30;
10
10
  text-align: center;
@@ -12,13 +12,13 @@
12
12
  margin: 0;
13
13
  }
14
14
 
15
- div.dialog {
15
+ .rails-default-error-page div.dialog {
16
16
  width: 95%;
17
17
  max-width: 33em;
18
18
  margin: 4em auto 0;
19
19
  }
20
20
 
21
- div.dialog > div {
21
+ .rails-default-error-page div.dialog > div {
22
22
  border: 1px solid #CCC;
23
23
  border-right-color: #999;
24
24
  border-left-color: #999;
@@ -31,13 +31,13 @@
31
31
  box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
32
  }
33
33
 
34
- h1 {
34
+ .rails-default-error-page h1 {
35
35
  font-size: 100%;
36
36
  color: #730E15;
37
37
  line-height: 1.5em;
38
38
  }
39
39
 
40
- div.dialog > p {
40
+ .rails-default-error-page div.dialog > p {
41
41
  margin: 0 0 1em;
42
42
  padding: 1em;
43
43
  background-color: #F7F7F7;
@@ -54,7 +54,7 @@
54
54
  </style>
55
55
  </head>
56
56
 
57
- <body>
57
+ <body class="rails-default-error-page">
58
58
  <!-- This file lives in public/404.html -->
59
59
  <div class="dialog">
60
60
  <div>
@@ -4,7 +4,7 @@
4
4
  <title>The change you wanted was rejected (422)</title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <style>
7
- body {
7
+ .rails-default-error-page {
8
8
  background-color: #EFEFEF;
9
9
  color: #2E2F30;
10
10
  text-align: center;
@@ -12,13 +12,13 @@
12
12
  margin: 0;
13
13
  }
14
14
 
15
- div.dialog {
15
+ .rails-default-error-page div.dialog {
16
16
  width: 95%;
17
17
  max-width: 33em;
18
18
  margin: 4em auto 0;
19
19
  }
20
20
 
21
- div.dialog > div {
21
+ .rails-default-error-page div.dialog > div {
22
22
  border: 1px solid #CCC;
23
23
  border-right-color: #999;
24
24
  border-left-color: #999;
@@ -31,13 +31,13 @@
31
31
  box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
32
  }
33
33
 
34
- h1 {
34
+ .rails-default-error-page h1 {
35
35
  font-size: 100%;
36
36
  color: #730E15;
37
37
  line-height: 1.5em;
38
38
  }
39
39
 
40
- div.dialog > p {
40
+ .rails-default-error-page div.dialog > p {
41
41
  margin: 0 0 1em;
42
42
  padding: 1em;
43
43
  background-color: #F7F7F7;
@@ -54,7 +54,7 @@
54
54
  </style>
55
55
  </head>
56
56
 
57
- <body>
57
+ <body class="rails-default-error-page">
58
58
  <!-- This file lives in public/422.html -->
59
59
  <div class="dialog">
60
60
  <div>
@@ -4,7 +4,7 @@
4
4
  <title>We're sorry, but something went wrong (500)</title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <style>
7
- body {
7
+ .rails-default-error-page {
8
8
  background-color: #EFEFEF;
9
9
  color: #2E2F30;
10
10
  text-align: center;
@@ -12,13 +12,13 @@
12
12
  margin: 0;
13
13
  }
14
14
 
15
- div.dialog {
15
+ .rails-default-error-page div.dialog {
16
16
  width: 95%;
17
17
  max-width: 33em;
18
18
  margin: 4em auto 0;
19
19
  }
20
20
 
21
- div.dialog > div {
21
+ .rails-default-error-page div.dialog > div {
22
22
  border: 1px solid #CCC;
23
23
  border-right-color: #999;
24
24
  border-left-color: #999;
@@ -31,13 +31,13 @@
31
31
  box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
32
  }
33
33
 
34
- h1 {
34
+ .rails-default-error-page h1 {
35
35
  font-size: 100%;
36
36
  color: #730E15;
37
37
  line-height: 1.5em;
38
38
  }
39
39
 
40
- div.dialog > p {
40
+ .rails-default-error-page div.dialog > p {
41
41
  margin: 0 0 1em;
42
42
  padding: 1em;
43
43
  background-color: #F7F7F7;
@@ -54,7 +54,7 @@
54
54
  </style>
55
55
  </head>
56
56
 
57
- <body>
57
+ <body class="rails-default-error-page">
58
58
  <!-- This file lives in public/500.html -->
59
59
  <div class="dialog">
60
60
  <div>
File without changes
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require 'rails_helper'
4
4
 
5
- describe LetterOpenerWeb do
5
+ RSpec.describe LetterOpenerWeb do
6
6
  subject { described_class }
7
7
  after(:each) { described_class.reset! }
8
8
 
@@ -1,19 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- describe LetterOpenerWeb::Letter do
4
- let(:location) { File.expand_path('../../tmp', __dir__) }
3
+ RSpec.describe LetterOpenerWeb::Letter do
4
+ let(:location) { Pathname.new(__dir__).join('..', '..', 'tmp').cleanpath }
5
5
 
6
6
  def rich_text(mail_id)
7
- <<-MAIL
8
- Rich text for #{mail_id}
9
- <!DOCTYPE html>
10
- <a href='a-link.html'>
11
- <img src='an-image.jpg'>
12
- Link text
13
- </a>
14
- <a href='fooo.html'>Bar</a>
15
- <a href="example.html" class="blank"></a>
16
- <address><a href="inside-address.html">inside address</a></address>
7
+ <<~MAIL
8
+ Rich text for #{mail_id}
9
+ <!DOCTYPE html>
10
+ <body>
11
+ <div id="container">
12
+ <div id="message_headers">
13
+ <dl>
14
+ <dt>From:</dt>
15
+ <dd>noreply@example.com</dd>
16
+ </dl>
17
+ </div>
18
+
19
+ <a href='a-link.html'>
20
+ <img src='an-image.jpg'>
21
+ Link text
22
+ </a>
23
+ <a href='fooo.html'>Bar</a>
24
+ <a href="example.html" class="blank"></a>
25
+ <address><a href="inside-address.html">inside address</a></address>
26
+ </div>
27
+ </body>
17
28
  MAIL
18
29
  end
19
30
 
@@ -36,6 +47,28 @@ Rich text for #{mail_id}
36
47
  FileUtils.rm_rf(location)
37
48
  end
38
49
 
50
+ describe 'rich text headers' do
51
+ let(:id) { '1111_1111' }
52
+ subject { described_class.new(id: id).headers }
53
+
54
+ before do
55
+ FileUtils.rm_rf("#{location}/#{id}/plain.html")
56
+ end
57
+
58
+ it { is_expected.to match(%r{<dl>\s*<dt>From:</dt>\s*<dd>noreply@example\.com</dd>}m) }
59
+ end
60
+
61
+ describe 'plain text headers' do
62
+ let(:id) { '1111_1111' }
63
+ subject { described_class.new(id: id).headers }
64
+
65
+ before do
66
+ FileUtils.rm_rf("#{location}/#{id}/rich.html")
67
+ end
68
+
69
+ it { is_expected.to eq('UNABLE TO PARSE HEADERS') }
70
+ end
71
+
39
72
  describe 'rich text version' do
40
73
  let(:id) { '1111_1111' }
41
74
  subject { described_class.new(id: id).rich_text }
@@ -45,9 +78,10 @@ Rich text for #{mail_id}
45
78
  it 'changes links to show up on a new window' do
46
79
  link_html = [
47
80
  "<a href='a-link.html' target='_blank'>",
48
- "<img src='an-image.jpg'/>",
49
- "Link text\n</a>"
50
- ].join("\n ")
81
+ " <img src='an-image.jpg'/>",
82
+ ' Link text',
83
+ '</a>'
84
+ ].join("\n ")
51
85
 
52
86
  expect(subject).to include(link_html)
53
87
  end
@@ -128,13 +162,24 @@ Rich text for #{mail_id}
128
162
 
129
163
  describe '#delete' do
130
164
  let(:id) { '1111_1111' }
165
+
131
166
  subject { described_class.new(id: id).delete }
132
167
 
133
- it'removes the letter with given id' do
168
+ it 'removes the letter with given id' do
134
169
  subject
135
170
  directories = Dir["#{location}/*"]
136
171
  expect(directories.count).to eql(1)
137
172
  expect(directories.first).not_to match(id)
138
173
  end
174
+
175
+ context 'when the id is outside of the letters base path' do
176
+ let(:id) { '../3333_3333' }
177
+
178
+ it 'does not remove the letter' do
179
+ expect(FileUtils).not_to receive(:rm_rf).with(location.join(id).cleanpath.to_s)
180
+
181
+ expect(subject).to be_nil
182
+ end
183
+ end
139
184
  end
140
185
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ if ENV.fetch('CI', '') == 'true'
7
+ require 'codecov'
8
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
9
+ end
10
+
3
11
  require 'shoulda-matchers'
4
12
 
5
13
  RSpec.configure do |config|