Skip to content

Commit

Permalink
Introduce suspenders:email generator
Browse files Browse the repository at this point in the history
Creates [email interceptor][] to ensure emails are sent to preconfigured
addresses in non-production environments. Drops support for [Recipient
Interceptor][], since this functionality is supported in Rails Core.

In order to enable the interceptor, set `EMAIL_RECIPIENTS` to a
comma-separated list.

```sh
EMAIL_RECIPIENTS="[email protected],[email protected]" bin/rails s
```

Configures [default_url_options][] in `test` and `development`
environments so that `*_url` helpers will work.

Note that we do not configure `production`, since we've determined the
`delivery_method` is something each project will configure
independently. This means we'll just use the [default configuration][],
which is `smtp`.

[email interceptor]: https://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
[Recipient Interceptor]: https://github.com/croaky/recipient_interceptor
[default_url_options]: https://guides.rubyonrails.org/action_mailer_basics.html#generating-urls-in-action-mailer-views
[default configuration]: https://guides.rubyonrails.org/configuring.html#config-action-mailer-delivery-method
  • Loading branch information
stevepolitodesign committed Feb 28, 2024
1 parent 647bb4c commit c1ecef7
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Unreleased
* Introduce `suspenders:setup` generator
* Introduce `suspenders:tasks` generator
* Introduce `suspenders:db:migrate` task
* Introduce `suspenders:email` generator

20230113.0 (January, 13, 2023)

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ bin/rails suspenders:rake
bin/rails suspenders:db:migrate
```

### Email

Intercepts emails in non-production environments by setting `INTERCEPTOR_ADDRESSES`.

```sh
EMAIL_RECIPIENTS="[email protected],[email protected]" bin/rails s
```

Configures `default_url_options` in `test` and `development`.

```
bin/rails g suspenders:email
```

## Contributing

See the [CONTRIBUTING] document.
Expand Down
52 changes: 52 additions & 0 deletions lib/generators/suspenders/email_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module Suspenders
module Generators
class EmailGenerator < Rails::Generators::Base
source_root File.expand_path("../../templates/email", __FILE__)
desc <<~TEXT
Intercepts emails in non-production environments by setting `INTERCEPTOR_ADDRESSES`.
```sh
EMAIL_RECIPIENTS="[email protected],[email protected]" bin/rails s
```
Configures `default_url_options` in `test` and `development`.
TEXT

def create_email_interceptor
copy_file "email_interceptor.rb", "app/mailers/email_interceptor.rb"
end

def create_email_interceptor_initializer
initializer "email_interceptor.rb", <<~RUBY
Rails.application.configure do
if ENV["INTERCEPTOR_ADDRESSES"].present?
config.action_mailer.interceptors = %w[EmailInterceptor]
end
end
RUBY
end

def configure_email_interceptor
environment do
%(
config.to_prepare do
EmailInterceptor.config.interceptor_addresses = ENV.fetch("INTERCEPTOR_ADDRESSES", "").split(",")
end
)
end
end

# TODO: Remove once https://github.com/rails/rails/pull/51191 is in latest
# Rails release
def configure_development_environment
environment %(config.action_mailer.default_url_options = { host: "localhost", port: 3000 }), env: "development"
end

# TODO: Remove once https://github.com/rails/rails/pull/51191 is in latest
# Rails release
def configure_test_environment
environment %(config.action_mailer.default_url_options = { host: "www.example.com" }), env: "test"
end
end
end
end
11 changes: 11 additions & 0 deletions lib/generators/templates/email/email_interceptor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class EmailInterceptor
include ActiveSupport::Configurable

config_accessor :interceptor_addresses, default: []

def self.delivering_email(message)
to = interceptor_addresses

message.to = to if to.any?
end
end
91 changes: 91 additions & 0 deletions test/generators/suspenders/email_generator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
require "test_helper"
require "generators/suspenders/email_generator"

module Suspenders
module Generators
class EmailGeneratorTest < Rails::Generators::TestCase
include Suspenders::TestHelpers

tests Suspenders::Generators::EmailGenerator
destination Rails.root
setup :prepare_destination
teardown :restore_destination

test "creates a mailer intercepter" do
expected = <<~RUBY
class EmailInterceptor
include ActiveSupport::Configurable
config_accessor :interceptor_addresses, default: []
def self.delivering_email(message)
to = interceptor_addresses
message.to = to if to.any?
end
end
RUBY

run_generator

assert_file app_root("app/mailers/email_interceptor.rb") do |file|
assert_equal expected, file
end
end

test "creates initializer" do
expected = <<~RUBY
Rails.application.configure do
if ENV["INTERCEPTOR_ADDRESSES"].present?
config.action_mailer.interceptors = %w[EmailInterceptor]
end
end
RUBY

run_generator

assert_file app_root("config/initializers/email_interceptor.rb") do |file|
assert_equal expected, file
end
end

test "configures application to user email intercepter" do
run_generator

assert_file app_root("config/application.rb"), /config\.to_prepare\s*do.\s*EmailInterceptor\.config\.interceptor_addresses\s*=\s*ENV\.fetch\("INTERCEPTOR_ADDRESSES",\s*\"\"\)\.split\(\",\"\).\s*end/m
end

test "configures action mailer in development" do
run_generator

assert_file app_root("config/environments/development.rb"), /config\.action_mailer\.default_url_options\s*=\s*{\s*host:\s*"localhost",\s*port:\s*3000\s*}/
end

test "configures action mailer in test" do
run_generator

assert_file app_root("config/environments/test.rb"), /config\.action_mailer\.default_url_options\s*=\s*{\s*host:\s*"www\.example\.com"\s*}/
end

test "has custom description" do
assert_no_match(/Description/, generator_class.desc)
end

private

def prepare_destination
backup_file "config/application.rb"
backup_file "config/environments/development.rb"
backup_file "config/environments/test.rb"
end

def restore_destination
restore_file "config/application.rb"
restore_file "config/environments/development.rb"
restore_file "config/environments/test.rb"
remove_file_if_exists "app/mailers/email_interceptor.rb"
remove_file_if_exists "config/initializers/email_interceptor.rb"
end
end
end
end

0 comments on commit c1ecef7

Please sign in to comment.