2009-06-29 14 views
22

Tengo un modelo que usa una horquilla acts_as_nested_set, y he agregado un método al modelo para guardar el modelo y mover el nodo al conjunto en una transacción. Este método llama a un método de validación para asegurarse de que el movimiento sea válido, lo que devuelve verdadero o falso. Si la validación falla, quiero que mi método de guardado eleve ActiveRecord::Rollback para deshacer la transacción, pero también devuelva falso a la persona que llama.¿Cómo generar una excepción ActiveRecord :: Rollback y devolver un valor?

Mi modelo es el siguiente:

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 

Sin embargo, cuando llamo, salvo en una situación que sería un fracaso, la transacción se deshace, pero la función devuelve 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"] 

Respuesta

26

Se puede almacenar el valor que desea obtener de la función en una variable y devolver esa fuera del bloque de transacción. P.ej.

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 

He puesto el valor_devuelto en false inicialmente como la única otra forma que pueda salir de ese bloque de transacción es decir, si uno de los otros métodos plantea ActiveRecord::Rollback creo.

+0

+1, esencialmente la misma conclusión a la que llegué. –

+0

¡GRACIAS! Aún válido en Rails 3.2.8. No estaba claro para mí de la [documentación] (http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html) que 'elevar ActiveRecord :: Rollback' salta a la línea después del final de la transacción. Parecía que estaba cayendo, como si el Rollback no interrumpiera el flujo del programa. –

10

Debido a que la ActiveRecord::Rollback la excepción se maneja, pero no se vuelve a generar por ActiveRecord::Transaction, podría mover mi devolución fuera del bloque de transacción, y así devolver un valor después de que se retrotraiga la transacción.

Con un poco de refactorización:

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

Sé que puede ser un poco tarde, pero me encontré con el mismo problema y me acabo de enterar, que dentro de un bloque de transacción puede simplemente generar una excepción y rescatar esa ... Rails implícitamente revierte toda la transacción. Entonces no hay necesidad de ActiveRecord :: Rollback.

Por ejemplo:

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 

estoy trabajando con rieles 3.2.15 y 1.9.3 de Ruby.

Cuestiones relacionadas