2009-06-29 9 views
22

Ich habe ein Modell, das ein acts_as_nested_set Fork verwendet, und ich habe eine Methode zu dem Modell hinzugefügt, um das Modell zu speichern und den Knoten in dem Set in einer Transaktion zu verschieben. Diese Methode ruft eine Validierungsmethode auf, um sicherzustellen, dass die Verschiebung gültig ist und True oder False zurückgibt. Wenn die Überprüfung fehlschlägt, möchte ich, dass meine Sicherungsmethode ActiveRecord::Rollback aufruft, um die Transaktion zurückzusetzen, aber auch false an den Aufrufer zurückgibt.Wie eine ActiveRecord :: Rollback-Ausnahme auslösen und einen Wert zusammen zurückgeben?

Mein Modell sieht wie folgt aus:

class Category < ActiveRecord::Base 
    acts_as_nested_set :dependent => :destroy, :scope => :journal 

    def save_with_place_in_set(parent_id) 
    Category.transaction do 
     return false if !save_without_place_in_set 

     if !validate_move parent_id 
     raise ActiveRecord::Rollback and return false 
     else 
     place_in_nested_set parent_id 
     return true 
     end 
    end 
    end 

    alias_method_chain :save, :place_in_set 

    def validate_move(parent_id) 
    # return true or false if the move is valid 
    # ... 
    end 

    def place_in_nested_set(parent_id) 
    # place the node in the correct place in the set 
    # ... 
    end 
end 

Allerdings, wenn ich außer in einer Situation nennen, die fehlschlagen würde, wird die Transaktion rückgängig gemacht, aber die Funktion gibt nil:

>> c = Category.new(:name => "test") 
=> #<Category id: nil, name: "test" parent_id: nil, lft: nil, rgt: nil> 
>> c.save_with_place_in_set 47 
=> nil 
>> c.errors.full_messages 
=> ["The specified parent is invalid"] 

Antwort

26

Sie könnten den Wert speichern Sie aus der Funktion in einer Variablen und zurück, dass außerhalb des Transaktionsblock zurückgegeben werden sollen. Z.B.

def save_with_place_in_set(parent_id) 
    return_value = false 
    Category.transaction do 
     if !save_without_place_in_set 
     return_value = false 
     elsif !validate_move parent_id 
     return_value = false 
     raise ActiveRecord::Rollback 
     else 
     place_in_nested_set parent_id 
     return_value = true 
     end 
    end 
    return return_value 
    end 

ich die return_value auf false zunächst als einzige andere Art und Weise festgelegt haben, können Sie aus dieser Transaktion Block erhalten ist, wenn einer der anderen Methoden wirft ActiveRecord::Rollback glaube ich.

+0

+1, im Wesentlichen die gleiche Schlussfolgerung, zu der ich kam. –

+0

DANKE! Immer noch gültig in Rails 3.2.8. Es war mir aus der [Dokumentation] (http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html) nicht klar, dass 'raise ActiveRecord :: Rollback' nach dem Ende der Transaktion auf die Zeile springt. Es sah aus, als würde es gerade durchfallen, als würde der Rollback den Programmfluss überhaupt nicht unterbrechen. –

10

Da die ActiveRecord::Rollback Ausnahme behandelt, aber nicht erneut von ActiveRecord::Transaction ausgelöst, ich könnte meine Rückkehr aus dem Transaktionsblock verschieben und damit einen Wert zurückgeben, nachdem die Transaktion zurückgesetzt wird.

Mit ein wenig Refactoring:

def save_with_place_in_set(parent_id = nil) 
    Category.transaction do 
    return false if !save_without_place_in_set 
    raise ActiveRecord::Rollback if !validate_move parent_id 

    place_in_nested_set parent_id 
    return true 
    end 

    return false 
end 
-1

Ich weiß, es kann ein wenig spät sein, aber ich lief auf das gleiche Problem und gerade herausgefunden, dass innerhalb einer Transaktionssperre können Sie einfach eine Ausnahme auslösen und diese ... Rails implizit Rollbacks die gesamte Transaktion. Daher ist ActiveRecord :: Rollback nicht erforderlich.

Zum Beispiel:

def create 
    begin 
    Model.transaction do 
     # using create! will cause Exception on validation errors 
     record = Model.create!({name: nil}) 
     check_something_afterwards(record) 
     return true 
    end 
    rescue Exception => e 
    puts e.message 
    return false 
    end 
end 

def check_something_afterwards(record) 
    # just for demonstration purpose 
    raise Exception, "name is missing" if record.name.nil? 
end 

Ich arbeite mit Rails 3.2.15 und Ruby 1.9.3.

Verwandte Themen