2009-06-29 16 views
22

Mam model, który używa widelca acts_as_nested_set i dodałem metodę do modelu, aby zapisać model i przenieść węzeł do zestawu w jednej transakcji. Ta metoda wywołuje metodę sprawdzania poprawności, aby upewnić się, że ruch jest poprawny, co zwraca wartość true lub false. Jeśli sprawdzanie poprawności nie powiedzie się, chcę, aby moja metoda składowania podniosła ActiveRecord::Rollback w celu wycofania transakcji, ale także zwróciła false do osoby dzwoniącej.Jak podnieść wyjątek ActiveRecord :: Rollback i zwrócić wartość razem?

Mój model wygląda następująco:

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 

Jednak kiedy zadzwonić Zapisz w sytuacji, która nie powiedzie, transakcja zostanie wycofana, ale zwraca 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"] 

Odpowiedz

26

Mogłeś przechowywania wartości, które mają być zwracane z funkcji w zmiennej i powrócić że na zewnątrz bloku transakcji. Na przykład.

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 

mam ustawić Return_Value false początkowo jako jedyny inny sposób można wyjść z tego bloku transakcji jest, jeśli jeden z pozostałych metod podnosi ActiveRecord::Rollback wierzę.

+0

+1, w zasadzie ten sam wniosek, do którego doszedłem. –

+0

DZIĘKUJEMY! Nadal ważne w Railsach 3.2.8. Nie było dla mnie jasne z [dokumentacji] (http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html), które "podnoszą ActiveRecord :: Rollback" przeskakuje do linii po zakończeniu transakcji. Wyglądało na to, że właśnie się przewracało, jakby Rollback w rzeczywistości nie przerywał przepływu programu. –

10

Ponieważ ActiveRecord::Rollback wyjątek jest obsługiwany, ale nie jest ponownie wywoływany przez ActiveRecord::Transaction, mógłbym przenieść mój powrót z bloku transakcji, a tym samym zwrócić wartość po wycofaniu transakcji.

Przy odrobinie refaktoringu:

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

Wiem, że może być trochę za późno, ale wpadłem na ten sam problem i właśnie się dowiedziałem, że w ramach bloku transakcji można po prostu podnieść wyjątek i uratować to ... Railsy domyślnie wycofują całą transakcję. Więc nie ma potrzeby ActiveRecord :: Rollback.

Na przykład:

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 

Pracuję z Rails Ruby 1.9.3 i 3.2.15.

Powiązane problemy