XP on Rails Extreme Programming Blog

13Apr/080

Rails e transazioni

In questi giorni ho toccato con mano la gestione delle transazioni in Rails.

Il mio problema consisteva nel gestire in un’unica transazione la crezione di due modelli diversi ed indipendenti l’uno dall’altro.

Ogni oggetto ActiveRecord e’ provvisto di un metodo

transaction

, in grado di gestire in blocco operazioni di scrittura sullo stesso o su oggetti collegati da relazioni tipo

has_one, has_many

ecc. Quindi, nel mio caso, creare due oggetti non relazionati significava aprire due transazioni separate.

Questo aspetto di Rails mi ha molto stupito e sinceramente non capisco la scelta di THH di gestire le transazioni in questo modo.
A mio parere, una scelta piu’ felice e’ stata presa dal team di Django, dando la possibilita’ di gestire le transazioni alla Rails o relative all’intera richiesta HTTP, attraverso la libreria

TransactionMiddleware

. Questa libreria permette infatti di aprire una transazione con l’inizio di una richiesta HTTP e di committare o di eseguire il rollback in base al successo o no della

response

Tornando al mio problema, a nulla sono valsi i tentativi di risolvere la transazione usando:

begin
  ActiveRecord::Base.transaction do
    @first_model = FirstModel.new(params[:first_model])
    @first_model.save!
    @second_model = SecondModel.new(:user => current_user, :first_model => @first_model)
    @second_model.save!  
  end
rescue Exception => @ex
 # handle the exception
end

oppure usando transazioni innestate:

begin
  FirstModel.transaction do
    SecondModel.transaction do
      @first_model = FirstModel.new(params[:first_model])
      @first_model.save!
      @second_model = SecondModel.new(:user => current_user, :first_model => @first_model)
      @second_model.save!  
    end
  end
rescue Exception => @ex
 # handle the exception
end

In entrambi i casi, se la creazione di

SecondModel

falliva, per la prima transazione relativa al

FirstModel

non veniva eseguito il rollback, e i dati venivano salvati comunque sul database.

La soluzione al problema e’ stata incapsulare l’intero motodo del controller in un

around filter

, aprendo una transazione prima dell’esecuzione del metodo e chiudendola all’uscita dello stesso.

Questo il codice:

#application.rb
prepend_around_filter :transaction_filter

def transaction_filter
  ActiveRecord::Base.transaction { |*block_args| yield(*block_args) if block_given? }
end

Un’alternativa potrebbe essere l’utilizzo del plugin Super Transaction

About stefano

Independent Information Technology and Services Professional
Tagged as: Leave a comment
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment


No trackbacks yet.