diff --git a/.github/workflows/brakeman-analysis.yml b/.github/workflows/brakeman-analysis.yml new file mode 100644 index 0000000..ae13909 --- /dev/null +++ b/.github/workflows/brakeman-analysis.yml @@ -0,0 +1,46 @@ +# This workflow integrates Brakeman with GitHub's Code Scanning feature +# Brakeman is a static analysis security vulnerability scanner for Ruby on Rails applications + +name: Brakeman Scan + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '21 4 * * 4' + +jobs: + brakeman-scan: + name: Brakeman Scan + runs-on: ubuntu-latest + steps: + # Checkout the repository to the GitHub Actions runner + - name: Checkout + uses: actions/checkout@v2 + + # Customize the ruby version depending on your needs + - name: Setup Ruby + uses: actions/setup-ruby@v1 + with: + ruby-version: '2.7' + + - name: Setup Brakeman + env: + BRAKEMAN_VERSION: '4.10' # SARIF support is provided in Brakeman version 4.10+ + run: | + gem install brakeman --version $BRAKEMAN_VERSION + + # Execute Brakeman CLI and generate a SARIF output with the security issues identified during the analysis + - name: Scan + continue-on-error: true + run: | + brakeman -f sarif -o output.sarif.json . + + # Upload the SARIF file generated in the previous step + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: output.sarif.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..6069391 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,34 @@ +name: Run tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master, next ] + +jobs: + test: + + runs-on: ubuntu-latest + + strategy: + matrix: + ruby-version: [2.7, 3.0] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Ruby ${{ matrix.ruby-version }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + + - name: Install dependencies + run: bundle install + + - name: Run tests + run: bundle exec rake + + - name: Build gem + run: bundle exec rake build diff --git a/.github/workflows/release-gem.yml b/.github/workflows/release-gem.yml new file mode 100644 index 0000000..a10867a --- /dev/null +++ b/.github/workflows/release-gem.yml @@ -0,0 +1,32 @@ +name: Release gem + +on: + push: + tags: + - 'v*' + +jobs: + build: + name: Build + Publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Ruby 3.0 + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.0 + + - run: bundle install + + - name: Publish to RubyGems + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + bundle exec rake build + gem push pkg/*.gem + env: + GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" diff --git a/.rubocop.yml b/.rubocop.yml index df53d75..bf3118c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,23 +1,14 @@ --- +inherit_from: .rubocop_todo.yml + AllCops: - TargetRubyVersion: 2.5 + NewCops: enable + TargetRubyVersion: 2.7 Exclude: - "spec/dummy/bin/**/*" - "tmp/**/*" - "vendor/**/*" -Layout/HeredocIndentation: - Enabled: false - -Style/Documentation: - Enabled: false - -Style/ClassAndModuleChildren: - EnforcedStyle: nested - -Style/SingleLineBlockParams: - Enabled: false - Metrics/BlockLength: Exclude: - spec/**/*_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..856ab10 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,19 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2021-10-02 14:42:36 UTC using RuboCop version 1.22.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 5 +# Configuration parameters: AllowedConstants. +Style/Documentation: + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'app/controllers/letter_opener_web/letters_controller.rb' + - 'app/models/letter_opener_web/letter.rb' + - 'lib/letter_opener_web.rb' + - 'lib/letter_opener_web/delivery_method.rb' + - 'lib/letter_opener_web/engine.rb' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4bead48..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: ruby - -rvm: - - "2.7" - - "2.6" - - "2.5" - -cache: bundler - -before_install: - - gem install bundler -v '< 2' - -notifications: - email: false - -deploy: - provider: rubygems - gem: letter_opener_web - api_key: - secure: Rv1IgeIhnfIV9NVr3iPwb2UCXe0UEtuZHfgkGruu3yvfoSGmQQ0xnc45ZhOH8ANl6tjzYNuilSDYbhz3fWOmniHsnzLGbFbccz/CWHmldUpPgVnKQ0d0Bm2EeNuVpmHwdnG7KEqMC0x1gxFYks62BGpx4C/2Kh5nWbvvPxN8X0U= - on: - tags: true - repo: fgrehm/letter_opener_web diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b7ad22..440b01e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [v2.0.0](https://github.com/fgrehm/letter_opener_web/compare/v1.4.1...v2.0.0) + + - Require Rails >= 5.2, run tests against Rails 6.1 [#113](https://github.com/fgrehm/letter_opener_web/pull/113) + - Inline CSS and Javascript, to avoid dependency on asset pipeline [#113](https://github.com/fgrehm/letter_opener_web/pull/113) + - Upgrade to Bootstrap 5.1.1 [#113](https://github.com/fgrehm/letter_opener_web/pull/113) + - Add rexml gem into dependency for Ruby 3.0 [#106](https://github.com/fgrehm/letter_opener_web/pull/106) + - Add routes for Rails API mode [#69](https://github.com/fgrehm/letter_opener_web/pull/69) + - Prevent name conflict with `Letter` class [#108](https://github.com/fgrehm/letter_opener_web/pull/108) + - Add Rails' built-in CSRF protection [#111](https://github.com/fgrehm/letter_opener_web/pull/111) + - Add Rails' CSP nonce to the script tag [#112](https://github.com/fgrehm/letter_opener_web/pull/112) + - Update dev dependencies [#113](https://github.com/fgrehm/letter_opener_web/pull/113) + - Switched to using GitHub actions as CI for the project [#113](https://github.com/fgrehm/letter_opener_web/pull/113) + ## [1.4.1](https://github.com/fgrehm/letter_opener_web/compare/v1.4.0...v1.4.1) (Oct 5, 2021) - Ensure letter is within letters base path [#110](https://github.com/fgrehm/letter_opener_web/pull/110) diff --git a/LICENSE.txt b/LICENSE.txt index 6824867..7eef989 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013-2018 Fabio Rehm +Copyright (c) 2013-2021 Fabio Rehm MIT License diff --git a/README.md b/README.md index 9dc3b13..d29bd7a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # letter_opener_web -[![Build Status](https://travis-ci.org/fgrehm/letter_opener_web.svg?branch=master)](https://travis-ci.org/fgrehm/letter_opener_web) +![Build Status](https://github.com/fgrehm/letter_opener_web/actions/workflows/main.yml/badge.svg) [![Gem Version](https://badge.fury.io/rb/letter_opener_web.svg)](http://badge.fury.io/rb/letter_opener_web) [![Code Climate](https://codeclimate.com/github/fgrehm/letter_opener_web.svg)](https://codeclimate.com/github/fgrehm/letter_opener_web) @@ -15,7 +15,7 @@ First add the gem to your development environment and run the `bundle` command t ```ruby group :development do - gem 'letter_opener_web', '~> 1.0' + gem 'letter_opener_web', '~> 2.0' end ``` @@ -33,8 +33,9 @@ And make sure you have [`:letter_opener` delivery method](https://github.com/rya configured for your app. Then visit `http://localhost:3000/letter_opener` after sending an email and have fun. -If you are running the app from a [Vagrant](http://vagrantup.com) machine, you -might want to skip `letter_opener`'s `launchy` calls and avoid messages like these: +If you are running the app from a [Vagrant](http://vagrantup.com) machine or Docker +container, you might want to skip `letter_opener`'s `launchy` calls and avoid messages +like these: ```terminal 12:33:42 web.1 | Failure in opening /vagrant/tmp/letter_opener/1358825621_ba83a22/rich.html @@ -43,19 +44,16 @@ environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file https://github.com/copiousfreetime/launchy/issues/new ``` -In that case (or if you just want to browse mails using the web interface), you -can set `:letter_opener_web` as your delivery method on your -`config/environments/development.rb`: +In that case (or if you really just want to browse mails using the web interface and +don't care about opening emails automatically), you can set `:letter_opener_web` as +your delivery method on your `config/environments/development.rb`: ```ruby config.action_mailer.delivery_method = :letter_opener_web - -# If not everyone on the team is using vagrant -config.action_mailer.delivery_method = ENV['USER'] == 'vagrant' ? :letter_opener_web : :letter_opener ``` -If you're using `:letter_opener_web` as your delivery method, you can change the location of the letters by adding the -following to an initializer (or in development.rb): +If you're using `:letter_opener_web` as your delivery method, you can change the location of +the letters by adding the following to an initializer (or in development.rb): ```ruby LetterOpenerWeb.configure do |config| @@ -63,11 +61,14 @@ LetterOpenerWeb.configure do |config| end ``` -## Usage on Heroku +## Usage on pre-production environments + +Some people use this gem on staging / pre-production environments to avoid having real emails +being sent out. To set that up you'll need to: -Some people use this gem on staging environments on Heroku and to set that up -is just a matter of moving the gem out of the `development` group and enabling -the route for all environments on your `routes.rb`. +1. Move the gem out of the `development` group in your `Gemfile` +2. Set `config.action_mailer.delivery_method` on the appropriate `config/environments/.rb` +3. Enable the route for the environments on your `routes.rb`. In other words, your `Gemfile` will have: @@ -79,20 +80,28 @@ And your `routes.rb`: ```ruby Your::Application.routes.draw do - mount LetterOpenerWeb::Engine, at: "/letter_opener" + # If you have a dedicated config/environments/staging.rb + mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.staging? + + # If you use RAILS_ENV=production in staging environments, you'll need another + # way to disable it in "real production" + mount LetterOpenerWeb::Engine, at: "/letter_opener" unless ENV["PRODUCTION_FOR_REAL"] end ``` You might also want to have a look at the sources for the [demo](http://letter-opener-web.herokuapp.com) available at https://github.com/fgrehm/letter_opener_web_demo. -**NOTICE: Using this gem on Heroku will only work if your app has just one Dyno and does not send emails from background jobs. For updates on this matter please subscribe to [GH-35](https://github.com/fgrehm/letter_opener_web/issues/35)** +**NOTICE: Using this gem on Heroku will only work if your app has just one Dyno +and does not send emails from background jobs. For updates on this matter please +subscribe to [GH-35](https://github.com/fgrehm/letter_opener_web/issues/35)** ## Acknowledgements Special thanks to [@alexrothenberg](https://github.com/alexrothenberg) for some -ideas on [this pull request](https://github.com/ryanb/letter_opener/pull/12). - +ideas on [this pull request](https://github.com/ryanb/letter_opener/pull/12) and +[@pseudomuto](https://github.com/pseudomuto) for keeping the project alive for a +few years. ## Contributing diff --git a/app/controllers/letter_opener_web/application_controller.rb b/app/controllers/letter_opener_web/application_controller.rb index 96a79b5..e34bdd3 100644 --- a/app/controllers/letter_opener_web/application_controller.rb +++ b/app/controllers/letter_opener_web/application_controller.rb @@ -2,5 +2,6 @@ module LetterOpenerWeb class ApplicationController < ActionController::Base + protect_from_forgery with: :exception, unless: -> { Rails.configuration.try(:api_only) } end end diff --git a/app/controllers/letter_opener_web/letters_controller.rb b/app/controllers/letter_opener_web/letters_controller.rb index b23920c..7eaebd6 100644 --- a/app/controllers/letter_opener_web/letters_controller.rb +++ b/app/controllers/letter_opener_web/letters_controller.rb @@ -10,7 +10,7 @@ class LettersController < ApplicationController before_action :load_letter, only: %i[show attachment destroy] def index - @letters = Letter.search + @letters = LetterOpenerWeb::Letter.search end def show @@ -31,13 +31,16 @@ def attachment end def clear - Letter.destroy_all + LetterOpenerWeb::Letter.destroy_all redirect_to routes.letters_path end def destroy @letter.delete - redirect_to routes.letters_path + respond_to do |format| + format.html { redirect_to routes.letters_path } + format.js { render js: "window.location='#{routes.letters_path}'" } + end end private @@ -47,7 +50,7 @@ def check_style end def load_letter - @letter = Letter.find(params[:id]) + @letter = LetterOpenerWeb::Letter.find(params[:id]) head :not_found unless @letter.valid? end diff --git a/app/models/letter_opener_web/letter.rb b/app/models/letter_opener_web/letter.rb index 3072441..f662672 100644 --- a/app/models/letter_opener_web/letter.rb +++ b/app/models/letter_opener_web/letter.rb @@ -33,6 +33,18 @@ def initialize(params) @sent_at = params[:sent_at] end + def headers + html = read_file(:rich) if style_exists?('rich') + html ||= read_file(:plain) + + # NOTE: This is ugly, we should look into using nokogiri and making that a + # dependency of this gem + match_data = html.match(%r{\s*]+id="container">\s*]+id="message_headers">\s*(
.+
)}m) + return remove_attachments_link(match_data[1]).html_safe if match_data && match_data[1].present? + + 'UNABLE TO PARSE HEADERS' + end + def plain_text @plain_text ||= adjust_link_targets(read_file(:plain)) end @@ -67,6 +79,15 @@ def valid? private + def remove_attachments_link(headers) + xml = REXML::Document.new(headers) + if xml.root.elements.size == 10 + xml.delete_element('//dd[last()]') + xml.delete_element('//dt[last()]') + end + xml.to_s + end + def base_dir LetterOpenerWeb.config.letters_location.join(id).cleanpath end @@ -88,7 +109,7 @@ def base_dir_within_letters_location? end def adjust_link_targets(contents) - # We cannot feed the whole file to an XML parser as some mails are + # We cannot feed the whole file to a XML parser as some mails are # "complete" (as in they have the whole structure) and letter_opener # prepends some information about the mail being sent, making REXML # complain about it diff --git a/app/views/layouts/letter_opener_web/_javascripts.html.erb b/app/views/layouts/letter_opener_web/_javascripts.html.erb index bcf9eeb..15e25ca 100644 --- a/app/views/layouts/letter_opener_web/_javascripts.html.erb +++ b/app/views/layouts/letter_opener_web/_javascripts.html.erb @@ -1,564 +1,31 @@ - diff --git a/app/views/layouts/letter_opener_web/_styles.html.erb b/app/views/layouts/letter_opener_web/_styles.html.erb index b055664..0ae8458 100644 --- a/app/views/layouts/letter_opener_web/_styles.html.erb +++ b/app/views/layouts/letter_opener_web/_styles.html.erb @@ -1,63 +1,3 @@ - \ No newline at end of file +<%= render "layouts/letter_opener_web/styles/icon" %> +<%= render "layouts/letter_opener_web/styles/bootstrap" %> +<%= render "layouts/letter_opener_web/styles/letters" %> diff --git a/app/views/layouts/letter_opener_web/js/_favcount.html.erb b/app/views/layouts/letter_opener_web/js/_favcount.html.erb new file mode 100644 index 0000000..e406338 --- /dev/null +++ b/app/views/layouts/letter_opener_web/js/_favcount.html.erb @@ -0,0 +1,104 @@ + diff --git a/app/views/layouts/letter_opener_web/js/_jquery.html.erb b/app/views/layouts/letter_opener_web/js/_jquery.html.erb new file mode 100644 index 0000000..fc1931e --- /dev/null +++ b/app/views/layouts/letter_opener_web/js/_jquery.html.erb @@ -0,0 +1,7 @@ + diff --git a/app/views/layouts/letter_opener_web/letters.html.erb b/app/views/layouts/letter_opener_web/letters.html.erb index d49862b..7bde6ef 100644 --- a/app/views/layouts/letter_opener_web/letters.html.erb +++ b/app/views/layouts/letter_opener_web/letters.html.erb @@ -3,12 +3,13 @@ LetterOpenerWeb <%= render "layouts/letter_opener_web/styles" %> - <%= render "layouts/letter_opener_web/javascripts" %> - + <% if respond_to? :csp_meta_tag %> + <%= csp_meta_tag %> + <% end %> <%= csrf_meta_tags %> <%= yield %> + <%= render "layouts/letter_opener_web/javascripts" %> diff --git a/app/views/layouts/letter_opener_web/styles/_bootstrap.html.erb b/app/views/layouts/letter_opener_web/styles/_bootstrap.html.erb new file mode 100644 index 0000000..ac881a2 --- /dev/null +++ b/app/views/layouts/letter_opener_web/styles/_bootstrap.html.erb @@ -0,0 +1,9 @@ + diff --git a/app/views/layouts/letter_opener_web/styles/_icon.html.erb b/app/views/layouts/letter_opener_web/styles/_icon.html.erb new file mode 100644 index 0000000..c2141c1 --- /dev/null +++ b/app/views/layouts/letter_opener_web/styles/_icon.html.erb @@ -0,0 +1,2 @@ + diff --git a/app/views/layouts/letter_opener_web/styles/_letters.html.erb b/app/views/layouts/letter_opener_web/styles/_letters.html.erb new file mode 100644 index 0000000..05065f0 --- /dev/null +++ b/app/views/layouts/letter_opener_web/styles/_letters.html.erb @@ -0,0 +1,70 @@ + diff --git a/app/views/letter_opener_web/letters/_item.html.erb b/app/views/letter_opener_web/letters/_item.html.erb new file mode 100644 index 0000000..9935843 --- /dev/null +++ b/app/views/letter_opener_web/letters/_item.html.erb @@ -0,0 +1,10 @@ +<% classes = %w(list-group-item list-group-action d-flex justify-content-between align-items-start p-2 message-headers) %> +<% classes << 'active' if active %> +<%= content_tag :li, class: classes do %> + <%= link_to(letter_path(letter, style: letter.default_style), target: 'mail') do %> + <%= letter.headers %> + <% end %> + +<% end %> diff --git a/app/views/letter_opener_web/letters/index.html.erb b/app/views/letter_opener_web/letters/index.html.erb index 535e51f..8b05c97 100644 --- a/app/views/letter_opener_web/letters/index.html.erb +++ b/app/views/letter_opener_web/letters/index.html.erb @@ -1,58 +1,23 @@

