16: Action Mailer - LPC-Ltda/Ruby-on-Rails GitHub Wiki
Es genial enviar emails sin una tonelada de código. Jake Scruggs
La integración con emails en una parte crucial de los proyectos de aplicacions web más modernos.Ya sea si se trata de confirmación de ingreso, recuperación de password, o control de cuentas vía email, usted estará feliz de escuchar que Rails ofrece buen soporte tanto para el envío como para la recepción de emails, gracias al marco de trabajo Action Mailer.
En este capítulo, cubriremos todo lo necesario para setear su despliegue para estar habilitado para enviar y recibir mails con el marco de trabajo Action Mailer y lo que necesita para escribir modelos mailer, los cualesson la entidad en Rails que encapsula el código que tiene que ver con el manejo de emails.
##16.1 Setup
Por defecto, Rails intentará emails vía SMTP (puerto 25) del localhost. Si usted está corriendo Rails sobre un host que tiene un demonio SMTP corriendo y este acepta email SMTP localmente, usted no tiene que hacer nada más para enviar un mail. Si usted no tiene SMTP disponible sobre localhost, usted tiene que decidir cómo su sistema enviará un email.
Cuando no se usa SMTP directamente, las opciones principales son usar sendmail o darle a Rails la información ara conectarse a un servidor de mail externo. La mayoría de las organizaciones tienen servidores SMTP disponibles para este tipo de uso, aunque es bueno hace notar que, gracias al abuso, muchos proveedores de hosting han dejado de ofrecer servicio SMTP compartido.
Los despliegues de producción más serios usan servicios SMTP de terceros que se especializan en el envío automático de emails, evitando los filtros de spam de los usuarios y las listas negras.
##16.2 Modelos Mailer
Asumiendo que el sistema de mails está configurado, siga adelante y cree un modelo mailer que contenga el código que corresponde al anvío y cerecpción de una clase se email. Rails provee un generador que nos permite partir rápidamente. Nuestro mailer enviará una noticia a cualquier usuario de nuestra aplicación ejemplo que ha ingresado tarde su tiempo.
rails generate mailer LateNotice
create app/mailers/late_notice.rb
invoke haml
create app/views/late_notice
invoke rspec
create spec/mailers/late_notice_spec.rb
Una carpeta para vistas es creada en app/views/late_notice
, y el mailer mismo es puesto en app/mailers/late_notice.rb
:
class LateNotice < ActionMailer::Base
default from: "[email protected]"
end
Tal como con una subclase Active Record, no hay mucho aquá al comienzo.
###16.2.1 Preparando mensajes de email salientes
Usted trabaja con clases Action Mailer para definir métodos de mailer públicos que corresponden a los tipos de emails que usted desea envíar. Dentro del método público, usted asigna todas las variables que pueda necesitar el template del mensaje email y entonces llama al método mail, el cual es conceptualmente similar al método render usado en los controladores.
Continuando con nuestro ejemplo, escribamos un método mailer late_timesheet
que toma los parámetros user
y week_of
. Note que setea la informaciń básica necesaria para enviar nuestro email de noticia (Listado 16.1).
Listado 16.1: Agregando un método mailer
class LateNotice < ActionMailer::Base
default from: <"[email protected]"
def late_timesheet(user, week_of)
@recipient = user.name
@week = week_of
attachement["image.png"] = File.read("/images/image.png")
mail(
to: user.email
subject: "[Time and Expenses] Timesheet notice"
)
end
end
Dentro del método que hemos creado, tenemos acceso a algunos métodos para setear el mensaje para se envío, incluyendo el método mail
mostrado antes:
attachments Le permite agregar archivos adjuntos normales e inline a su mensaje.
attachements["myfile.zip"] = File.read("/myfile.zip")
attachements.inline["logo.png"] = File.read("/logo.png")
headers Le permite proporcionar un hash de encabezados de mail persomalizados.
headers("X-Author" => "Obie Fernandez")
mail Setea el email que será enviado. Acepta un hash de encabezados que un Mail::Message
aceptará y permite un block opcional. Si no hay block especificado, la vista será usada para construir el email con el mismo nombre que tiene el método en el mailer. Si un block es especificado esto puede ser personalizado.
Note también el cambio de la dirección from por defecto por una especificada por nuestra aplicación. Aquí está una lista ejemplo de los encabezados que usted puede incluir en el hash pasado al método mmail o en la macro default. Adicionalmente a estos, usted puede pasar cualquier encabezado de email que necesite al enviar, esto es, { "X-Spam" => value }
.
subject La línea 'asunto' para el mensaje.
to La dirección que recibe el mensaje, ya sea un string (para una dirección única) o un arreglo (para direcciones múltiples). Recuerde que este método espera actualmente strings de direcciones, no sus objetos de usuarios.
users.map(&:email)
from Especifica la dirección from para el mensaje como un string (requerida).
cc Especifica las direcciones de copia de carbon (cc:) para el mensaje, ya sea como un string (para dirección única) o un arreglo de múltiples direcciones.
bcc Especifica la dirección del recibidor ciego (bcc:) para el mensaje, ya sea un string (para dirección única) o un arreglo de múltiples direcciones.
reply_to Setea el email para el encabezado reply-to.
date Una fecha de envío opcional para el mensaje, usalmente se pasa Time.now
. Será seteado automáticamente por el mecanismo de envío si usted no proporciona un valor y no puede ser setado usando una macro por defecto.
El método mail
puede tomar un block o no si ustede desea personalizar formalizar los formatos en forma similar a las rutas de Rails.
mail (to: "[email protected]") do |format|
format.text
format.html
end
El cuerpo del email es creado usando un template Action View (Haml o Erb) que tiene las variables de instancia en mailer disponibles como variables de instancia en el template. Así el cuerpo del template correspondiente al método mailer del Listado 16.1 puede ser como el siguiente:
Dear #{@recipient},
Your timesheet for the week of #{@week} is late.
Y si quie recibe es Aslak, el email generado lucirá así:
Date: Sun, 12 Dec 2004 00:00:00 +0100
From: [email protected]
To: aslak@[email protected]
Subject: [Time and Expense] Late timesheet notice
Dear Aslak Hellesoy,
Your timesheet for the week of Aug 15th is late.
###16.2.2 Mensajes de email HTML
Para enviar mails como HTML, asegurese que su template de vistas genere HTML, y que el nombre del template correspondiente corresponda al nombre del método del email, para nuestro método late_timesheet
, éste deberá estar en app/views/late_notice/late_timesheet.html.haml
(o .erb
). Usted puede incluso sobrescribir este nombre del template en el block de mail
.
mail(to: "[email protected]") do |format|
format.text
format.html { render "another_template" }
end
###16.2.3 Mensajes multipartes
Si un texto plano y un template HTML están presentes para una acción mailer específica, el template text y el template HTML serán ambos enviados por defecto como un mensaje multiparte. La parte HTML será marcada como contenido alternativo para aquellos clientes de mails que lo soporten.
16.2.3.1 Mensajes multipart implícitos
Como mensionamos antes en este capítulo, los mensajes multiparte pueden también ser usado implícitamente sin invocar el método part
, ya que Action Mailer puede automáticamente detectar y usar templates multipartes, donde cada template es nombrado despues del nombre de la acción por el tipo de contenido. Cada template detectado puede ser agregado como una parte separada del mensaje.
Por ejemplo, si los siguientes templates existeran, cada uno podría ser rendereado y agregado como una parte separada del mensaje con el correspondiente tipo de contenido. El mismo hash de cuerpo es pasado a cada template.
signup_notification.text.haml
signup_notification.text.html.haml
signup_notification.text.xml.builder
signup_notification.text.yaml.erb
###16.2.4 Attachments
Incluir adjuntos en emails es relativemente simple; sólo use el método en su clase.
class LateNotice < ActionMailer::Base
def late_timesheet(user, week_of)
@recipient = user.name
attachments["image.png"] = File.read("/images/image.png")
mail(
to: user.email,
from: "[email protected]",
subject: "[Time and Expense] Timesheet notice"
)
end
end
Si ustede desea atachar una imagen inline, use attachment.inline.
attachment.inline["image.png"] = File.read("/images/image.png")
Usted puede accesar este attachment en el template si necesita vía el hash attachments
y luego llamar la url
sobre ese objeto para la ruta de id de contenido (cid:) relacionado con la imagen.
Dear #{@recipient},
Your timesheet is late. Here's a photo depicting our sadness:
= image_tag attachments['image.png'].url, alt: "Invoicing"
###16.2.5 Generando URLs
La generación de URLs de la aplicación es manejado a través de rutas nombradas o usando el helper url_for
. Ya que los mailers no tienen contexto para los requerimientos como lo tienen los controldores, la opción de configuración del host necesita ser seteada. a mejor práctica para esto es definirlas en la de emabiente correspondiente, aunque puede ser definida en cada mailer.
# config/environments/production.rb
config.action_mailer.default_url_options = { host: 'accounting.com' }
En su mailer, usted puede ahora generar su URL. Es importante hace notar que usted no puede usar la variante _path
para sus rutas nombradas ya que ellas deben ser rendereadas como URLs absolutas.
class LateNotice < ActionMailer::Base
def late_timesheet(user, week_to)
@recipient = user.name
@link = user_url(user)
mail(
to: user.email,
from: "[email protected]",
subject: "[Time and Expenses] Timesheet notice"
)
end
end
Cuando generamos URLs a través de url_for
, el controlador y la acción necesitan ser especificadas. Si usted ha provisto un host por defecto, entonces la opción :only_path
debe ser entregada para contarle al helper que genere una ruta absoluta.
= url_for(controller: "users", action: "update", only_path: false)
###16.2.6 Layout de mailers
Los layouts de mailers se comportan tal como lo hacen los layouts de controladores. Para ser automáticamente reconocidos, ellos necesitan tener el mismo nombre que el mailer mismo. En nuestro caso previo, puede ser automáticamente usado para nuestros emails HTML. Usted puede también agregar layouts personalizados si lo desea, ya sea a nivel de clase, o como una opción de rendereo.
class LateNotice < ActionMailer::Base
layout "alternative"
def late_timesheet(user, week_to)
mail(to: user.email) do |format|
format.html { render layout: "another" }
end
end
end
Hemos hablado mucho de cómo preparar emails para ser enviados a los destinatarios, pero como se envían a los destinatarios?
###16.2.7 Enviando un email
Enviar un email sólo requiere obetener un objeto de su mailer y enviarlo.
aslak = User.find_by(name: "Aslak Hellesoy")
message = LateNotice.late_timesheet(aslak, 1.week.ago)
message.deliver
###16.2.8 Callbacks
Para Rails 4, la habilidad de definir callbacks de acciones para un mailer fue agregada. Com osu contraparte Action Controller, uno puede especificar callbacks before_action
, after_action
, y around_action
para ejecutar compartiendo pre y post proceso dentro de un mailer.
Los callbacks pueden aceptar uno o más símbolos, representando un método conincidente en la clase mailer:
before_action :set_headers
O usted puede pasar al callback un bloque a ejecutar, como este:
before_action { logger.info "sending out an email!" }
Un ejemplo común del uso de un callback en un mailer es para setear un attachment inline, como por ejemplo imágenes que se usan dentro del template del email.
class LateNotice < ActionMailer::Base
before_action :set_inline_attachments
def late_timesheet(user, week_of)
@recipient = user.name
mail(
to: user.email
from: "[email protected]"
subject: "[Time and Expense] Tmesheet notice"
)
end
protected
def set_inline_attachments
attachments["logo.png"] = File.read("/images/logo.png")
end
end
Los callbacks de acciones son cubiertos en detalle en el Capitulo 4 "Trabajando con Controladores", en la sección "Callbacks de acciones".
##16.3 Recibiendo emails