Skip to content

Commit

Permalink
Add ActionMailer support
Browse files Browse the repository at this point in the history
  • Loading branch information
r4do committed Jul 22, 2022
1 parent b554482 commit 0e9e628
Show file tree
Hide file tree
Showing 18 changed files with 395 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ Naming/MethodParameterName:
RSpec/MultipleExpectations:
Max: 4

RSpec/NestedGroups:
Max: 4

Style/Documentation:
Enabled: false

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@
## [1.0.1] - 2022-06-20

- Update packed files list


## [1.1.0] - 2022-07-22

- Add ActionMailer support
8 changes: 7 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
PATH
remote: .
specs:
mailtrap (1.0.1)
mailtrap (1.1.0)
mail
net-smtp

GEM
remote: https://rubygems.org/
Expand All @@ -13,6 +15,10 @@ GEM
rexml
diff-lcs (1.5.0)
hashdiff (1.0.1)
mail (2.7.1)
mini_mime (>= 0.1.1)
mini_mime (1.1.2)
net-smtp (0.1.0)
parallel (1.22.1)
parser (3.1.2.0)
ast (~> 2.4.1)
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,29 @@ client = Mailtrap::Sending::Client.new(api_key: 'your-api-key')
client.send(mail)
```

### ActionMailer

This gem also adds ActionMailer delivery method. To configure it, add following to your ActionMailer configuration (in Rails projects located in `config/$ENVIRONMENT.rb`):
```ruby
config.action_mailer.delivery_method = :mailtrap
config.action_mailer.mailtrap_settings = {
api_key: ENV.fetch('MAILTRAP_API_KEY'),
api_host: ENV.fetch('MAILTRAP_API_HOST'),
api_port: ENV.fetch('MAILTRAP_API_PORT')
}
```
And continue to use ActionMailer as usual.

To add `category` and `custom_variables`, add them to the mail generation:
```ruby
mail(
to: '[email protected]',
subject: 'You are awesome!',
category: 'Test category',
custom_variables: { test_variable: 'abc' }
)
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Expand Down
1 change: 1 addition & 0 deletions lib/mailtrap.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative 'mailtrap/action_mailer' if defined? ActionMailer
require_relative 'mailtrap/sending'
require_relative 'mailtrap/version'

Expand Down
8 changes: 8 additions & 0 deletions lib/mailtrap/action_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

require_relative 'action_mailer/delivery_method'
require_relative 'action_mailer/railtie' if defined? Rails

module Mailtrap
module ActionMailer; end
end
27 changes: 27 additions & 0 deletions lib/mailtrap/action_mailer/delivery_method.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

require 'base64'

module Mailtrap
module ActionMailer
class DeliveryMethod
attr_accessor :settings

def initialize(settings)
self.settings = settings
end

def deliver!(message)
mail = Mailtrap::Sending::Convert.from_message(message)

client.send(mail)
end

private

def client
@client ||= Mailtrap::Sending::Client.new(**settings.slice(:api_key, :api_host, :api_port))
end
end
end
end
13 changes: 13 additions & 0 deletions lib/mailtrap/action_mailer/railtie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Mailtrap
module ActionMailer
class Railtie < Rails::Railtie
initializer 'mailtrap_action_mailer.add_delivery_method', before: 'action_mailer.set_configs' do
ActiveSupport.on_load(:action_mailer) do
::ActionMailer::Base.add_delivery_method(:mailtrap, Mailtrap::ActionMailer::DeliveryMethod)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/mailtrap/sending.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative 'sending/attachment'
require_relative 'sending/client'
require_relative 'sending/convert'
require_relative 'sending/mail'

module Mailtrap
Expand Down
81 changes: 81 additions & 0 deletions lib/mailtrap/sending/convert.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

module Mailtrap
module Sending
module Convert
class << self
def from_message(message) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
Mailtrap::Sending::Mail.new(
from: prepare_address(message['from']&.address_list&.addresses&.first),
to: prepare_addresses(message['to']&.address_list&.addresses),
cc: prepare_addresses(message['cc']&.address_list&.addresses),
bcc: prepare_addresses(message['bcc']&.address_list&.addresses),
subject: message.subject,
text: prepare_text_part(message),
html: prepare_html_part(message),
headers: prepare_headers(message),
attachments: prepare_attachments(message.attachments),
category: message['category']&.unparsed_value,
custom_variables: message['custom_variables']&.unparsed_value
)
end

