Magazine Tecnologia

Gestire invii con Ruby on Rails e Actionmailer

Creato il 18 agosto 2011 da Mareika @mareikagiacobbi

A volte mi sono trovato in difficoltà a gestire parecchi invii di email dalla mia applicazione in quanto era difficile recuperare il valore massimo di invii simultanei dal fornitore di hosting, c'è chi risponde infinito, chi risponde 1 al minuto e chi non risponde.

Per avere una certa flessibilità l'ideale sarebbe quello di avere una sorta di buffer nel quale vengono accodate le email da inviare ed ogni X secondi viene estratta una email ed inviata.

In questo modo non si fa arrabbiare il fornitore di hosting e si inviano email singole agli utenti in modo automatico.

Come costruire un buffer per accodare le email che dovranno essere inviate

Prima cosa generiamo un Mailer

rails g mailer Notify

ed aggiungiamo il metodo notification per inviare una email ad un generico utente

file: /app/mailers/notify.rb

class Notify < ActionMailer::Base
  default :from => "[email protected]"

  def notification(to, from, subject, msg)
	  @msg = msg
	  mail(:from => from, :to => to, :subject => subject)
  end  
end

creiamo il relativo template

file: /app/views/notify/notification.html.erb

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Title</title>
	</head>
	<body>
		<p>
			<%= @msg %>
		</p>
	</body>
</html>

ora generiamo un modello che rappresenta il buffer contenent le email

rails g model queued_email mailer:string mailer_method:string priority:integer args:text

aggiungiamo 2 metodi all'interno del modello

  • send_email: estrae una email dal buffer e si occupa dell'invio usando uno dei metodi del Mailer
  • add: aggiunge una mail alla coda

In questo caso viene estratta una sola email per volta, nulla vieta di alzare questo valore in funzione del vostro SMTP. Viene estratta la prima email inserita, immaginatela come una coda FIFO.

file: /app/models/queued_email.rb

class QueuedEmail < ActiveRecord::Base
  serialize :args

  def self.send_email
	find(:all, :order=> "priority desc, created_at asc", :limit=>1).each do |mail|
	  mailer_class = mail.mailer.constantize
	  mailer_method = ("deliver_" + mail.mailer_method).to_sym
	  mailer_class.send(mailer_method, *mail.args)
	  mail.destroy
	end
	true
  end 

  def self.add(mailer, method, args, priority)
	QueuedEmail.create(:mailer=>mailer.to_s, :mailer_method=>method.to_s, :args => args, :priority=> priority)
  end
end

La direttiva serialize :args ci permette di serializzare l'attributo args, vediamolo come un oggetto che contiene i valori per l'invio della email e che verrà deserializzato al momento della chiamata al metodo del Mailer.

Per inserire una email nel buffer possiamo procedere in questo modo:

args = [MAIL_ADMIN, MAIL_FROM, "oggetto","il tuo messaggio qui"]
QueuedEmail.add("Notify","notification", args, 0)

creiamo un controller che verrà richiamato ogni X secondi e produrrà l'invio della email

rails g controller senders index

file: /app/controllers/senders_controller.rb

class SendersController < ApplicationController
	def index
		QueuedEmail.send_email
	end
end

chiamando via browser questo url verrà estratta la prima email inserita nel buffer ed inviata

http://localhost:3000/senders

possiamo pianificare un job nel crontab per chiamare ciclicamente l'url sopra indicato!

*/5 * * * * wget http://localhost:3000/senders

Se il nostro fornitore di hosting non ci permette di editare il crontab che fare? 

Vi mostrerò dei workaround nei prossimi post.


Potrebbero interessarti anche :

Ritornare alla prima pagina di Logo Paperblog

Possono interessarti anche questi articoli :