├── .github └── workflows │ └── ruby.yml ├── .gitignore ├── .rspec ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── letter_opener.gemspec ├── lib ├── letter_opener.rb └── letter_opener │ ├── configuration.rb │ ├── delivery_method.rb │ ├── message.rb │ ├── railtie.rb │ ├── tasks │ └── letter_opener.rake │ └── templates │ ├── default.html.erb │ └── light.html.erb └── spec ├── letter_opener ├── delivery_method_spec.rb └── message_spec.rb └── spec_helper.rb /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake 6 | # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby 7 | 8 | name: Ruby 9 | 10 | on: 11 | push: 12 | branches: [ master ] 13 | pull_request: 14 | branches: [ master ] 15 | 16 | jobs: 17 | test: 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | ruby-version: ['3.0', '3.1', '3.2', '3.3', 'truffleruby', 'jruby'] 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Ruby 26 | # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, 27 | # change this to (see https://github.com/ruby/setup-ruby#versioning): 28 | uses: ruby/setup-ruby@v1 29 | with: 30 | ruby-version: ${{ matrix.ruby-version }} 31 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 32 | - name: Run tests 33 | run: bundle exec rake 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle 3 | Gemfile.lock 4 | pkg/* 5 | tmp/* 6 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --backtrace 3 | --order rand 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.10.0 ## 2 | * Allow Launchy 3.0+. 3 | 4 | ## 1.9.0 ## 5 | * Store mail location in `Mail::Message` object (thanks [Jonathan Chan](https://github.com/jonmchan)) 6 | * Drop Ruby 2 support. Support only Ruby 3.0+ 7 | * Remove `nkf` gem dependency. 8 | 9 | ## 1.8.1 ## 10 | * Fix duplication of Rails tasks caused by LetterOpener's rake task file. (thanks [zarqman](https://github.com/zarqman)) 11 | 12 | ## 1.8.0 ## 13 | * Allow configuration of the 'file///' part in Launchy.open path (thanks [ 14 | Regis Millet](https://github.com/Kulgar)) 15 | * Enhance 'tmp:clear' task to delete 'tmp/letter_opener' files (thanks [Joaquín Vicente](https://github.com/wacko)) 16 | * Convert mail's subject to UTF-8. (thanks [kuroponzu](https://github.com/kuroponzu)) 17 | * Use proper attachment's filename sanitization so we don't escape Japanese characters. 18 | 19 | ## 1.7.0 ## 20 | * Use default configuration in `Message::rendered_messages` (thanks [Krystan HuffMenne 21 | ](https://github.com/gitKrystan)) 22 | * Do not use `Rails.root` path if LetterOpener is used outside of Rails (thanks [centrevillage](https://github.com/centrevillage)) 23 | * Allow to set only `Mail#cc`/`Mail#bcc` without `Mail#to`. 24 | 25 | ## 1.6.0 ## 26 | * Do not depend on Mail gem to check delivery params. 27 | * Do not parse and escape url before passing it to Launchy. 28 | 29 | ## 1.5.0 ## 30 | * Use proper check for `Rails::Railties` (thanks [Florian Weingarten](https://github.com/fw42)) 31 | * Add a shim for the iFrame "srcdoc" attribute (make it work with IE). 32 | * Do not convert `-` to `_` in attachment file names. (thanks [Steven Harman](https://github.com/stevenharman)) 33 | * Drop Ruby 1.9 support. 34 | * Escape inline attachment names the same way they are stored in the attachments directory (thanks [Daniel Rikowski](https://github.com/daniel-rikowski)) 35 | * Increase timestamp precision in the mail filename. (thanks [Piotr Usewicz](https://github.com/pusewicz)) 36 | * Add ability to configure location of stored mails. (thanks [Ben](https://github.com/beejamin)) 37 | * Add light version of template for mails that doesn't render any additional styling. (thanks [Ben](https://github.com/beejamin)) 38 | 39 | ## 1.4.1 ## 40 | * Stop base tag appearing in plain-text previews. (thanks [Coby Chapple](https://github.com/cobyism)) 41 | 42 | ## 1.4.0 ## 43 | * Add base tag to the iframe so links work with X-Frame-Options set to SAMEORIGIN. (thanks [Jason Tokoph](https://github.com/jtokoph)) 44 | * Check delivery params before rendering an email to match SMTP behaviour. 45 | 46 | ## 1.3.0 ## 47 | 48 | * Fix message body encoding is observed correctly in QP CTE. (thanks [Mark Dodwell](https://github.com/mkdynamic)) 49 | * Remove fixed width on the mail content. (thanks [weexpectedTHIS](https://github.com/weexpectedTHIS)) 50 | * Render email content in the iframe. Fixes [#98](https://github.com/ryanb/letter_opener/issues/98). (thanks [Jacob Maine](https://github.com/mainej)) 51 | 52 | ## 1.2.0 ## 53 | 54 | * Fix auto_link() which in some cases would return an empty tag for plain text messages. (thanks [Kevin McPhillips](https://github.com/kmcphillips)) 55 | * Update styles. (thanks [Adam Doppelt](https://github.com/gurgeous)) 56 | 57 | ## 1.1.2 ## 58 | 59 | * Show formatted display names in html template (thanks [ClaireMcGinty](https://github.com/ClaireMcGinty)) 60 | * Use `file:///` uri scheme to fix Launchy on Windows. 61 | 62 | ## 1.1.1 ## 63 | 64 | * Handle cc and bcc as array of emails. (thanks [jordandcarter](https://github.com/jordandcarter)) 65 | * Use `file://` uri scheme since Launcy can't open escaped URL without it. (thanks [Adrian2112](https://github.com/Adrian2112)) 66 | * Update Launchy dependency to `~> 2.2` (thanks [JeanMertz](https://github.com/JeanMertz)) 67 | * Change all nonword chars in filename of attachment to underscore so 68 | it can be saved on all platforms. (thanks [phallstrom](https://github.com/phallstrom)) 69 | 70 | ## 1.1.0 ## 71 | 72 | * Update Launchy dependency to `~> 2.2.0` (thanks [webdevotion](https://github.com/webdevotion)) 73 | * Handle `sender` field (thanks [sjtipton](https://github.com/sjtipton)) 74 | * Show subject only if it's present (thanks [jadehyper](https://github.com/jadehyper)) 75 | * Show subject as title of web page (thanks [statique](https://github.com/statique)) 76 | 77 | ## 1.0.0 ## 78 | 79 | * Attachment Support (thanks [David Cornu](https://github.com/davidcornu)) 80 | * Escape HTML in subject and other fields 81 | * Raise an exception if the :location option is not present instead of using a default 82 | * Open rich version by default (thanks [Damir](https://github.com/sidonath)) 83 | * Override margin on dt and dd elements in CSS (thanks [Edgars Beigarts](https://github.com/ebeigarts)) 84 | * Autolink URLs in plain version (thanks [Matt Burke](https://github.com/spraints)) 85 | 86 | ## 0.1.0 ## 87 | 88 | * From and To show name and Email when specified 89 | * Fix bug when letter_opener couldn't open email in Windows 90 | * Handle spaces in the application path (thanks [Mike Boone](https://github.com/boone)) 91 | * As letter_opener doesn't work with Launchy < 2.0.4 let's depend on >= 2.0.4 (thanks [Samnang Chhun](https://github.com/samnang)) 92 | * Handle `reply_to` field (thanks [Wes Gibbs](https://github.com/wgibbs)) 93 | * Set the charset in email preview (thanks [Bruno Michel](https://github.com/nono)) 94 | 95 | ## 0.0.2 ## 96 | 97 | * Fixing launchy requirement (thanks [Bruno Michel](https://github.com/nono)) 98 | 99 | ## 0.0.1 ## 100 | 101 | * Initial release 102 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | 4 | if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.1") 5 | gem "net-imap", require: false 6 | gem "net-pop", require: false 7 | gem "net-smtp", require: false 8 | end 9 | 10 | gem "rake" 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Ryan Bates 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Letter Opener [](https://github.com/ryanb/letter_opener/actions/workflows/ruby.yml) 2 | 3 | Preview email in the default browser instead of sending it. This means you do not need to set up email delivery in your development environment, and you no longer need to worry about accidentally sending a test email to someone else's address. 4 | 5 | ## Rails Setup 6 | 7 | First add the gem to your development environment and run the `bundle` command to install it. 8 | 9 | ```rb 10 | gem "letter_opener", group: :development 11 | ``` 12 | 13 | Then set the delivery method in `config/environments/development.rb` 14 | 15 | ```rb 16 | config.action_mailer.delivery_method = :letter_opener 17 | config.action_mailer.perform_deliveries = true 18 | ``` 19 | 20 | Now any email will pop up in your browser instead of being sent. The messages are stored in `tmp/letter_opener`. 21 | If you want to change application that will be used to open your emails you should override `LAUNCHY_APPLICATION` environment variable or set `Launchy.application` in the initializer. 22 | 23 | ### Configuration 24 | 25 | ```rb 26 | LetterOpener.configure do |config| 27 | # To overrider the location for message storage. 28 | # Default value is `tmp/letter_opener` 29 | config.location = Rails.root.join('tmp', 'my_mails') 30 | 31 | # To render only the message body, without any metadata or extra containers or styling. 32 | # Default value is `:default` that renders styled message with showing useful metadata. 33 | config.message_template = :light 34 | 35 | # To change default file URI scheme you can provide `file_uri_scheme` config. 36 | # It might be useful when you use WSL (Windows Subsystem for Linux) and default 37 | # scheme doesn't work for you. 38 | # Default value is blank 39 | config.file_uri_scheme = 'file://///wsl$/Ubuntu-18.04' 40 | end 41 | ``` 42 | 43 | ## Non Rails Setup 44 | 45 | If you aren't using Rails, this can be easily set up with the Mail gem. Just set the delivery method when configuring Mail and specify a location. 46 | 47 | ```rb 48 | require "letter_opener" 49 | Mail.defaults do 50 | delivery_method LetterOpener::DeliveryMethod, location: File.expand_path('../tmp/letter_opener', __FILE__) 51 | end 52 | ``` 53 | 54 | The method is similar if you're using the Pony gem: 55 | 56 | ```rb 57 | require "letter_opener" 58 | Pony.options = { 59 | via: LetterOpener::DeliveryMethod, 60 | via_options: {location: File.expand_path('../tmp/letter_opener', __FILE__)} 61 | } 62 | ``` 63 | 64 | Alternatively, if you are using ActionMailer directly (without Rails) you will need to add the delivery method. 65 | 66 | ```rb 67 | require "letter_opener" 68 | ActionMailer::Base.add_delivery_method :letter_opener, LetterOpener::DeliveryMethod, :location => File.expand_path('../tmp/letter_opener', __FILE__) 69 | ActionMailer::Base.delivery_method = :letter_opener 70 | ``` 71 | 72 | ## Remote Alternatives 73 | 74 | Letter Opener uses [Launchy](https://github.com/copiousfreetime/launchy) to open sent mail in the browser. This assumes the Ruby process is running on the local development machine. If you are using a separate staging server or VM this will not work. In that case consider using [Mailtrap](http://mailtrap.io/) or [MailCatcher](http://mailcatcher.me/). 75 | 76 | If you are running your application within a Docker Container or VM and do not have a browser available to open emails received by Letter Opener, you may see the following error: 77 | 78 | ``` 79 | WARN: Launchy::CommandNotFoundError: Unable to find a browser command. If this is unexpected, Please rerun with environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug at https://github.com/copiousfreetime/launchy/issues/new 80 | ``` 81 | 82 | To resolve this, simply set the following ENV variables: 83 | 84 | ``` 85 | LAUNCHY_DRY_RUN=true 86 | BROWSER=/dev/null 87 | ``` 88 | 89 | In order to keep this project simple, I don't have plans to turn it into a Rails engine with an interface for browsing the sent mail but there is a [gem you can use for that](https://github.com/fgrehm/letter_opener_web). 90 | 91 | 92 | ## Development & Feedback 93 | 94 | Questions or problems? Please use the [issue tracker](https://github.com/ryanb/letter_opener/issues). If you would like to contribute to this project, fork this repository and run `bundle` and `rake` to run the tests. Pull requests appreciated. 95 | 96 | Special thanks to the [mail_view](https://github.com/37signals/mail_view/) gem for inspiring this project and for their mail template. Also thanks to [Vasiliy Ermolovich](https://github.com/nashby) for helping manage this project. 97 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | require 'rspec/core/rake_task' 4 | 5 | desc "Run RSpec" 6 | RSpec::Core::RakeTask.new do |t| 7 | t.verbose = false 8 | end 9 | task :default => :spec 10 | -------------------------------------------------------------------------------- /letter_opener.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = "letter_opener" 3 | s.version = "1.10.0" 4 | s.author = "Ryan Bates" 5 | s.email = "ryan@railscasts.com" 6 | s.homepage = "https://github.com/ryanb/letter_opener" 7 | s.summary = "Preview mail in browser instead of sending." 8 | s.description = "When mail is sent from your application, Letter Opener will open a preview in the browser instead of sending." 9 | s.license = "MIT" 10 | 11 | s.files = Dir["{lib}/**/*", "[A-Z]*"] - ["Gemfile.lock"] 12 | s.require_path = "lib" 13 | 14 | s.add_dependency 'launchy', '>= 2.2', '< 4' 15 | 16 | s.add_development_dependency 'rspec', '~> 3.10.0' 17 | s.add_development_dependency 'mail', '~> 2.6.0' 18 | 19 | s.required_rubygems_version = ">= 1.3.4" 20 | 21 | if s.respond_to?(:metadata) 22 | s.metadata = { 23 | 'bug_tracker_uri' => 'https://github.com/ryanb/letter_opener/issues', 24 | 'changelog_uri' => 'https://github.com/ryanb/letter_opener/blob/master/CHANGELOG.md', 25 | 'documentation_uri' => 'http://www.rubydoc.info/gems/letter_opener/', 26 | 'homepage_uri' => 'https://github.com/ryanb/letter_opener', 27 | 'source_code_uri' => 'https://github.com/ryanb/letter_opener/', 28 | } 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/letter_opener.rb: -------------------------------------------------------------------------------- 1 | module LetterOpener 2 | autoload :Message, "letter_opener/message" 3 | autoload :DeliveryMethod, "letter_opener/delivery_method" 4 | autoload :Configuration, "letter_opener/configuration" 5 | 6 | def self.configuration 7 | @configuration ||= Configuration.new 8 | end 9 | 10 | def self.configure 11 | yield(configuration) 12 | end 13 | end 14 | 15 | require "letter_opener/railtie" if defined?(Rails::Railtie) 16 | -------------------------------------------------------------------------------- /lib/letter_opener/configuration.rb: -------------------------------------------------------------------------------- 1 | module LetterOpener 2 | class Configuration 3 | attr_accessor :location, :message_template, :file_uri_scheme 4 | 5 | def initialize 6 | @location = Rails.root.join('tmp', 'letter_opener') if defined?(Rails) && Rails.respond_to?(:root) && Rails.root 7 | @message_template = 'default' 8 | @file_uri_scheme = nil 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/letter_opener/delivery_method.rb: -------------------------------------------------------------------------------- 1 | require "digest/sha1" 2 | require "launchy" 3 | 4 | module LetterOpener 5 | class DeliveryMethod 6 | class InvalidOption < StandardError; end 7 | 8 | attr_accessor :settings 9 | 10 | def initialize(options = {}) 11 | options[:message_template] ||= LetterOpener.configuration.message_template 12 | options[:location] ||= LetterOpener.configuration.location 13 | options[:file_uri_scheme] ||= LetterOpener.configuration.file_uri_scheme 14 | 15 | raise InvalidOption, "A location option is required when using the Letter Opener delivery method" if options[:location].nil? 16 | 17 | self.settings = options 18 | end 19 | 20 | def deliver!(mail) 21 | validate_mail!(mail) 22 | location = File.join(settings[:location], "#{Time.now.to_f.to_s.tr('.', '_')}_#{Digest::SHA1.hexdigest(mail.encoded)[0..6]}") 23 | 24 | messages = Message.rendered_messages(mail, location: location, message_template: settings[:message_template]) 25 | ::Launchy.open("#{settings[:file_uri_scheme]}#{messages.first.filepath}") 26 | end 27 | 28 | private 29 | 30 | def validate_mail!(mail) 31 | if !mail.smtp_envelope_from || mail.smtp_envelope_from.empty? 32 | raise ArgumentError, "SMTP From address may not be blank" 33 | end 34 | 35 | if !mail.smtp_envelope_to || mail.smtp_envelope_to.empty? 36 | raise ArgumentError, "SMTP To address may not be blank" 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/letter_opener/message.rb: -------------------------------------------------------------------------------- 1 | require "cgi" 2 | require "erb" 3 | require "fileutils" 4 | require "uri" 5 | 6 | module LetterOpener 7 | class Message 8 | attr_reader :mail 9 | 10 | def self.rendered_messages(mail, options = {}) 11 | messages = [] 12 | messages << new(mail, options.merge(part: mail.html_part)) if mail.html_part 13 | messages << new(mail, options.merge(part: mail.text_part)) if mail.text_part 14 | messages << new(mail, options) if messages.empty? 15 | messages.each(&:render) 16 | messages.sort 17 | end 18 | 19 | ERROR_MSG = '%s or default configuration must be given'.freeze 20 | 21 | def initialize(mail, options = {}) 22 | @mail = mail 23 | @location = options[:location] || LetterOpener.configuration.location 24 | @part = options[:part] 25 | @template = options[:message_template] || LetterOpener.configuration.message_template 26 | @attachments = [] 27 | 28 | raise ArgumentError, ERROR_MSG % 'options[:location]' unless @location 29 | raise ArgumentError, ERROR_MSG % 'options[:message_template]' unless @template 30 | end 31 | 32 | def render 33 | FileUtils.mkdir_p(@location) 34 | 35 | if mail.attachments.any? 36 | attachments_dir = File.join(@location, 'attachments') 37 | FileUtils.mkdir_p(attachments_dir) 38 | mail.attachments.each do |attachment| 39 | filename = attachment_filename(attachment) 40 | path = File.join(attachments_dir, filename) 41 | 42 | unless File.exist?(path) # true if other parts have already been rendered 43 | File.open(path, 'wb') { |f| f.write(attachment.body.raw_source) } 44 | end 45 | 46 | @attachments << [attachment.filename, "attachments/#{filename}"] 47 | end 48 | end 49 | 50 | File.open(filepath, 'w') do |f| 51 | f.write ERB.new(template).result(binding) 52 | end 53 | 54 | mail["location_#{type}"] = filepath 55 | end 56 | 57 | def template 58 | File.read(File.expand_path("../templates/#{@template}.html.erb", __FILE__)) 59 | end 60 | 61 | def filepath 62 | File.join(@location, "#{type}.html") 63 | end 64 | 65 | def content_type 66 | @part && @part.content_type || @mail.content_type 67 | end 68 | 69 | def body 70 | @body ||= begin 71 | body = (@part || @mail).decoded 72 | 73 | mail.attachments.each do |attachment| 74 | body.gsub!(attachment.url, "attachments/#{attachment_filename(attachment)}") 75 | end 76 | 77 | body 78 | end 79 | end 80 | 81 | def from 82 | @from ||= Array(@mail['from']).join(", ") 83 | end 84 | 85 | def sender 86 | @sender ||= Array(@mail['sender']).join(", ") 87 | end 88 | 89 | def subject 90 | @subject ||= @mail.subject 91 | end 92 | 93 | def to 94 | @to ||= Array(@mail['to']).join(", ") 95 | end 96 | 97 | def cc 98 | @cc ||= Array(@mail['cc']).join(", ") 99 | end 100 | 101 | def bcc 102 | @bcc ||= Array(@mail['bcc']).join(", ") 103 | end 104 | 105 | def reply_to 106 | @reply_to ||= Array(@mail['reply-to']).join(", ") 107 | end 108 | 109 | def type 110 | content_type =~ /html/ ? "rich" : "plain" 111 | end 112 | 113 | def encoding 114 | body.respond_to?(:encoding) ? body.encoding : "utf-8" 115 | end 116 | 117 | def auto_link(text) 118 | text.gsub(URI::Parser.new.make_regexp(%W[https http])) do |link| 119 | "#{ link }" 120 | end 121 | end 122 | 123 | def h(content) 124 | CGI.escapeHTML(content) 125 | end 126 | 127 | def attachment_filename(attachment) 128 | # Copied from https://github.com/rails/rails/blob/6bfc637659248df5d6719a86d2981b52662d9b50/activestorage/app/models/active_storage/filename.rb#L57 129 | attachment.filename.encode( 130 | Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/\t\r\n\\", "-" 131 | ) 132 | end 133 | 134 | def <=>(other) 135 | order = %w[rich plain] 136 | order.index(type) <=> order.index(other.type) 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /lib/letter_opener/railtie.rb: -------------------------------------------------------------------------------- 1 | module LetterOpener 2 | class Railtie < Rails::Railtie 3 | initializer "letter_opener.add_delivery_method" do 4 | ActiveSupport.on_load :action_mailer do 5 | ActionMailer::Base.add_delivery_method( 6 | :letter_opener, 7 | LetterOpener::DeliveryMethod 8 | ) 9 | end 10 | end 11 | 12 | rake_tasks do 13 | load 'letter_opener/tasks/letter_opener.rake' 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/letter_opener/tasks/letter_opener.rake: -------------------------------------------------------------------------------- 1 | namespace :tmp do 2 | task :letter_opener do 3 | rm_rf Dir["tmp/letter_opener/[^.]*"], verbose: false 4 | end 5 | 6 | task clear: :letter_opener 7 | end 8 | -------------------------------------------------------------------------------- /lib/letter_opener/templates/default.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | <% if mail.subject %> 5 |115 | <% if type == "plain" && mail.html_part %> 116 | View HTML version 117 | <% elsif type == "rich" && mail.text_part %> 118 | View plain text version 119 | <% end %> 120 |
121 | <% end %> 122 |<%= auto_link(h(body)) %>126 | <% else %> 127 | 128 | <% end %> 129 |