
Opa, galera! Nesse post vamos falar sobre o ActionMailer, um recurso nativo do Rails que lida com envio de e-mails. No Rails 5, ele teve um upgrade e vamos falar sobre ele aqui e sobre mais temas interessantes quando precisamos lidar com emails.
O que vamos aprender?
- Como utilizar o MailCatcher
- Utilização de callbacks no Mailer
- Como utilizar Foundations nos Emails
- Pré-visualização de Emails
- Enviar email de forma Assíncrona
- Envio de Emails utilizando serviços como MailGun e Sendgrid
Ferramentas
- Rails 5.1.5
- ActionMailer
- Mailcatcher
- Foundations
- MailGun e Sendgrid
Considerações sobre o App
Nós não vamos construir um app do zero hoje. No post “Como Monitorar seu APP Rails“, construímos juntos um app para aluguel de carros. Bem, vamos pegar este mesmo app e criar um email padrão para confirmar o aluguel de um carro com o ActionMailer e durante o post nós vamos ver como ver os envios e pré-visualizar os emails de um jeito incrível!
Baixando o app
1. Primeiro, baixe o código do repositório do Github:
1 |
git clone https://github.com/OneBitCodeBlog/rent_cars |
2. Vamos entrar no projeto
1 |
cd rent_cars |
3. Repare que este projeto já possui o Devise instalado e configurado
4. Agora vamos criar uma nova branch chamada send_email onde trabalharemos no envio de email
1 |
git checkout -b send_email |
5. Vamos iniciar o Rails server com o comando:
1 |
rails s |
6. Agora basta acessar o app em seu navegador com a url localhost:3000 e testar as funcionalidades de app para cadastro, criação de um novo carro e de um aluguel.
Criando um e-mail básico
Nesta parte nós vamos simplesmente criar um email básico que será enviado ao usuário assim que o aluguel for criado para durante o post trabalharmos nas melhorias.
1. O primeiro passo é criarmos o nosso RentalMailer com a ação de confirmação do aluguel. Para isso vamos usar um comando do Docker Compose:
1 |
rails g mailer rental confirmation |
2. Agora vamos acrescentar o seguinte código na action confirmation do RentailMailer (app/mailers/rental_mailer.rb):
1 2 3 4 |
def confirmation @rental = params[:rental] mail to: @rental.user.email, subject: "Rental Confirmation of #{@rental.car.name}" end |
Nesta action, estamos apenas carregando o Aluguel (Rental) com base nos parâmetros que serão enviados para o Mailer e enviando o email para o usuário do aluguel.
3. A action confirmation vai renderizar as views de mesmo nome nos formatos HTML e Texto. Então, vamos alterar as duas views inserindo a mesma mensagem. A nossa view HTML ficará desse jeito (app/views/rental_mailer/confirmation.html.erb):
1 2 3 4 5 6 7 8 9 10 |
<h1>Rental Confirmation</h1> <p> Hi <%= @rental.user.email %>, </p> <p> You've just rented the car <%= @rental.car.name %> - <%= @rental.car.manufacturer %>. Enjoy it! =) </p> |
5. Agora, vamos fazer o mesmo na view formato Texto. Vamos adicionar a mesma mensagem, porém sem as tags. A nossa view ficará desse jeito (app/views/rental_mailer.text.erb):
1 2 3 4 5 |
Rental Confirmation Hi <%= @rental.user.email %>, You've just rented the car <%= @rental.car.name %> - <%= @rental.car.manufacturer %>. Enjoy it! =) |
Podemos reparar que apenas as tags foram removidas. A mensagem de texto é exatamente a mesma. Precisamos definir estes dois formatos HTML e Texto devido a problema de compatibilidade dos clientes email. Não são todos que interpretam emails com conteúdo HTML. Quando um email é enviado, os dois formatos são enviados, mas se o cliente não suporta formato HTML, o arquivo com formato Texto é renderizado.
6. Por último, vamos acrescentar um callback after_create no nosso model Rental para enviar o email assim que o aluguel for criado. Então, nosso model ficará desse jeito (app/models/rental.rb):
1 2 3 4 5 6 7 8 9 10 11 12 |
class Rental < ApplicationRecord belongs_to :user belongs_to :car after_create :send_confirmation private def send_confirmation RentalMailer.with(rental: self).confirmation.deliver_now! end end |
Podemos reparar que no método send_confirmation nós chamamos o mailer como RentalMailer.with enviei um atributo e depois que chamamos o confirmation, que é a action do mailer que queremos. Nós fizemos isso porque, ao utilizar o with, o atributos dentro dele serão enviados dentro de uma variável do RentalMailer chamada params. Ele se diferencia do jeito tradicional porque é um atributo que podemos utilizar em qualquer action do Mailer. Mais pra frente nós faremos uma melhoria e este passo é muito importante.
Utilizando o Mailcatcher
O Mailcatcher é uma ferramenta muito interessante que nos permite ver se nossos emails estão realmente sendo enviados. Ele funciona como um serviço de envio de email, nós vamos configurar o Rails para utilizá-lo, mas em vez de realmente enviar o email, o Mailcatcher captura e exibe o email.
O Mailcatcher utiliza duas portas: a 1080 e a 1025. A porta 1080 é uma porta HTTP para podermos ver no browser o que o Mailcatcher capturou. A 1025 é a porta SMTP, que a utilizaremos na configuração do Rails.
Então, bora mostrar no código. o/
1. O primeiro passo é instalar o Mailcatcher:
1 |
gem install mailcatcher |
Nós não colocamos o Mailcatcher dentro do nosso Gemfile pois é uma recomendação da própria ferramenta para evitar conflitos com o nosso app.
2. Agora vamos configurar o SMTP no Rails. Como o Mailcatcher é uma ferramenta de suporte ao desenvolvimento, vamos adicionar apenas neste ambiente. Então, vamos acrescentar a seguinte linha nas configurações de desenvolvimento (config/environments/development.rb):
1 |
config.action_mailer.smtp_settings = { address: "localhost", port: 1025 } |
Quando iniciarmos o Mailcatcher, usaremos as configurações padrão dele.
3. Agora vamos iniciar o Mailcatcher. Como ele já roda automaticamente em daemon, vamos apenas executar o comando:
1 |
mailcatcher |
Quando instalados a gem mailcatcher, ficou disponível este comando para utilizarmos.
4. Agora, vamos acessar a aplicação com localhost:3000, fazer o login se for necessário e adicionar um novo carro indo no item Cars no menu, depois clicaremos em New Car e adicionaremos um novo carro com as seguintes informações:
- Name: Palio 1.0
- Manufacturer: Fiat
- Year: 2016
- Additional Information: Awesome car with Power Steering, Air Conditioning and much more
5. Feito isso, agora nós vamos abrir o Mailcatcher através do link localhost:1080. Perceba que esta é a porta HTTP que abrimos no arquivo do Docker Compose. Ao acessar este link, nós podemos ver uma interface do Mailcatcher vazia. Assim que criarmos o aluguel, esta interface deve ser preenchida com um novo email.
6. Agora vamos criar o aluguel (Rental). Para fazer isso, vamos no menu superior Rentals e na página seguinte clicar em New Rental. Neste form, vamos preencher com os seguintes dados:
- Start date: 2018/02/10 07:00
- End date: 2018/02/13 18:00
- User: <selecione qualquer um>
- Car: Palio 1.0
7. Ao clicar em Create Rental, devido ao callback after_create, o email será enviado.
No Mailcatcher também aparece o email nos formatos HTML e Texto.
Adicionando callbacks no Mailer
Assim como nos Controllers, também podemos utilizar callbacks como before_action e after_action nos Mailers. Os objetos que queremos enviar nos callbacks devem ser enviados por meio do método with do Mailer, já que é através deste método que os objetos que enviarmos serão colocamos dentro na variável params e ficarão disponíveis para qualquer método que chamarmos.
Para entendermos melhor, vamos recordar um pequeno trecho de código que adicionamos no nosso model Rental (app/models/rental.rb):
1 2 3 |
def send_confirmation RentalMailer.with(rental: self).confirmation.deliver_now! end |
Se tivéssemos feito o nosso RentalMailer para receber o objeto rental como parâmetro do confirmation, não seria possível utilizá-lo nos callbacks, pois ela só ficaria disponível dentro do escopo do método. É por isso que precisamos passar o objeto rental com o with, pra que ele fique disponível em todo escopo do RentalMailer e possamos utilizar nos nossos callbacks.
Agora que entendemos melhor esta parte, vamos aos códigos 🙂
No RentalMailer nós vamos utilizar o callback before_action. Então, o nosso RentalMailer ficará desse jeito (app/mailers/rental_mailers.rb):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class RentalMailer < ApplicationMailer before_action :load_rental def confirmation mail to: @rental.user.email, subject: "Rental Confirmation of #{@rental.car.name}" end private def load_rental @rental = params[:rental] end end |
Para utilizar um callback no Mailer foi tão simples quanto utilizar num Controller. Nós simplesmente criamos um método privado chamado load_rental para atribuir o parâmetro :rental que enviamos através no RentalMailer.with à uma variável de instância @rental. Depois nós simplesmente adicionamos o before_action com o nome do método que queremos executar e pronto, temos um callback em uso. : )
Customizando o email com Foundations
Para customizar o email com o Foundations, nós vamos utilizar duas gems: uma chamada inby-rb e outra chamada preloader-rails. Esta gem inky-rb utiliza uma ferramenta chamada Inky.
Como a utilização de recursos HTML nos emails é bem limitada, precisamos trabalhar com recursos mais complexos utilizando tabelas e o Inky se encarrega de converter algumas tags customizadas próprias que podemos inserir nos arquivos de view do email para tags de tabela. Já veremos como ele funciona codando.
E vamos trabalhar com outra gem também chamada preloader-rails, que será utilizada para capturar os nossos assets CSS e colocar inline na página (dentro da tag style). Precisamos disso porque os emails só leem CSS que estiverem inline.
1. Vamos começar acrescentando essas duas gems em nosso Gemfile:
1 2 |
gem 'inky-rb', require: 'inky' gem 'premailer-rails' |
2. Agora vamos instalar com o bundle:
1 |
bundle install |
3. Agora precisamos instalar as configurações do Inky com o comando:
1 |
rails g inky:install |
4. Vamos ver a mensagem de retorno do Inky:
1 2 |
create app/assets/stylesheets/foundation_emails.scss create app/views/layouts/mailer.html.erb |
Olha que interessante, o Inky já criou um arquivo SCSS que importa o Foundations e um novo layout para os emails. Se abrirmos este arquivo, ele já contem o stylesheet_link_tag para o arquivo foundation_emails.scss. Para evitar que a criação desse novo layout de email seja intrusivo, ele ainda renomeia o layout antigo que tínhamos. Se formos no diretório app/views/layouts haverá um arquivo com o nome no formato “old_mailer_<sequence>.html.erb”.
5. Já que teremos um novo asset sendo utilizado e não estamos importando via Asset Pipeline, nós precisamos adicionar este arquivo à pré-compilação do Rails. Adicionaremos a seguinte linha na configuração de assets (config/initializers/assets.rb):
1 |
Rails.application.config.assets.precompile += %w( foundation_emails.css ) |
6. Vamos também informar ao Asset Pipeline não queremos importar o foundations_emails no no nosso app. Estão, o nosso application.css ficará assim (app/assets/stylesheets/application.css):
1 2 3 4 5 |
/* *= stub foundation_emails *= require_tree . *= require_self */ |
Adicionamos ao application.css um stub no foundation_emails. O stub é uma diretiva do Asset Pipeline para informar que não queremos importar um determinado arquivo. Como temos um require_tree, o foundation_email também será importando se não colocarmos o stub.
7. Como o Inky já se encarrega de fazer todas a configurações iniciais, o nosso trabalho agora é apenas customizar a nossa view do confirmation do RentalMailer. Mas antes disso, precisamos informar ao que queremos utilizar o Inky numa view. Para isso, vamos renomear a view de confirmation.
1 |
mv app/views/rental_mailer/confirmation.html.erb app/views/rental_mailer/confirmation.html.inky |
Poderíamos nos perguntar: Não vamos mais utilizar o ERB? A resposta é: Sim, vamos. Nós ainda podemos utilizar as tags to ERB pois o Inky se encarrega de ver qual é o renderizador que estamos utilizando por padrão. O Inky será utilizado apenas para conversão das customizadas dele para as tag HTML.
8. Agora que já temos o nosso template Inky, podemos customizá-lo. Vamos acrescentar o seguinte conteúdo na nossa view confirmation do RentalMailer (app/views/rental_mailer/confirmation.html.inky):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<spacer size="16"></spacer> <container> <spacer size="24"></spacer> <row> <columns small="12"> <h1>Rental Confirmation</h1> <hr /> <p class="lead">Hi <%= @rental.user.email %>!</p> <p style="color: #484848; line-height: 1.5;"> You've just rented the car <%= @rental.car.name %> - <%= @rental.car.manufacturer %>. Enjoy it! =) </p> <spacer size="16"></spacer> </column> </row> </container> |
Repare as tags spacer, row, column e container. Elas são as tags do Inky que serão convertidas depois. Nas docs do Inky que está no final do post há mais detalhes a respeito.
9. Se subirmos os nossos containers, abrirmos o Mailcatcher e criarmos um novo aluguel (Rental), veremos que o nosso com novo design foi enviado.
Prévisualizando Emails
É um trabalho bem cansativo ter que ficar enviando o email toda vez que fizermos algumas alteração na view para ver como ficou. Mesmo utilizando o Mailcatcher, esse trabalho desgasta com o tempo.
O Rails possui nativamente uma funcionalidade onde podemos pré-visualizar os emails exatamente como se tivesse sido enviados. Vamos seguir alguns passos de código para vermos como podemos fazer.
1- O primeiro passo é darmos atenção a um arquivo que foi criado na pasta test quando executamos o generator do RentalMailer. Esse arquivo é o RentalMailerPreview. Então, vamos dar uma olhada no conteúdo dele (test/mailers/previews/rental_mailer_preview.rb):
1 2 3 4 5 6 7 8 |
class RentalMailerPreview < ActionMailer::Preview # Preview this email at http://localhost:3000/rails/mailers/rental_mailer/confirmation def confirmation RentalMailer.confirmation end end |
Repare que é um arquivo onde temos o método confirmation que está chamando o método confirmation do RentalMailer. Logo acima do método, tem uma dica de como podemos acessar esse preview mas, se tentarmos fazer isso será retornado um erro pra gente. O erro retornado é porque não estamos enviando o objeto de aluguel (Rental) para o mailer. E esse objeto ele usa num before_action para preencher uma variável de instância.
2. Agora precisaremos fazer uma alteração do método confirmation desse preview para carregarmos o objeto do tipo Rental antes e enviar ao RentalMailer. Então, o método ficará desse jeito (test/mailers/previews/rental_mailer_preview.rb):
1 2 3 4 |
def confirmation rental = Rental.first RentalMailer.with(rental: rental).confirmation end |
Nós apenas buscamos o primeiro aluguel que foi cadastrado no nosso app e enviamos para o RentalMailer. Dessa forma, o nosso preview vai renderizar o email do jeito que precisamos.
3. Agora podemos acessar o link que foi sugerido e ver o email. Então, inicie o seu server e digite a URL http://localhost:3000/rails/mailers/rental_mailer/confirmation.
Enviando emails de forma assíncrona
Quando configuramos o envio de email no model Rental, nós utilizamos um envio síncrono. Ou seja, o mesmo processo que faz a requisição do usuário para salvar um aluguel, fica aguardando uma resposta a respeito do envio de email.
Imagine agora que o serviço de email por algum problema qualquer, como fila de envio por exemplo, atrase em 30 segundos o envio. É bem chato para o usuário ficar esperando esse tempo.
Para melhorarmos esta parte, nós vamos fazer o envio de forma assíncrona. É bem simples!
Nós vamos apenas alterar o método de envio de deliver_now para deliver_later. Então o método send_confirmation que utilizamos para enviar o email no model Rental ficará assim (app/models/rental.rb):
1 2 3 |
def send_confirmation RentalMailer.with(rental: self).confirmation.deliver_later end |
Se testarmos o nosso app agora, o email já será enviado através de uma fila. Ele funciona exatamente como Job no Rails quando executamos o perform_later, onde a Job é colocado numa fila e enviado quando puder ser enviado sem prender a sessão do usuário. Por padrão do Rails, essa fila de Jobs e Emails é armazenada na memória.
Consultoria OneBitCode
Evite utilizar a memória para as filas de emails. Opte por soluções como Redis + Sidekiq.
Para mais informações, veja nosso post Dominando o Uso de Jobs
Integração com Ferramentas Externas
Quando lidamos com envio de emails em produção, também temos alternativas de integrar com ferramentas externas. Aqui no post nós vamos ver duas que são bem utilizadas: O Sendgrid e o MailGun.
Como o propósito é mostrar como as integrações são feitas, nós criaremos uma nova branch para cada uma delas e faremos a alteração em desenvolvimento mesmo para que possamos vê-la funcionando. Mas o ideal é que elas sejam aplicadas ao ambiente de Produção.
A. Sendgrid
Vamos seguir então um passo a passo de como enviar emails utilizando o serviço do Sendgrid.
1. Antes de integrar, é necessário criar uma conta no Sendgrid. Para isso, basta acessar a URL https://sendgrid.com
2. Feito o cadastro, poderemos efetuar o login na ferramenta e seremos redirecionados pra um dashboard parecido com este:
3. Agora vamos abrir as nossas configurações de desenvolvimento e substuir as configurações do Mailtcatcher para a do Sendgrid (config/environments/development.rb):
Então, nós vamos remover esta linha:
1 |
config.action_mailer.smtp_settings = { address: "localhost", port: 1025 } |
E adicionar este conteúdo:
1 2 3 4 5 6 7 8 |
config.action_mailer.smtp_settings = { :user_name => '<seu usuario>', :password => '<sua senha>', :address => 'smtp.sendgrid.net', :port => 587, :authentication => :plain, :enable_starttls_auto => true } |
Substitua o <seu usuario> e <sua senha> pelos dados que você utilizou para se cadastrar na ferramenta. Nós também não adicionamos um domain nas configurações, mas é legal que isso seja feito em produção para poder tornar o e-mail mais confiável se ver aberto pelo usuário. Como não colocamos aqui, o Sendgrid pegou por padrão o domínio example.com.
4. Agora podemos reiniciar o Rails server e ao criar um novo aluguel, o email será enviado utilizando o Sendgrid.
5. Após criar um novo aluguel, se formos no item Activity do dashboard Sendgrid, haverá um item informando os status dos emails. A tela de Activity ficará parecido com esta:
O Sendgrid nos informou 3 status bem interessantes para o email do aluguel que foi enviado, que são: quando ele foi processado, quando foi entregue e quando foi lido. É realmente uma gerência que coopera muito para acompanhamentos de envio.
B. MailGun
Integrar com MailGun vai ser bem parecido com o Sendgrid, com apenas uma particularidade na hora de testarmos o envio.
1. Antes, vamos precisar criar uma conta no MailGun. Para isso, vamos acessar https://www.mailgun.com e fazer o cadastro. No cadastro não é necessário adicionar a forma de pagamento, já que vamos apenas testar.
2. Após o cadastro, seremos redirecionamos para uma tela para adicionar o dominio mas podemos pular esta etapa, já que utilizaremos o Sandbox do MailGun para fins de teste.
3. Não podemos esquecer de ativar a conta no link envio por email. Enquanto não ativarmos o MailGun não fará o envio dos emails.
4. Feito isso, seguiremos para o dashboard do MailGun:
5. Antes de falarmos sobre as configurações, o MailGun tem uma particularidade: quando utilizamos o Sandbox dele, nós precisamos convidar os usuários que receberão o email de teste, chamado Recipients. Então, o que precisamos fazer agora é adicionar um novo clicando em Add Recipients no dashboard e na tela seguinte clicar em Invite Recipient. Após convidar um usuário, ele receberá um email para aceitar o convite e a nossa tela de Recipients ficará desse jeito:
6. Como vamos utilizar o Sandbox do MailGun, precisaremos acessar a aba Domains e nessa tela clicar no domínio do Sandbox. Nesta tela, vamos pegar as informações do SMTP Hostname, Default SMTP Login e Default Password.
7. Agora vamos substituir as configurações do Maicatcher pelas configurações do MailGun no nosso ambiente de desenvolvimento (config/environments/development.rb):
Vamos remover a linha:
1 |
config.action_mailer.smtp_settings = { address: "localhost", port: 1025 } |
E adicionar esta:
1 2 3 4 5 6 |
config.action_mailer.smtp_settings = { :port => 587, :address => "<SMTP Hostname>", :user_name => "<Default SMTP Login>", :password => "<Default Password>", } |
8. Agora vamos reiniciar o servidor e tentar cadastrar um novo aluguel (Rental). Não se esqueça de utilizar o mesmo usuário que foi convidade como Recipent.
9. Criado o aluguel, vamos na aba Logs do MailGun que vai ter o log do email que enviamos.
Não perca nenhum conteúdo
Receba nosso resumo semanal com os novos posts, cursos, talks e vagas o/