private

PROCESSED_HEADERS = %w[
from
to
cc
bcc
subject
category
customvariables
contenttype
].freeze

def prepare_addresses(addresses)
Array(addresses).map { |address| prepare_address(address) }
end

def prepare_headers(message)
message
.header_fields
.reject { |header| PROCESSED_HEADERS.include?(header.name.downcase.delete('-')) }
.to_h { |header| [header.name, header.value] }
.compact
end

def prepare_address(address)
{
email: address.address,
name: address.display_name
}.compact
end

def prepare_attachments(attachments_list = [])
attachments_list.map do |attachment|
{
content: Base64.strict_encode64(attachment.body.decoded),
type: attachment.mime_type,
filename: attachment.filename,
disposition: attachment.header[:content_disposition]&.disposition_type,
content_id: attachment.header[:content_id]&.field&.content_id
}
end
end

def prepare_html_part(message)
return message.body.decoded if message.mime_type == 'text/html'

message.html_part&.decoded
end

def prepare_text_part(message)
return message.body.decoded if message.mime_type == 'text/plain' || message.mime_type.nil?

message.text_part&.decoded
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/mailtrap/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Mailtrap
VERSION = '1.0.1'
VERSION = '1.1.0'
end
3 changes: 3 additions & 0 deletions mailtrap.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Gem::Specification.new do |spec|
spec.metadata['changelog_uri'] = 'https://github.com/railsware/mailtrap-ruby/blob/main/CHANGELOG.md'
spec.metadata['rubygems_mfa_required'] = 'true'

spec.add_dependency 'mail'
spec.add_dependency 'net-smtp'

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path(__dir__)) do
Expand Down
Binary file added spec/fixtures/files/attachments/file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions spec/fixtures/files/attachments/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is a text file

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions spec/mailtrap/action_mailer/delivery_method_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

require 'mailtrap/action_mailer'

RSpec.describe Mailtrap::ActionMailer::DeliveryMethod, :vcr do
describe '#deliver!' do
subject(:deliver!) { described_class.new(settings).deliver!(message) }

let(:settings) { { api_key: 'correct-api-key' } }
let(:message) { Mail::Message.new(params) }
let(:params) do
{
from: 'Mailtrap Test <[email protected]>',
to: 'To 1 <[email protected]>, [email protected]',
cc: '[email protected], Cc 2 <[email protected]>',
bcc: '[email protected], [email protected]',
reply_to: '[email protected]',
subject: 'You are awesome!',
category: 'Module Test'
}
end
let(:expected_message_ids) do
%w[
858cbc46-09d5-11ed-91e0-0a58a9feac02
858cbc5c-09d5-11ed-91e0-0a58a9feac02
858cbc6d-09d5-11ed-91e0-0a58a9feac02
858cbc7e-09d5-11ed-91e0-0a58a9feac02
858cbc90-09d5-11ed-91e0-0a58a9feac02
858cbca1-09d5-11ed-91e0-0a58a9feac02
]
end

before do
allow(::Mail::ContentTypeField).to receive(:generate_boundary).and_return('--==_mimepart_random_boundary')
message.text_part = 'Some text'
message.html_part = '<div>HTML part</div>'
message.headers('X-Special-Domain-Specific-Header': 'SecretValue')
message.headers('One-more-custom-header': 'CustomValue')
message.attachments['file.txt'] = File.read('spec/fixtures/files/attachments/file.txt')
message.attachments['file.txt'].content_id = '<[email protected]>'
message.attachments.inline['file.png'] = File.read('spec/fixtures/files/attachments/file.png')
message.attachments['file.png'].content_id = '<[email protected]>'
end

it 'converts the message and sends via API' do
expect(deliver!).to eq({ success: true, message_ids: expected_message_ids })
end
end
end
Loading

0 comments on commit 0e9e628

Please sign in to comment.