Friday, March 15, 2013

Action Mailer: Sendgrid


En una aplicación Rails podemos configurar diferentes cuentas SMTP para el envío de emails.
Dentro de config/environments podemos especificar digamos la cuenta que vamos a usar para el envío general de emails. En el environment correspondiente: development.rb, production.rb, especificaríamos dicha cuenta:

# Disable delivery errors, bad email addresses will be ignored
  # Don't care if the mailer can't send
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.smtp_settings = {
    :enable_starttls_auto => true,
    :address => "smtp.gmail.com",
    :port => 587,
    :domain => "",
    :authentication => "plain",
    :user_name => "", #should be you@domain.com
    :password => ""
  }


Si no se especifica lo contrario esta cuenta será la que se utilice para las diferentes clases que extiendan de  ActionMailer::Base.

Imaginemos que ahora tenemos la clase EnvioMasivoMailer que se usará para el envío masivo de emails a diferentes usuarios. Algo tal como esto:
class EnvioMasivoMailer < ActionMailer::Base
   def email_for_users(opts = {})
   end
end

En este caso podríamos considerar oportuno que por sus características deberíamos usar sendgrid. Sendgrid permite enviar hasta medio millón de emails por mes con la garantía de que no acabarán en la caja de spam.
Con estadísticas, informes de spam, filtros de cuentas inexistentes, opciones de importación y muchas más funciones, es uno de los grandes en el mundo del email marketing.
¿Como configuraríamos para la clase que hemos definido este servidor de correo?











En primer lugar nos podríamos definir un archivo mailer_sendgrid.yml dentro de la carpeta config con la configuración:
send_grid_conf:
  address:        "smtp.sendgrid.net"
  port:           587
  authentication: !ruby/sym plain
  user_name:      ""
  password:       "" 


Y ya dentro de nuestra clase haríamos lo siguiente, sobreescribir el smtp_settings del action mailer para usar sendgrid:
class EnvioMasivoMailer < ActionMailer::Base
   def email_for_users(opts = {})
      headers_email["X-SMTPAPI"] = {:to => opts[:array_of_recipients]}.to_json 
      mail(headers_email) do |format|
              format.html
      end


   end
   
   cattr_accessor :send_grid_config

   self.send_grid_config = YAML::load(File.open("#{Rails.root}/config/mailer_sendgrid.yml"))

   # Esta configuracion es para produccion, se sobreescribe el smtp_settings del environment correspondiente para meter la configuración de send_grid
  
   def self.smtp_settings
      send_grid_config['send_grid_conf'].symbolize_keys
   end


end
De esta manera sólo esta clase usaría sendgrid como método de envío de emails, el resto usaría la que especificamos dentro del environment correspondiente.

Thursday, March 07, 2013

Rails: Rakes


Sirven para crear y automatizar tareas. Algunas de las rakes más útiles que define rails pueden ser:

  • rake db:migrate: actualiza la base de datos. Con el parámetro VERSION=X, después del comando, la actualiza a la versión especificada.
  • rake stats:  nos da estadísticas de nuestra aplicación.
  • rake db:schema:load - Carga el fichero schema.rb en base de datos
  • rake db:fixtures:load - Carga los datos de las fixtures en el entorno de base de datos que especifiquemos. Puedes cargar fixtures especificas con FIXTURES=x,y
  • etc

 También nos podemos crear nuestras propias rakes. Para ello crearemos dentro de la estructura de carpeta lib/tasks un fichero terminado en .rake. La siguiente rake sirve para cargar datos de una o varias tablas en ficheros .yml:


namespace :db do
  desc 'Create YAML test fixtures from data in an existing database.
  Defaults to development database. Set RAILS_ENV to override.'

  task :load_data_table_to_yml => :environment do
    sql = "SELECT * FROM %s"
    skip_tables = ["schema_info", "sessions"]
    ActiveRecord::Base.establish_connection
    tables = ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : ActiveRecord::Base.connection.tables - skip_tables
    tables.each do |table_name|
      i = "000"
      if File.exists?("#{Rails.root}/test/fixtures/#{table_name}.yml")
      else
        File.new("#{Rails.root}/test/fixtures/#{table_name}.yml", "w+")
      end
      File.open("#{Rails.root}/test/fixtures/#{table_name}.yml", 'w') do |file|
        data = ActiveRecord::Base.connection.select_all(sql % table_name)
        file.write data.inject({}) { |hash, record|
          hash["#{table_name}_#{i.succ!}"] = record
          hash
        }.to_yaml
      end
    end
  end
end 
La forma de ejecutar esta rake sería:

rake db:load_data_table_to_yml

Donde si os fijais el 'db' es el namespace que definimos dentro de la rake y sirve para agrupar digamos tareas que son similares.Y 'load_data_table_to_yml' es el task que hemos definido en la rake. No tiene nada que ver con el nombre que le hemos dado al fichero .rake. Para pasarle parámetros se pasan a continuación de la instrucción en mayúsculas y dentro de la rake se recogen dentro de la variable de entorno ENV como se puede ver en el ejemplo.