13: Tarea H, Enviando un correo - LPC-Ltda/Ruby-on-Rails GitHub Wiki

En este punto, tenemos un website que responde a requerimientosy proveerá feeds permitirán la venta de títulos individuales a ser chequeados periódicamente. Hace sentido tener algó más que eso. Lo que necesitamos es la habilidad de dirigir un mensaje a alguien específico cuando un evento ocurre. Esto puede ser enviar una notificación al administrador del sistema cuando ocurre una excepción, puede ser un formulario de feedback del usuario. En este capítulo simplemente optaremos por enviar un mail de confirmación a las personas que nos ponen órdenes. Una vez completado esto, crearemos pruebas no śolo para el soporte de mail que hemos agregado sino para el escenario de usuario completo que hemos ido creando.

13.1 Iteración H1: Enviando mails de confirmación

Hay tres partes básicas para enviar un mail en Rails: configurar cómo el mail será enviado, determinar cuando enviar el mail, y especificar que desea usted decir.

Configuración de Email

La configuración de Email es una parte del ambiente de aplicación de Rails y envuelve un block Depot::Application.configure. Si usted desea usar la misma configuración para development, testing y production, agregue la configuración a environment.rb en el directorio config ; de otra forma, agregue diferentes configuraciones a los archivos apropiados en el directorio config/environments.

Dentro del block, necesitará tener una o más instrucciones. Usted debe primero decidir como desea que su mail sea enviado.

config.action_mailer.delivery_method = :smtp

Alternativas a :smtp son :sendmail y :test.

Las opciones :smtp y :sendmail son usadas cuando usted queire que Action Mailer intente enviar un email. Usted claramete usará uno de esos métodos en producción.

El seteo :test es bueno para las pruebas de unidades (unit) y funcionales, de las cuales haremos uso en la sección Probando un Email. En esta opción el email no será enviado; a cambio, será agregado a un arraglo (Accesible vía el atributo ActionMailer::Base.deliveries). Este es el método de envío por defecto en el ambiente test, el método por defecto en el ambiente develpment es :smtp. Si usted desea que Rails envíe un email durante el desarrollo de su aplicación, esta opción es bueno. Si usted prefiere desabilitar el envío de emails en el modo development, edite el archivo development.rb en el directorio config/environments, y agregue las siguientes líneas:

Depot::Application.configure do
  config.action_mailer.delivery_method = :test
end

El seteo :sendmail delega el envío de emails en el programa sendmail de su sistema local, el cual se asume está en /usr/sbin. Este mecanismo de envío no es particularmente portable, ya que sendmail no siempre está instalado en ese mismo directorio en diferentes sistemas operativos. También se basa en su sendmail el soporte de las opciones de comandos -i y -t.

Usted logra mas portabilidad al dejar esta opción en su valor por defecto :smtp. Si usted hace esto, necesitará especificar algo de configuración adicional para contarle a Action Mailer donde encontrar un servidor SMTP para manejar el email saliente. Esta puede ser la máquina que corre su aplicación web, o puede ser una caja (box) separada (quizá en su ISP si usted está corriendo Rails en un ambiente no coorporativo). Su administrador de sistema estará habilitado para darle el seteo para estos parámetros. Usted puede también estar habilitado para determinarlos desde su propia configuración de cliente de mail.

El siguiente es el seteo típico de Gmail, Adaptados a sus necesidades.

Depot::Application.configure do
  config.action_mailer.delivery_method = :smtp

  config.action_mailer.mailer.smtp_settings = {
    address: "smtp.gmail.com",
    port: "587",
    domain: "domain.of.sender.net",
    authentication: "plain",
    user_name: "dave",
    password: "secret",
    enable_starttls_auto: true
  }
end

Como con todos los cambios de configuración, usted necesita reiniciar su aplicación si usted hace cambios en cualquier archivo de ambiente.

Enviando un Email

Ahora que está todo configurado, escribiremos alg de código para enviar emails.

Usted no se sorprenderá de saber que Rails tiene un script generador para crear mailers. En Rails, un mailer es una clase que está almacenada en el directorio app/mailer. Este contiene uno o más métodos, cada método corresponde a un template de email. Para crear el cuerpo de un email, estos métodos en uso usan vistas (en la misma forma en que las acciones del controlador usan vistas para crear HTML y XML). Así que, creemos un email para nuestra aplicación de venta. Las usaremos para enviar dos tipos diferentes de email: uno cuando una orden es puesta y segundo cuando una orden se envía. El comando rails generate mailer toma el nombre de la clase de mailer y los nombres de los métodos de acción.

depot> rails generate mailer OrderNotifier received shipped
  create app/mailers/order_notifier.rb
  invoke erb
  create app/views/order_notifier
  create app/views/order_notifier/received.text.erb
  create app/views/order_notifier/shipped.text.erb
  invoke test_unit
  create test/mailers/order_notifier_test.rb

Note que usted ha creado una clase OrderNotifier en app/mailers y dos archivos templates, uno para cada tipo de email, en app/views/order_notifier. (tambien creamos un archivo de prueba).

Cada método en la clase mailer es responsable de setear el ambiente para enviar un mail particular. Veamos un ejemplo antes de ir a los detalles. Aquí está el código generado para nuestra clase OrderNotifier, con un default cambiado:

[rails40/depot_q/app/mailers/order_notifier.rb]

class OrderNotifier < ActionMailer::Base
> default from: 'Sam Ruby <[email protected]>'
  # El subject puede ser seteado en su archivo I18n en config/locales/en.yml
  # de la siguiente forma:
  #
  #  en.order_notifier.received.subject
  #
  def received
    @greeting = "Hi"

    mail to: "[email protected]"
  end

  # El subject puede ser seteado en su archivo I18n en config/locales/en.yml
  # de la siguiente forma:
  #
  #  en.order_notifier.shipped.subject
  #
  def shipped
    @greeting = "Hi"

    mail to: "[email protected]"
  end