Letters - - <%= link_to letters_path, class: 'btn refresh' do %> - + + <%= link_to letters_path, class: 'btn btn-primary refresh' do %> Refresh <% end %> - <%= link_to clear_letters_path, method: 'delete', data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' do %> - + <%= link_to clear_letters_path, method: 'post', data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' do %> Clear <% end %>

- - - - - - - - - - <% if first_letter = @letters.shift %> - - - - - - <% end %> - <% @letters.each do |letter| %> - - - - - - <% end %> - -
IDSent atAction
- <%= link_to(first_letter.id, letter_path(first_letter, style: first_letter.default_style), target: 'mail') %> - <%= first_letter.sent_at %> - <%= link_to delete_letter_path(first_letter), method: 'delete', data: { confirm: 'Are you sure you want to delete this email?' } do %> - - - - <% end %> -
- <%= link_to(letter.id, letter_path(letter, style: letter.default_style), target: 'mail') %> - <%= letter.sent_at %> - <%= link_to delete_letter_path(letter), :method => 'delete', data: { confirm: 'Are you sure you want to delete this email?' } do %> - - - - <% end %> -
+
+
    + <% if first_letter = @letters.shift %> + <%= render partial: "item", locals: { letter: first_letter, active: true } %> + <% end %> + <%= render partial: "item", collection: @letters, as: :letter, locals: { active: false } %> +