Não perca nenhum conteúdo
Receba nosso resumo semanal com os novos posts, cursos, talks e vagas o/
Conclusão
Conseguimos ver hoje como o ActionMailer coopera para enviarmos emails sem dor de cabeça através de uma API bem simples e prática. Fazendo um comparativo, a forma de utilizarmos é igual a dos Controles do Rails, onde temos ActionScript, callbacks e até parâmetros.
Integrado com ferramentas externas como Sendgrid e MailGun, temos recursos bem interessantes para gerenciarmos nossas métricas de envio de emails.
Espero que tenham gostado desse post! Pra quem se interessar em ir mais a fundo, recomendo a leitura dos docs do ActionMailer.
O repositório com nosso código está no link abaixo. E lembrando que temos 3 branches importantes com o que fizemos hoje: a send_email com o app utilizando o MailCatcher, a sendgrid utilizando o Sendgrid via SMTP e a mailgun utilizando o MailGun via SMTP.
https://github.com/OneBitCodeBlog/rent_cars
Até a próxima! : )
Primeira vez no OneBitCode? Curtiu esse conteúdo?
O OneBitCode tem muito mais para você!
O OneBitCode traz conteúdos de qualidade, e em português, sobre programação com foco em Ruby on Rails e também JavaScript.
Além disso, aqui sempre levamos à você conteúdos valiosos sobre a carreira de programação, dicas sobre currículos, portfólios, perfil profissional, soft skills, enfim, tudo o que você precisa saber para continuar evoluindo como Programador(a)!
Fique por dentro de todos os conteúdos o/
Nossas redes sociais:
📹 • https://youtube.com/Onebitcode [Live todas as terças-feiras às 19h)
💻 • https://linkedin.com/company/onebitcode
🙂 • https://onebitcode.com/best-online-dating-sites-for-men/
📱 • nerd dating sites canada
🐦 • https://onebitcode.com/search-engine-free-online-dating-sites/
Nossos cursos:
🥇 • casual dating vs serious dating
💎 • Curso Completo de Ruby
⚙ • Minicurso: API Rails 5 Completo
🐞 • Minicurso de Testes para Ruby on Rails com RSpec
Espero que curta nossos conteúdos e sempre que precisar de ajuda, fala com a gente!
Estamos aqui para você 🙂
Bem-vindo à família OneBitCode o/
Olá Daniel, peguei como exemplo, e apliquei em um projeto que tenho aqui e o desempenho adquirido foi maravilhoso, ainda mais utilizando o SendGrid. Vlw pela dica.
\o/
Que maravilha, Diego! o/
Muito obrigado. Apliquei no meu projeto. Está show de bola
Opa, Victor!
Fico feliz que o post tenha de ajudado!
Fantástico esse tutorial pessoal, parabéns!
Sabem me dizer como personalizar a font dos emails utilizando o foundation_emails?
Tentei importar diretamente no foundation_emails.scss da seguinte forma:
@import url(‘https://fonts.googleapis.com/css?family=Open+Sans’);
@import “foundation-emails”;
$body-font-family: ‘Open Sans’, sans-serif !important;
Infelizmente não deu certo…