end

Si usted está pensando que esto se parece a un controlador está en lo correcto. Hay un método por acción. En lugar de un llamado a render(), hay un llamado a mail(). Mail acepta un número de parámetros incluyendo :to (como se ve en el ejemplo), :cc, :from, y :subject, cada uno de los cuales hace lo que parecen hacer. Los valores que son comúnes a todos las llamadas de mail en el mailer pueden ser seteados simplemente llamando a default, como fue hecho en :from en la cima de esta clase.

Los comentarios en esta clase indican que las líneas del subject están ya habilitadas para traducción, un subject será cubierto en el capítulo 15, Tarea J: Internacionalización. Por ahora, usaremos el parámetro :subject solamente.

Como con los controladores, los templates contienen el texto que se enviará, y los controladores y meilers pueden proveer valores para ser insertados en estos templates vía instancias de variables.

Templates de email

El script generate creo dos templates de email en app/views/order_notifier, uno para cada acción en la clase OrderNotifier. Estos son archivos .erb regulares. Los usaremos para crear emails de texto plano (veremos luego como crear emails HTML). Como con los templates para crear páginas, los archivos contienen una combinación de texto estático y contenido dinámico. Usted puede personalizar el template en received.text.erb; este es el email que será enviado para confirmar una orden:

[rails40/depot_q/app/views/order_notifier/received.text.erb]

Querido <%= @order.name %>

Gracias por su orden reciente a Pragmatic Store

Usted ordenó los siguientes ítems:

<%= render @order.line_items -%>

Le enviaremos un email separado cuando su orden sea enviada.

El template partial que renderea una línea de ítem formatea una única línea con la cantidad y el título. Ya que estamos dentro de un template, todo los métodos helpers regulares, como truncate(), están disponibles.

[rails40/depot_q/app/views/line_items/_line_item.text.erb]

<%= sprintf("%2d x %s", line_item.quantity, truncate(line_item.product.title, lenght: 50)) %>

Ahora volveremos atrás y llenaremos el método received() en la clase OrdenNotifier.

[rails40/depot_r/app/mailers/order_notifier.rb]

def received(order)
  @order = order

  mail to: order.email, subject: 'Pragmatic Store Order Confirmation'
end

Lo que hicimos aqui es agregar order como un argumento a la llamada del método received, agregar código para copiar la variable pasada en una variable de instancia, y actualizar la llamada a mail() especificando donde enviar el email y la línea de subject que se utilizará.

Generando emails

Ahora que tenemos nuestro template seteado y nuestro método definido, podemos usarlos en nuestro controlador regular para crear y/o enviar emails.

[rails40/depot_r/app/controllers/order_controller.rb]

def create
  @order = Order.new(order_params)
  @order.add_line_items_from_cart(@cart)

  respond_to do |format|
    if @order.save
      Cart.destroy(session[:cart_id])
      session[:cart_id] = nil
>     OrderNotifier.received(@order).deliver
      format.html { redirect_to store_url, notice: 'Gracias por su orden.' }
      format.json { render action: 'show', status: :created, location: @order }
    else
      format.html { render action: 'new' }
      format.json { render json: @order.errors, status: :unprocessable_entity }
    end
  end
end

Y necesitamos actualizar shipped() tal como lo hicimos con received().

[rails40/depot_r/app/mailers/order_notifier.rb]

def shipped(order)
  @order = order

  mail to: order.email, subject: 'Pragmatic Store Order Shipped'
end

En este punto, tenemos suficiente para que usted ponga una orden y se envíe un correo a si mismo, presumiendo que usted no ha deshabilitado el envío de emails en modo development. Ahora agreguemos un poco de formateo al email.

Enviando tipos de múltiples contenidos

Alguna gente prefiere recibir email en formato de texto plano, mientras otros prefieren emails en HTML. Rails hace fácil enviar emails que contienen formatos de contenido alternativos, permitiendo al usuario (o a sus clientes de email) decidir que prefieren ver.

En la sección anterior creamos un email de texto plano. La vista para nuestra acción recived fue llamada received.text.erb. Esta es la convención de nombres estándar de Rails. También podemos crear emails formateados en HTML.

Probemos con la notificación de orden enviada. No necesitamos modificar el código; simplemente necesitamos crear un nuevo template.

[rails40/depot_r/app/views/order_notifier/shipped.html.erb]

<h3>Pragmatic Order Shipped</h3>
<p>
  Este correo es sólo para avisarle que su orden ha sido enviada
</p>

<table>
  <tr><th colspan="2">Qty</th><th>Description</th></tr>
  <%= render @order.line_items -%>
</table>

No necesitamos modificar el partial porque ya tenemos uno que lo hace bien.

[rails40/depot_r/app/views/line_items/_line_item.html.erb]

<% if line_item == @current_item &>
<tr id="current_item">
<% else %>
<tr>
<% end %>
  <td><%= line_item.quantity %>&times;</td>
  <td><%= line_items.product.title %></td>
  <td class="intem_price"><%= number_to_currency(line_item.total_price) %></td>
</tr>

Pero, para templates de email hay un poco de magia en los nombres. Si usted crea multiples templates con el mismo nombre pero con diferentes tipos de contenidos enbuidos en sus nombres, Rails los enviará todos en un mail arregándoselas el contenido para que el cliente de email está habilitado para distinguir cual.

Esto siginifica que usted tendrá que actualizar o borrar el template de texto plano que Rails provee para el notificador shipped.

Probando emails

⚠️ **GitHub.com Fallback** ⚠️