2009-06-29 5 views
22

Ho un modello che utilizza una forcella acts_as_nested_set e ho aggiunto un metodo al modello per salvare il modello e spostare il nodo nell'insieme in una transazione. Questo metodo chiama un metodo di convalida per assicurarsi che lo spostamento sia valido, che restituisce vero o falso. Se la convalida fallisce, voglio che il mio metodo di salvataggio aumenti ActiveRecord::Rollback per il rollback della transazione, ma restituisca anche false al chiamante.Come aumentare un'eccezione di ActiveRecord :: Rollback e restituire un valore insieme?

Il mio modello è simile al seguente:

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 

Tuttavia, quando chiamo salvo in una situazione che verrebbe a mancare, la transazione viene annullata, ma la funzione restituisce 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"] 

risposta

26

è possibile memorizzare il valore che si desidera venga restituito dalla funzione in una variabile e restituire che al di fuori del blocco di transazione. Per esempio.

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 

ho impostato il RETURN_VALUE false inizialmente come l'unico altro modo si può uscire da quella blocco di transazione è se uno degli altri metodi solleva ActiveRecord::Rollback credo.

+0

+1, essenzialmente la stessa conclusione a cui sono arrivato. –

+0

GRAZIE! Ancora valido in Rails 3.2.8. Non mi è stato chiaro dalla [documentazione] (http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html) che "raise ActiveRecord :: Rollback" salta alla linea dopo la fine della transazione. Sembrava che stesse cadendo, come se il Rollback non interrompesse affatto il flusso del programma. –

10

Perché il L'eccezione ActiveRecord::Rollback viene gestita, ma non controrilanciata da ActiveRecord::Transaction, potrei spostare il mio ritorno fuori dal blocco della transazione e quindi restituire un valore dopo il rollback della transazione.

Con un po 'di 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

So che potrebbe essere un po 'in ritardo, ma mi sono imbattuto nello stesso problema e ho appena scoperto che all'interno di un blocco di transazioni puoi semplicemente sollevare un'eccezione e salvare quella ... Rails esegue il rollback implicito dell'intera transazione. Quindi non c'è bisogno di ActiveRecord :: Rollback.

Ad esempio:

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 

sto lavorando con Rails 3.2.15 e 1.9.3 di Ruby.