2013-09-29 17 views
5

Mam sytuację, w której chciałbym, aby metoda działała w ramach transakcji, ale tylko wtedy, gdy transakcja jeszcze się nie rozpoczęła. Oto zmyślony przykład destylować co mówię:Zapewnienie transakcji w ramach metody z ActiveRecord

class ConductBusinessLogic 
    def initialize(params) 
    @params = params 
    end 

    def process! 
    ActiveRecord::Base.transaction do 
     ModelA.create_multiple(params[:model_a]) 
     ModelB.create_multiple(params[:model_a]) 
    end 
    end 
end 

class ModelA < ActiveRecord::Base 
    def self.create_multiple(params) 
    # I'd like the below to be more like "ensure_transaction" 
    ActiveRecord::Base.transaction do 
     params.each { |p| create(p) } 
    end 
    end 
end 

class ModelB < ActiveRecord::Base 
    def self.create_multiple(params) 
    # Again, a transaction here is only necessary if one has not already been started 
    ActiveRecord::Base.transaction do 
     params.each { |p| create(p) } 
    end 
    end 
end 

Zasadniczo nie chcę ich do działania jako transakcje zagnieżdżone. Chcę, aby metody .create_multiple tylko rozpoczynały transakcje, jeśli nie zostały jeszcze wywołane w ramach transakcji, na przykład przez ConductBusinessLogic#process!. Jeśli metody modelu są wywoływane osobno, powinny rozpocząć własną transakcję, ale jeśli są już wywoływane w ramach transakcji, np. Przez ConductBusinessLogic#process!, nie powinny zagnieżdżać pod-transakcji.

Nie znam sposobu, w jaki Rails dostarcza to po wyjęciu z pudełka. Jeśli powyższy kod zostanie uruchomiony bez zmian, a wycofanie zostanie wywołane przez jedną z metod modelu, cała transakcja będzie nadal wykonywana, ponieważ transakcja podrzędna połknie wyjątek ActiveRecord::Rollback. Jeśli użyję opcji requires_new w przypadku pod-transakcji, punkty zapalne zostaną użyte do symulacji transakcji zagnieżdżonych i tylko ta pod-transakcja zostanie faktycznie wycofana. Zachowanie, które chciałbym osiągnąć, byłoby czymś w rodzaju efektu ActiveRecord::Base.ensure_transaction, tak że nowa transakcja jest uruchamiana tylko wtedy, gdy nie ma już transakcji zewnętrznej, tak że każda transakcja podrzędna może wywołać wycofanie całej transakcji zewnętrznej. Pozwoliłoby to, aby te metody były transakcyjne same, ale odroczyły transakcję nadrzędną, jeśli taka istnieje.

Czy istnieje wbudowany sposób osiągnięcia tego zachowania, a jeśli nie, czy istnieje klejnot lub łata, które zadziałają?

+0

Co o [transaction_open] (http: //api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-transaction_open-3F)? –

Odpowiedz

1

Co powiesz na dodanie metody create_multiple_without_transaction do swoich klas ModelA i ModelB? który będzie wyglądać mniej więcej tak:

class ConductBusinessLogic 
    def initialize(params) 
    @params = params 
    end 

    def process! 
    ActiveRecord::Base.transaction do 
     ModelA.create_multiple_without_transaction(params[:model_a]) 
     ModelB.create_multiple_without_transaction(params[:model_a]) 
    end 
    end 
end 

class ModelA < ActiveRecord::Base 
    def self.create_multiple(params) 
    # I'd like the below to be more like "ensure_transaction" 
    ActiveRecord::Base.transaction do 
     self.create_multiple_without_transaction(params) 
    end 
    end 

    def self.create_multiple_without_transaction(params) 
    params.each { |p| create(p) } 
    end 
end 

następnie regularnego create_multiple będzie działać jak poprzednio, ale w przypadkach były nie trzeba transakcji będzie po prostu zadzwonić create_multiple_without_transaction

Powiązane problemy