actionmailer-callbacks per rails3: concept di una gemma
Pubblicato 4 giorni fa da Andrea
Tempo fa ho sviluppato per rails 2 la gemma actionmailer-callbacks che permetteva di eseguire codice arbitrario prima e dopo la creazione o l’invio di una email, un po’ come siamo da sempre abituati a fare con i controller rails utlizzando le macro before_filter e after_filter o nei modelli con le callback di active record.
Con l’arrivo di rails 3 action mailer è stato riscritto aggiungendo nuove funzionalità, ora i nuovi observer e interceptor ci permettono di chiamare dei metodi rispettivamente dopo la creazione e dopo l’invio dell’email: per la versione compatibile con rails 3 ci resta solo da implementare le funzionalità legate al momento precedente alla creazione dell’email. Per prima cosa definiamo l’interfaccia che vorremmo utlizzare nelle varie classi mailer (ricordiamoci che l’interfaccia viene prima di tutto):
class MyAppMailer < ApplicationMailer before_create :log_args def greet_new_user(user) #... end private def log_args(*args) Rails.logger.info "A #{self.class} was initialized with #{args.inspect}" end end
Il codice precedente deve permetterci quindi di eseguire log_args prima del processo normale di costruzione dell’email. Modifichiamo ActionMailer aggiungendo la definizione di before_create:
class ActionMailer::Base class << self def before_create(*methods) include CallbackProcessor add_callbacks(*methods) end private def add_callbacks(*methods) methods.each do |method| before_create_callbacks << method end end end end
add_callbacks si occupa di includere il modulo CallbackProcessor nella classe su cui viene chiamato e di aggiungere alle liste delle callback i nomi dei vari metodi che intendiamo usare (il modulo CallbackProcessor va creato prima di modificare ActionMailer nonostante in questo post ne mostri il codice solo in seguito).
Infine creiamo il modulo mancante definendo una serie di metodi che estendono la classe del mailer non appena viene chiamato before_create_callbacks:
module CallbackProcessor def self.included(base) base.extend ClassMethods end module ClassMethods def before_create_callbacks @before_create_callbacks ||= [] end end def initialize(*args) self.class.before_create_callbacks.each do |callback| send callback, *args end super end end
A questo punto ogni volta che verrà creata/inviata una email con
MyAppMailer.greet_new_user
verrà eseguito il metodo log_args prima di tutto il resto.
L’implementazione reale della gemma è più complessa ma il meccanismo è sostanzialmente il medesimo.
In chiusura sottolineo una importante differenza tra le versioni rails 2 e rails 3 della mia gemma: nel primo caso utilizzai alias_method_chain per modificare direttamente il comportamento di ActionMailer::Base, mentre nella nuova versione mi sono affidato ad un sistema più pulito promosso da Yehuda Katz in un post di 3 anni fa ma ancora attualissimo.
Chi fosse interessato all’implementazione della gemma può trovarla
qui. (la versione rails 3 è al momento ancora WIP).
Archiviato in Ruby on Rails, Ruby


