Outbound Mailbox
Rails engine that stores outbound email the same way Action Mailbox stores inbound email: Active Record metadata, raw RFC822 source in Active Storage, a Mail delivery method, and an HTML conductor at /rails/conductor/outbound_mailbox/outbound_emails when enabled — by default if Action Mailer uses delivery_method :outbound_mailbox, or any time via config.outbound_mailbox.mount_conductor.
There is no runtime dependency on Action Mailer in the library; the delivery class implements Mail’s delivery contract so any stack that ends in Mail::Message#deliver! can use it. Typical Rails hosts configure Action Mailer to use this method in development.
Installation
Add to your Gemfile:
gem "outbound_mailbox"
Then:
bundle install
bin/rails generate outbound_mailbox:install
bin/rails db:migrate
Optional: use a dedicated Active Storage service for captured mail (mirrors Action Mailbox’s config.action_mailbox.storage_service):
# config/application.rb
config.outbound_mailbox.storage_service = :local # or any configured service name
To browse captured mail via the conductor without using Action Mailer's :outbound_mailbox delivery method (for example you call OutboundMailbox::OutboundEmail.create_from_mail! yourself or use a custom delivery path), enable routes explicitly:
# config/application.rb
config.outbound_mailbox.mount_conductor = true
Set to false to hide the conductor even when delivery_method is :outbound_mailbox. You can also assign a proc: config.outbound_mailbox.mount_conductor = ->(app) { ... } (must return a boolean-coercible value). The default is nil, meaning mount when config.action_mailer.delivery_method is :outbound_mailbox.
Capturing mail in development
# config/environments/development.rb
config.action_mailer.delivery_method = :outbound_mailbox
This replaces real SMTP delivery in that environment (similar to :test or Letter Opener). Plain Mail is also supported if you assign the same delivery class.
Conductor
By default, when config.action_mailer.delivery_method is :outbound_mailbox, the engine registers conductor routes under:
/rails/conductor/outbound_mailbox/outbound_emails
You can override that with config.outbound_mailbox.mount_conductor; see Installation optional settings above.
List and show stored messages, or Incinerate to delete a record and purge its blob. When routes are not registered, those URLs are not routed (404). Route registration follows config/routes.rb (startup and reload in development).
Testing this gem
Tests boot a single-file Rails app in test/dummy/app.rb. Migrations live under test/dummy/db/migrate.
bundle install
bundle exec rake test
bundle exec rubocop
Continuous integration uses the latest stable MRI Ruby ruby/setup-ruby, ruby-version: "ruby" and exercises Rails 8.1 (default Gemfile), Rails 8.0, Rails 7.1, and Rails 7.0 via gemfiles/rails_8_1.gemfile, gemfiles/rails_8_0.gemfile, gemfiles/rails_7_1.gemfile, and gemfiles/rails_7_0.gemfile. To match a matrix entry locally:
BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle install
BUNDLE_GEMFILE=gemfiles/rails_7_0.gemfile bundle exec rake test
The gem’s Gemfile includes actionmailer so config.action_mailer exists in that app; the gemspec does not list action_mailer or actionmailbox as runtime dependencies. Runtime Rails components are constrained to >= 7.0, < 8.2 (actionpack, activestorage, activerecord, railties). The gem requires Ruby 3.1+ (required_ruby_version).
Releasing
Releases are done automatically by GitHub Actions when a new tag is pushed to the repository.
To release a new version, create a pull request updating the "Unreleased" section of CHANGELOG.md file to reflect the upcoming version and expected release date, and to update the version number in lib/outbound_mailbox/version.rb. Once the pull request is merged, create a new tag in the format vX.Y.Z, which will trigger GitHub Actions to publish the new version to RubyGems.
License
MIT — see MIT-LICENSE.