Como enviar correos con Rails

Uno de las mejores cosas que tiene Ruby on Rails, es su soporte para enviar correos, el manejo muy simple de plantillas y la forma de conectarlo con el servidor.

En mi caso yo no soy SysAdmin, soy desarrollador o al menos lo intento, y en mi caso… Logre configurar en mi VPS, un servidor con Apache, Mysql, Mongrel, Rails, Ruby, Rubygems, etc etc. Pero cuando quise configurar un servidor smtp para solo enviar correos, me tope con una muy poco documentada y engorrosa forma de echar a andar un servidor smtp.

¿Como envío correos sin tener que instalar un smtp server en el vps?

Leyendo y leyendo, encontré una excelente forma de hacerlo ocupando el smtp de google y como la mayoría usamos google apps for your domain es bastante simple.

Vamos por el código!

Copia esto en tu carpeta lib de tu aplicación con el nombre de smtp_tls.rb


require 'openssl'
require 'net/smtp'

Net::SMTP.class_eval do
private
def do_start(helodomain, user, secret, authtype)
raise IOError, 'SMTP session already started' if @started
check_auth_args user, secret, authtype if user or secret

sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
@socket = Net::InternetMessageIO.new(sock)
@socket.read_timeout = 60 #@read_timeout

check_response(critical { recv_response() })
do_helo(helodomain)

raise 'openssl library not installed' unless defined?(OpenSSL)
starttls
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
ssl.connect
@socket = Net::InternetMessageIO.new(ssl)
@socket.read_timeout = 60 #@read_timeout
do_helo(helodomain)

authenticate user, secret, authtype if user
@started = true
ensure
unless @started
# authentication failed, cancel connection.
@socket.close if not @started and @socket and not @socket.closed?
@socket = nil
end
end

def do_helo(helodomain)
begin
if @esmtp
ehlo helodomain
else
helo helodomain
end
rescue Net::ProtocolError
if @esmtp
@esmtp = false
@error_occured = false
retry
end
raise
end
end

def starttls
getok('STARTTLS')
end

def quit
begin
getok('QUIT')
rescue EOFError
end
end
end

Luego en tu config/enviromment.rb

Copia en la cabecera:

require 'lib/smtp_tls'

y en la parte baja


# Include your application configuration below

ActionMailer::Base.smtp_settings = {
:address => "smtp.gmail.com",
:port => "587",
:domain => "tudominio.com",
:authentication => :plain,
:user_name => "tusuarioengafyd",
:password => "ñamñamtuclave"
}

Ahora tienes toda la configuración necesaria para enviar correos!

Pero falta algo mas, implementarlo y echar a correr la magina de ActionMailer.
(En este punto todo es mas fácil y existe mucha documentación al respecto, de hecho la totalidad de los libros sobre Rails refieren capítulos enteros a este punto)

En models/user_mailer.rb


class UserMailer < ActionMailer::Base
def sent()
@subject = "El titulo de tu correo"
@recipients = 'correo@de.destino'
@from = 'correo@de.origen'
@sent_on = Time.now
@body["user"] = "Pasale alguna variable para personalizar tu correo"
end
end

Solo 2 pasos mas.

En tus vistas

views/user_mailer/sent.rhtml -> El mensaje y podras pasar la variable(Ups!! tu objeto! Recuerda: En ruby absolutamente todo es un objeto) enviada en el body


Bienvenidos
Tus datos son:
user: <%= @user.login %>
Nota: Si tu cuenta no es activada en 24 horas sera borrado el registro.