+
diff --git a/config/routes.rb b/config/routes.rb index 8b31ecd..275dbd8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true LetterOpenerWeb::Engine.routes.draw do - delete 'clear' => 'letters#clear', as: :clear_letters - delete ':id' => 'letters#destroy', as: :delete_letter - get '/' => 'letters#index', as: :letters - get ':id(/:style)' => 'letters#show', as: :letter - get ':id/attachments/:file' => 'letters#attachment' + get '/' => 'letters#index', as: :letters + post 'clear' => 'letters#clear', as: :clear_letters + get ':id(/:style)' => 'letters#show', as: :letter + post ':id/delete' => 'letters#destroy', as: :delete_letter + get ':id/attachments/:file' => 'letters#attachment' end diff --git a/letter_opener_web.gemspec b/letter_opener_web.gemspec index b9d442f..339f3e7 100644 --- a/letter_opener_web.gemspec +++ b/letter_opener_web.gemspec @@ -13,19 +13,22 @@ Gem::Specification.new do |gem| gem.summary = gem.description gem.homepage = 'https://github.com/fgrehm/letter_opener_web' gem.license = 'MIT' - gem.required_ruby_version = '>= 2.5' + gem.required_ruby_version = '>= 2.7' gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) gem.executables = gem.files.grep(%r{^exe/}).map { |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ['lib'] - gem.add_dependency 'actionmailer', '>= 3.2' - gem.add_dependency 'letter_opener', '~> 1.0' - gem.add_dependency 'railties', '>= 3.2' + gem.add_dependency 'actionmailer', '>= 5.2' + gem.add_dependency 'letter_opener', '~> 1.7' + gem.add_dependency 'railties', '>= 5.2' + gem.add_dependency 'rexml' - gem.add_development_dependency 'rails', '~> 5.0' - gem.add_development_dependency 'rspec-rails', '~> 3.0' - gem.add_development_dependency 'rubocop', '~> 0.79' - gem.add_development_dependency 'shoulda-matchers', '~> 2.5' + gem.add_development_dependency 'rails', '~> 6.1' + gem.add_development_dependency 'rspec-rails', '~> 5.0' + gem.add_development_dependency 'rubocop', '~> 1.22' + gem.add_development_dependency 'rubocop-rails', '~> 2.12' + gem.add_development_dependency 'rubocop-rspec', '~> 2.5' + gem.add_development_dependency 'shoulda-matchers', '~> 5.0' end diff --git a/lib/letter_opener_web.rb b/lib/letter_opener_web.rb index b9efa30..38e6e59 100644 --- a/lib/letter_opener_web.rb +++ b/lib/letter_opener_web.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'letter_opener_web/version' require 'letter_opener_web/engine' require 'rexml/document' diff --git a/lib/letter_opener_web/version.rb b/lib/letter_opener_web/version.rb index ae25a45..baafafb 100644 --- a/lib/letter_opener_web/version.rb +++ b/lib/letter_opener_web/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module LetterOpenerWeb - VERSION = '1.4.1' + VERSION = '2.0.0' end diff --git a/spec/controllers/letter_opener_web/letters_controller_spec.rb b/spec/controllers/letter_opener_web/letters_controller_spec.rb index 553f21e..ce0c772 100644 --- a/spec/controllers/letter_opener_web/letters_controller_spec.rb +++ b/spec/controllers/letter_opener_web/letters_controller_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe LetterOpenerWeb::LettersController do +RSpec.describe LetterOpenerWeb::LettersController do routes { LetterOpenerWeb::Engine.routes } after(:each) { LetterOpenerWeb.reset! } @@ -19,7 +19,7 @@ it 'returns an HTML 200 response' do expect(response.status).to eq(200) - expect(response.content_type).to eq('text/html') + expect(response.content_type).to eq('text/html; charset=utf-8') end end @@ -38,7 +38,7 @@ it 'renders an HTML 200 response' do expect(response.status).to eq(200) - expect(response.content_type).to eq('text/html') + expect(response.content_type).to eq('text/html; charset=utf-8') end end @@ -89,7 +89,7 @@ it 'sends the file as an inline attachment' do allow(controller).to receive(:send_file) { controller.head :ok } - get :attachment, params: { id: id, file: file_name.gsub(/\.\w+/, ''), format: File.extname(file_name)[1..-1] } + get :attachment, params: { id: id, file: file_name.gsub(/\.\w+/, ''), format: File.extname(file_name)[1..] } expect(response.status).to eq(200) expect(controller).to have_received(:send_file) diff --git a/spec/dummy/app/assets/config/manifest.js b/spec/dummy/app/assets/config/manifest.js index 4f5fffa..59220aa 100644 --- a/spec/dummy/app/assets/config/manifest.js +++ b/spec/dummy/app/assets/config/manifest.js @@ -1,5 +1,3 @@ - //= link_tree ../images -//= link_directory ../javascripts .js //= link_directory ../stylesheets .css //= link letter_opener_web_manifest.js diff --git a/spec/dummy/app/assets/stylesheets/application.css b/spec/dummy/app/assets/stylesheets/application.css index 618306e..0ebd7fe 100644 --- a/spec/dummy/app/assets/stylesheets/application.css +++ b/spec/dummy/app/assets/stylesheets/application.css @@ -13,19 +13,3 @@ *= require_tree . *= require_self */ - -body { - padding-top: 20px; -} - -.alert { - margin-top: 20px; -} - -h1, .footer { - text-align: center; -} - -.icon-white { - background-image: url(<%=asset_path "letter_opener_web/glyphicons-halflings-white.png"%>); -} diff --git a/spec/dummy/app/channels/application_cable/channel.rb b/spec/dummy/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..9aec230 --- /dev/null +++ b/spec/dummy/app/channels/application_cable/channel.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/spec/dummy/app/channels/application_cable/connection.rb b/spec/dummy/app/channels/application_cable/connection.rb new file mode 100644 index 0000000..8d6c2a1 --- /dev/null +++ b/spec/dummy/app/channels/application_cable/connection.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/spec/dummy/app/controllers/application_controller.rb b/spec/dummy/app/controllers/application_controller.rb index 280cc28..7944f9f 100644 --- a/spec/dummy/app/controllers/application_controller.rb +++ b/spec/dummy/app/controllers/application_controller.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true class ApplicationController < ActionController::Base - protect_from_forgery with: :exception end diff --git a/spec/dummy/app/controllers/home_controller.rb b/spec/dummy/app/controllers/home_controller.rb deleted file mode 100644 index 68c072c..0000000 --- a/spec/dummy/app/controllers/home_controller.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -class HomeController < ApplicationController - def index; end - - def create - ContactMailer.new_message(params[:email], params[:message], params[:attachment]).deliver - redirect_to '/', notice: "Email sent successfully, please check letter_opener_web's inbox." - end -end diff --git a/spec/dummy/app/assets/javascripts/application.js b/spec/dummy/app/javascript/packs/application.js similarity index 93% rename from spec/dummy/app/assets/javascripts/application.js rename to spec/dummy/app/javascript/packs/application.js index e54c646..67ce467 100644 --- a/spec/dummy/app/assets/javascripts/application.js +++ b/spec/dummy/app/javascript/packs/application.js @@ -10,4 +10,6 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // +//= require rails-ujs +//= require activestorage //= require_tree . diff --git a/spec/dummy/app/jobs/application_job.rb b/spec/dummy/app/jobs/application_job.rb new file mode 100644 index 0000000..bef3959 --- /dev/null +++ b/spec/dummy/app/jobs/application_job.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError +end diff --git a/spec/dummy/app/mailers/contact_mailer.rb b/spec/dummy/app/mailers/contact_mailer.rb deleted file mode 100644 index 3aaa884..0000000 --- a/spec/dummy/app/mailers/contact_mailer.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class ContactMailer < ApplicationMailer - default to: 'admin@letter_opener_web.org', from: 'no-reply@letter_opener_web.org' - - def new_message(from, message, attachment) - @from = from - @message = message - - attachments[attachment.original_filename] = attachment.tempfile.read if attachment.present? - - mail(subject: 'Testing letter_opener_web') - end -end diff --git a/spec/dummy/app/models/application_record.rb b/spec/dummy/app/models/application_record.rb new file mode 100644 index 0000000..71fbba5 --- /dev/null +++ b/spec/dummy/app/models/application_record.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/spec/dummy/app/models/concerns/.keep b/spec/dummy/app/models/concerns/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/dummy/app/views/contact_mailer/new_message.html.erb b/spec/dummy/app/views/contact_mailer/new_message.html.erb deleted file mode 100644 index 9b2bce5..0000000 --- a/spec/dummy/app/views/contact_mailer/new_message.html.erb +++ /dev/null @@ -1,21 +0,0 @@ - - -

New message!

- -

- - <%= @from %> -

- -

- -

- -
- <%= simple_format @message %> -
diff --git a/spec/dummy/app/views/contact_mailer/new_message.text.erb b/spec/dummy/app/views/contact_mailer/new_message.text.erb deleted file mode 100644 index d33a779..0000000 --- a/spec/dummy/app/views/contact_mailer/new_message.text.erb +++ /dev/null @@ -1,3 +0,0 @@ -# New message from: <%= @from %> - -<%= @message %> diff --git a/spec/dummy/app/views/home/index.html.erb b/spec/dummy/app/views/home/index.html.erb deleted file mode 100644 index 3239c30..0000000 --- a/spec/dummy/app/views/home/index.html.erb +++ /dev/null @@ -1,50 +0,0 @@ -

letter_opener_web Demo

- -
-
- -
- <%= form_tag root_path, multipart: true do %> - Contact form -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
- <% end %> - - -
- - <% if flash[:notice].present? %> -
- <%= flash[:notice] %> -
- <% end %> -
-
diff --git a/spec/dummy/app/views/layouts/application.html.erb b/spec/dummy/app/views/layouts/application.html.erb index 7ccee89..b74860f 100644 --- a/spec/dummy/app/views/layouts/application.html.erb +++ b/spec/dummy/app/views/layouts/application.html.erb @@ -2,17 +2,14 @@ Dummy + <%= csrf_meta_tags %> + <%= csp_meta_tag %> - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> - <%= stylesheet_link_tag '//maxcdn.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css' %> + <%= stylesheet_link_tag 'application', media: 'all' %> -
- <%= yield %> -
- - <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + <%= yield %> diff --git a/spec/dummy/bin/bundle b/spec/dummy/bin/bundle deleted file mode 100755 index 58115ec..0000000 --- a/spec/dummy/bin/bundle +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -load Gem.bin_path('bundler', 'bundle') diff --git a/spec/dummy/bin/rails b/spec/dummy/bin/rails index a31728a..6fb4e40 100755 --- a/spec/dummy/bin/rails +++ b/spec/dummy/bin/rails @@ -1,6 +1,4 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +require_relative "../config/boot" +require "rails/commands" diff --git a/spec/dummy/bin/rake b/spec/dummy/bin/rake index c199955..4fbf10b 100755 --- a/spec/dummy/bin/rake +++ b/spec/dummy/bin/rake @@ -1,6 +1,4 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - -require_relative '../config/boot' -require 'rake' +require_relative "../config/boot" +require "rake" Rake.application.run diff --git a/spec/dummy/bin/setup b/spec/dummy/bin/setup index 629e358..5792302 100755 --- a/spec/dummy/bin/setup +++ b/spec/dummy/bin/setup @@ -1,19 +1,16 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - -require 'pathname' -require 'fileutils' -include FileUtils +require "fileutils" # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do - # This script is a starting point to setup your application. +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. puts '== Installing dependencies ==' @@ -22,11 +19,11 @@ chdir APP_ROOT do # puts "\n== Copying sample files ==" # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' + # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' # end puts "\n== Preparing database ==" - system! 'bin/rails db:setup' + system! 'bin/rails db:prepare' puts "\n== Removing old logs and tempfiles ==" system! 'bin/rails log:clear tmp:clear' diff --git a/spec/dummy/bin/update b/spec/dummy/bin/update deleted file mode 100755 index fdac831..0000000 --- a/spec/dummy/bin/update +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require 'pathname' -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a way to update your development environment automatically. - # Add necessary update steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - puts "\n== Updating database ==" - system! 'bin/rails db:migrate' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/spec/dummy/config.ru b/spec/dummy/config.ru index 842bccc..6dc8321 100644 --- a/spec/dummy/config.ru +++ b/spec/dummy/config.ru @@ -5,3 +5,4 @@ require_relative 'config/environment' run Rails.application +Rails.application.load_server diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index 2ffb3d8..89afc85 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -2,22 +2,36 @@ require_relative 'boot' -require 'active_model/railtie' +require 'rails' +# Pick the frameworks you want: +# require "active_model/railtie" +# require "active_job/railtie" +# require "active_record/railtie" +# require "active_storage/engine" require 'action_controller/railtie' require 'action_mailer/railtie' +# require "action_mailbox/engine" +# require "action_text/engine" require 'action_view/railtie' -require 'sprockets/railtie' -require 'rails/test_unit/railtie' +# require "action_cable/engine" +# require "sprockets/railtie" +# require "rails/test_unit/railtie" +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) require 'letter_opener_web' module Dummy class Application < Rails::Application - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. + config.load_defaults Rails::VERSION::STRING.to_f - config.action_mailer.delivery_method = :letter_opener_web + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") end end diff --git a/spec/dummy/config/environments/development.rb b/spec/dummy/config/environments/development.rb index b5751dc..80fdae8 100644 --- a/spec/dummy/config/environments/development.rb +++ b/spec/dummy/config/environments/development.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true +require 'active_support/core_ext/integer/time' + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false @@ -14,15 +16,45 @@ # Show full error reports. config.consider_all_requests_local = true - config.action_controller.perform_caching = false - config.cache_store = :null_store + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join('tmp', 'caching-dev.txt').exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{2.days.to_i}" + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # Store uploaded files on the local file system (see config/storage.yml for options). + # config.active_storage.service = :local # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + config.action_mailer.perform_caching = false + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + # config.active_record.migration_error = :page_load + + # Highlight code that triggered database queries in logs. + # config.active_record.verbose_query_logs = true + # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. @@ -31,10 +63,16 @@ # Suppress logger output for asset requests. config.assets.quiet = true - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. # config.file_watcher = ActiveSupport::EventedFileUpdateChecker + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true end diff --git a/spec/dummy/config/environments/production.rb b/spec/dummy/config/environments/production.rb index cd801ae..c6ef419 100644 --- a/spec/dummy/config/environments/production.rb +++ b/spec/dummy/config/environments/production.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'active_support/core_ext/integer/time' + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -16,27 +18,31 @@ config.consider_all_requests_local = false config.action_controller.perform_caching = true + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier + # Compress CSS using a preprocessor. # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # config.asset_host = 'http://assets.example.com' # Specifies the header that your server uses for sending files. # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - # Mount Action Cable outside main process or domain + # Store uploaded files on the local file system (see config/storage.yml for options). + # config.active_storage.service = :local + + # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil # config.action_cable.url = 'wss://example.com/cable' # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] @@ -44,9 +50,9 @@ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). + config.log_level = :info # Prepend all log lines with the following tags. config.log_tags = [:request_id] @@ -54,9 +60,10 @@ # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Use a real queuing backend for Active Job (and separate queues per environment) + # Use a real queuing backend for Active Job (and separate queues per environment). # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "dummy_#{Rails.env}" + # config.active_job.queue_name_prefix = "dummy_production" + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. @@ -70,16 +77,46 @@ # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify + # Log disallowed deprecations. + config.active_support.disallowed_deprecation = :log + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new # Use a different logger for distributed setups. - # require 'syslog/logger' + # require "syslog/logger" # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') if ENV['RAILS_LOG_TO_STDOUT'].present? - logger = ActiveSupport::Logger.new(STDOUT) + logger = ActiveSupport::Logger.new($stdout) logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) + config.logger = ActiveSupport::TaggedLogging.new(logger) end + + # Do not dump schema after migrations. + # config.active_record.dump_schema_after_migration = false + + # Inserts middleware to perform automatic connection switching. + # The `database_selector` hash is used to pass options to the DatabaseSelector + # middleware. The `delay` is used to determine how long to wait after a write + # to send a subsequent read to the primary. + # + # The `database_resolver` class is used by the middleware to determine which + # database is appropriate to use based on the time delay. + # + # The `database_resolver_context` class is used by the middleware to set + # timestamps for the last write to the primary. The resolver uses the context + # class timestamps to determine how long to wait before reading from the + # replica. + # + # By default Rails will store a last write timestamp in the session. The + # DatabaseSelector middleware is designed as such you can define your own + # strategy for connection switching and pass that into the middleware through + # these configuration options. + # config.active_record.database_selector = { delay: 2.seconds } + # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver + # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session end diff --git a/spec/dummy/config/environments/test.rb b/spec/dummy/config/environments/test.rb index 451f7cc..3187519 100644 --- a/spec/dummy/config/environments/test.rb +++ b/spec/dummy/config/environments/test.rb @@ -1,12 +1,15 @@ # frozen_string_literal: true +require 'active_support/core_ext/integer/time' + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! config.cache_classes = true # Do not eager load code on boot. This avoids loading your whole application @@ -14,9 +17,16 @@ # preloads Rails for running tests, you may have to set it to true. config.eager_load = false + # Configure public file server for tests with Cache-Control for performance. + config.public_file_server.enabled = true + config.public_file_server.headers = { + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" + } + # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false + config.cache_store = :null_store # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false @@ -24,6 +34,11 @@ # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + # Store uploaded files on the local file system in a temporary directory. + # config.active_storage.service = :test + + config.action_mailer.perform_caching = false + # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. @@ -32,6 +47,15 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true end diff --git a/spec/dummy/config/initializers/application_controller_renderer.rb b/spec/dummy/config/initializers/application_controller_renderer.rb index 315ac48..f4556db 100644 --- a/spec/dummy/config/initializers/application_controller_renderer.rb +++ b/spec/dummy/config/initializers/application_controller_renderer.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true # Be sure to restart your server when you modify this file. -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/spec/dummy/config/initializers/assets.rb b/spec/dummy/config/initializers/assets.rb index 678efe9..6f260d5 100644 --- a/spec/dummy/config/initializers/assets.rb +++ b/spec/dummy/config/initializers/assets.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true - # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' +# Rails.application.config.assets.version = '1.0' -# Add additional assets to the asset load path +# Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path # Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. -# Rails.application.config.assets.precompile += %w( search.js ) +# application.js, application.css, and all non-JS/CSS in the app/assets +# folder are already added. +# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/spec/dummy/config/initializers/backtrace_silencers.rb b/spec/dummy/config/initializers/backtrace_silencers.rb index d0f0d3b..d43cc1d 100644 --- a/spec/dummy/config/initializers/backtrace_silencers.rb +++ b/spec/dummy/config/initializers/backtrace_silencers.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true + # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } +# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code +# by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". +Rails.backtrace_cleaner.remove_silencers! if ENV['BACKTRACE'] diff --git a/spec/dummy/config/initializers/content_security_policy.rb b/spec/dummy/config/initializers/content_security_policy.rb new file mode 100644 index 0000000..98230c9 --- /dev/null +++ b/spec/dummy/config/initializers/content_security_policy.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy +# For further information see the following documentation +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +# Rails.application.config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https + +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end + +# If you are using UJS then enable automatic nonce generation +# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } + +# Set the nonce only to specific directives +# Rails.application.config.content_security_policy_nonce_directives = %w(script-src) + +# Report CSP violations to a specified URI +# For further information see the following documentation: +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only +# Rails.application.config.content_security_policy_report_only = true diff --git a/spec/dummy/config/initializers/filter_parameter_logging.rb b/spec/dummy/config/initializers/filter_parameter_logging.rb index 7a4f47b..3babc73 100644 --- a/spec/dummy/config/initializers/filter_parameter_logging.rb +++ b/spec/dummy/config/initializers/filter_parameter_logging.rb @@ -3,4 +3,6 @@ # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] +Rails.application.config.filter_parameters += %i[ + passw secret token _key crypt salt certificate otp ssn +] diff --git a/spec/dummy/config/initializers/new_framework_defaults.rb b/spec/dummy/config/initializers/new_framework_defaults.rb deleted file mode 100644 index 268ca84..0000000 --- a/spec/dummy/config/initializers/new_framework_defaults.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.0 upgrade. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Configure SSL options to enable HSTS with subdomains. Previous versions had false. -Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/spec/dummy/config/initializers/permissions_policy.rb b/spec/dummy/config/initializers/permissions_policy.rb new file mode 100644 index 0000000..50bcf4e --- /dev/null +++ b/spec/dummy/config/initializers/permissions_policy.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +# Define an application-wide HTTP permissions policy. For further +# information see https://developers.google.com/web/updates/2018/06/feature-policy +# +# Rails.application.config.permissions_policy do |f| +# f.camera :none +# f.gyroscope :none +# f.microphone :none +# f.usb :none +# f.fullscreen :self +# f.payment :self, "https://secure.example.com" +# end diff --git a/spec/dummy/config/initializers/session_store.rb b/spec/dummy/config/initializers/session_store.rb deleted file mode 100644 index 969d977..0000000 --- a/spec/dummy/config/initializers/session_store.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -Rails.application.config.session_store :cookie_store, key: '_dummy_session' diff --git a/spec/dummy/config/locales/en.yml b/spec/dummy/config/locales/en.yml index 0653957..cf9b342 100644 --- a/spec/dummy/config/locales/en.yml +++ b/spec/dummy/config/locales/en.yml @@ -16,8 +16,18 @@ # # This would use the information in config/locales/es.yml. # +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# 'true': 'foo' +# # To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. +# available at https://guides.rubyonrails.org/i18n.html. en: hello: "Hello world" diff --git a/spec/dummy/config/puma.rb b/spec/dummy/config/puma.rb index 03a2824..8ae6a78 100644 --- a/spec/dummy/config/puma.rb +++ b/spec/dummy/config/puma.rb @@ -1,24 +1,33 @@ # frozen_string_literal: true # Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers a minimum and maximum. +# The `threads` method setting takes two numbers: a minimum and maximum. # Any libraries that use thread pools should be configured to match # the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum, this matches the default thread size of Active Record. +# and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i -threads threads_count, threads_count +max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5) +min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } +threads min_threads_count, max_threads_count -# Specifies the `port` that Puma will listen on to receive requests, default is 3000. +# Specifies the `worker_timeout` threshold that Puma will use to wait before +# terminating a worker in development environments. # -port ENV.fetch('PORT') { 3000 } +worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch('PORT', 3000) # Specifies the `environment` that Puma will run in. # -environment ENV.fetch('RAILS_ENV') { 'development' } +environment ENV.fetch('RAILS_ENV', 'development') + +# Specifies the `pidfile` that Puma will use. +pidfile ENV.fetch('PIDFILE', 'tmp/pids/server.pid') # Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together +# Workers are forked web server processes. If using threads and workers together # the concurrency of the application would be max `threads` * `workers`. # Workers do not work on JRuby or Windows (both of which do not support # processes). @@ -28,22 +37,9 @@ # Use the `preload_app!` method when specifying a `workers` number. # This directive tells Puma to first boot the application and load code # before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. If you use this option -# you need to make sure to reconnect any threads in the `on_worker_boot` -# block. +# process behavior so workers use less memory. # # preload_app! -# The code in the `on_worker_boot` will be called if you are using -# clustered mode by specifying a number of `workers`. After each worker -# process is booted this block will be run, if you are using `preload_app!` -# option you will want to use this block to reconnect to any threads -# or connections that may have been created at application boot, Ruby -# cannot share connections between processes. -# -# on_worker_boot do -# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) -# end - # Allow puma to be restarted by `rails restart` command. plugin :tmp_restart diff --git a/spec/dummy/config/routes.rb b/spec/dummy/config/routes.rb index c13cda8..a214ddb 100644 --- a/spec/dummy/config/routes.rb +++ b/spec/dummy/config/routes.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true Rails.application.routes.draw do - mount LetterOpenerWeb::Engine => '/letter_opener' - - root to: 'home#index' - post '/', to: 'home#create' + mount LetterOpenerWeb::Engine => '/letter_opener_web' end diff --git a/spec/dummy/config/secrets.yml b/spec/dummy/config/secrets.yml deleted file mode 100644 index 05e3837..0000000 --- a/spec/dummy/config/secrets.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rails secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -development: - secret_key_base: a620e35d747d88c5fc8892b46a0fbb32b6b9975b2edcb4aaf1b76a8e46b70cca77d55c55ff1146a14ea9ee91eec9a6a5ebda33745cb5c05a58968d2b318af86b - -test: - secret_key_base: a18afce29fc99e78cd2abf56c7b4bade7bba4da68ca781dbaa3cb53afe9819f6cf0a42667a6d0c9a23bfe34ce070eeaef32f14b67887d25c9333d9371df30ee6 - -# Do not keep production secrets in the repository, -# instead read values from the environment. -production: - secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/spec/dummy/config/spring.rb b/spec/dummy/config/spring.rb deleted file mode 100644 index c5933e4..0000000 --- a/spec/dummy/config/spring.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -%w[ - .ruby-version - .rbenv-vars - tmp/restart.txt - tmp/caching-dev.txt -].each { |path| Spring.watch(path) } diff --git a/spec/dummy/public/404.html b/spec/dummy/public/404.html index b612547..2be3af2 100644 --- a/spec/dummy/public/404.html +++ b/spec/dummy/public/404.html @@ -4,7 +4,7 @@ The page you were looking for doesn't exist (404) - +
diff --git a/spec/dummy/public/422.html b/spec/dummy/public/422.html index a21f82b..c08eac0 100644 --- a/spec/dummy/public/422.html +++ b/spec/dummy/public/422.html @@ -4,7 +4,7 @@ The change you wanted was rejected (422) - +
diff --git a/spec/dummy/public/500.html b/spec/dummy/public/500.html index 061abc5..78a030a 100644 --- a/spec/dummy/public/500.html +++ b/spec/dummy/public/500.html @@ -4,7 +4,7 @@ We're sorry, but something went wrong (500) - +
diff --git a/spec/dummy/storage/.keep b/spec/dummy/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/spec/letter_opener_web_spec.rb b/spec/letter_opener_web_spec.rb index bf87a6d..79f7fce 100644 --- a/spec/letter_opener_web_spec.rb +++ b/spec/letter_opener_web_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'spec_helper' +require 'rails_helper' -describe LetterOpenerWeb do +RSpec.describe LetterOpenerWeb do subject { described_class } after(:each) { described_class.reset! } diff --git a/spec/models/letter_opener_web/letter_spec.rb b/spec/models/letter_opener_web/letter_spec.rb index cbe318d..1e1245c 100644 --- a/spec/models/letter_opener_web/letter_spec.rb +++ b/spec/models/letter_opener_web/letter_spec.rb @@ -1,19 +1,30 @@ # frozen_string_literal: true -describe LetterOpenerWeb::Letter do +RSpec.describe LetterOpenerWeb::Letter do let(:location) { Pathname.new(__dir__).join('..', '..', 'tmp').cleanpath } def rich_text(mail_id) - <<-MAIL -Rich text for #{mail_id} - - - - Link text - -Bar - -
inside address
+ <<~MAIL + Rich text for #{mail_id} + + +
+
+
+
From:
+
noreply@example.com
+
+
+ + + + Link text + + Bar + +
inside address
+
+ MAIL end @@ -36,6 +47,28 @@ def rich_text(mail_id) FileUtils.rm_rf(location) end + describe 'rich text headers' do + let(:id) { '1111_1111' } + subject { described_class.new(id: id).headers } + + before do + FileUtils.rm_rf("#{location}/#{id}/plain.html") + end + + it { is_expected.to match(%r{
\s*
From:
\s*
noreply@example\.com
}m) } + end + + describe 'plain text headers' do + let(:id) { '1111_1111' } + subject { described_class.new(id: id).headers } + + before do + FileUtils.rm_rf("#{location}/#{id}/rich.html") + end + + it { is_expected.to eq('UNABLE TO PARSE HEADERS') } + end + describe 'rich text version' do let(:id) { '1111_1111' } subject { described_class.new(id: id).rich_text } @@ -45,9 +78,10 @@ def rich_text(mail_id) it 'changes links to show up on a new window' do link_html = [ "", - "", - "Link text\n" - ].join("\n ") + " ", + ' Link text', + '' + ].join("\n ") expect(subject).to include(link_html) end