(En el punto anterior puedes usar etiquetas html para enriquecer el correo.

y finalmente para hacer uso de el.

En cualquiera de tus controladores/metodo o donde quieras hacerlo (Just try to Be DRY).


#Envio el correo
email = UserMailer.create_sent() #Aqui puedes pasarle objetos.
email.set_content_type("text/html")
UserMailer.deliver(email)

Y listo, Ya estas enviando correos con tu flamante aplicación rails.

He tenido algunos problemas con el soporte UTF-8 si alguien tuviese experiencia con lo anterior, comentelo!

Guardado en: aprender, rubyonrails |

18 Comments

  • Thursday, 31 May 2007, 13:42
    By gameXs

    Vaya, están bien detallados los pasos justo lo que andaba buscando. Voy a probar y aviso como me fue. Muchas gracias por darte el tiempo.

  • Thursday, 31 May 2007, 14:06
    By gameXs

    Al arrancar el servidor me da el siguiente error:
    /ruby/lib/ruby/gems/1.8/gems/actionmailer-1.2.5/lib/action_mailer/base.rb:293:in `method_missing’: undefined method `smtp_settings=’ for ActionMailer::Base:Class (NoMethodError)

    Creo que debe ser por algún tipo de actualización, ojalá me puedas echar una mano.

  • Thursday, 31 May 2007, 17:17

    Al parecer la forma de llamar al metodo no es la correcta. Que versión de Rails estas usando?, desde la versión 1.2 smtp_settings remplazo a server_settings.

    2 opciones.

    1. Actualiza Rails a la verson 1.2.x
    2. Cambiar smtp_settings por server_settings.

    Espero sea eso, si no lo es. Cuentanos como te fue.

  • Thursday, 31 May 2007, 18:39

    Excelente información Nicolás! A probarlo inmediatamente!

  • Thursday, 31 May 2007, 19:18
    By gameXs

    Así es, reemplazé smtp_settings por server_settings y funcionó :). Pero tengo la versión 1.1.6 por si acaso. Muchas gracias por la ayuda.

  • Thursday, 31 May 2007, 19:31

    De nada gameXs, te aconsejo actualizar Rails, pronto… sin fecha aun se viene la version 2.0 y cosas como esas seran descontinuadas definitivamente.

    Por otro lado, te cuento que estamos re-armando la Comunidad Ruby on Rails Chile, te esperamos por alla.

  • Thursday, 31 May 2007, 19:55
    By gameXs

    Bien, todo bien. El único problema es que se demora un poco en el envío. Habrá alguna forma de “esconder” ese tiempo de envío para que no se perciba ese tiempo de espera y que el sistema continúe otras cosas que debe hacer???.

  • Thursday, 31 May 2007, 20:27

    Depende yo creo también de la velocidad de tu servidor, pero al igual que tu he experimentado un lapsus de espera. La mejor forma de hacerlo creo podria ser esconderlo tras un gif de espera con AJAX.

    Tambien he escuchado sobre multi-threading en Rails, pero nunca lo he probado. gameXs, si encuentras alguna solución(AJAX para espera u otra) please me cuentas.

    Saludos.

  • Sunday, 3 June 2007, 4:07
    By gameXs

    Intenté enviar el mail mediante un hilo de la siguiente forma:

    tengo dos funciones, la primera se encarga de ingresar algunos datos en la bd, luego de ingresarlos se envía un mail con esos datos. Ahí viene la segunda función. El código sería algo así.
    def funcion_ingresa_a_bd
    .
    .
    .
    if @objeto.save
    hilo=Thread.new(@objeto){|objeto|
    enviar_mail(objeto)
    }
    hilo.join
    redirect_to(:action=>’otra_parte’,:id=>@objeto.id)
    end
    end
    def enviar_mail(objeto)
    email = Mail.create_sent(”Asunto”,objeto)
    email.set_content_type(”text/html”)
    Mail.deliver(email)
    end

    Me envía bien el mail, el problema es que se sigue tardando ya que al agregar el método join se bloquea el hilo “llamador” hasta que el hilo creado termine.

    Y sin ese método se ejecuta todo bien, pero al parecer como que se bloquea el hilo creado ya que el mail no se envía.

    Así que por mientras sólo pongo un aviso, con Ajax, para que el usuario sepa que la demora es por un mail que se está enviando, cosa que no quiero.

    A ver si alguien sabe como hacer que se ejecute ese hilo sin importar lo que haga el hilo que lo llamó.

  • Wednesday, 13 June 2007, 11:54

    Sorry gameX tu comentario fue injustamente agarrado por Askimet y lo tiro a Spam.

    Dejame revisarlo y te cuento, saludos.

  • Sunday, 22 July 2007, 6:12

    […] Desde el 19 de Julio google hizo algunas modificaciones en como manejan las entradas SSL (Pero aun no logro entender que fue lo que realmente cambiaron) lo que produce que Enviar correos desde apps Rails con Google ahora nos lance un error. […]

  • Sunday, 22 July 2007, 12:09
    By gastu

    Y esto con que sentido si gmail tiene una excelente interfaz.

  • Sunday, 22 July 2007, 13:34

    Gastu, para enviar correos a traves de tu aplicación Rails. por ej: Un correo de verificación de cuenta.

  • Sunday, 26 August 2007, 11:38
    By dannyrafa

    tengo una inquietud, si yo deseo enviar el email no siem pre a al mismo correo sino que en vista yo escriba la direccion de email a la cual deseo que le llegue el email como lo haria, porque veo que en tu codigo, es decir en el modelo en la variable @recipients le asignas de una vez al correo donde deseas que le llegue el email, no sabes de alguna forma para indicarle que no siempre sea ese email al cual deben llegar los correos, sino hacerlo confugurable, es decir que yo le diga a cual correo deseo que llegue ese email

  • Sunday, 26 August 2007, 14:48

    Se lo pasas como parámetro al método, en este caso sent … sent(correodestino) y luego lo utilizas en el método de ActiveMailer.

  • Tuesday, 28 August 2007, 11:09
    By Iesus

    Hola, creo que el código que has puesto no está del todo bien copiado porque sólo me ha funcionado cuando lo he copiado de la página original (http://66.102.9.104/search?q=cache:IA2sy01KDhwJ:blog.pomozov.info/posts/how-to-send-actionmailer-mails-to-gmailcom.html+http://blog.pomozov.info/posts/how-to-send-actionmailer-mails-to-gmailcom.html&hl=es&ct=clnk&cd=1&gl=es&client=firefox-a) .
    Un saludin

  • Tuesday, 28 August 2007, 12:05

    Hola el smpt_tls es copy_paste de el código de los amigos, lo hubiese referenciado si la pagina de ellos en su momento hubiese estado en linea, pero no resolvía.. recuerdo que lo saque del cache de google. Pero gracias por avisarme, quizás algo hay cambiado.

    Un abrazo

  • Tuesday, 15 July 2008, 12:38
    By Juan

    hice lo que tienes en tu post y me da el siguiete error si me puedes ayudar pues necesito hacer una aplicacion en rails que mande correo, el error es el siguiente
    /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:278:in `load_missing_constant’: uninitialized constant ActionMailer (NameError)
    from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:467:in `const_missing’
    from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:479:in `const_missing’
    from /home/jpmg/ruby/corre/config/environment.rb:70
    from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/initializer.rb:91:in `run’
    from /home/jpmg/ruby/corre/config/environment.rb:13
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require’
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require’
    from /usr/lib/ruby/gems/1.8/gems/activesupport-2.1.0/lib/active_support/dependencies.rb:509:in `require’
    … 8 levels…
    from /usr/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/commands/server.rb:39
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require’
    from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:27:in `require’
    from script/server:3

Leave a comment

norellan