2009-06-29 11 views
22

私はacts_as_nested_setフォークを使用するモデルを持っています。モデルを保存し、そのノードを1つのトランザクションでセットに移動する方法をモデルに追加しました。このメソッドは、検証メソッドを呼び出して、trueまたはfalseを返す移動が有効であることを確認します。検証に失敗した場合は、保存メソッドをActiveRecord::Rollbackにしてトランザクションをロールバックし、呼び出し元にfalseを返すようにします。ActiveRecord :: Rollback例外を発生させ、値を一緒に返すにはどうすればいいですか?

私のモデルは次のようになります。私は失敗するような状況でsaveを呼び出すとき

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 

しかし、トランザクションがロールバックされますが、機能が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"] 

答えて

26

あなたは変数に関数から返さする値を格納し、トランザクションブロックの外にそれを返すことができます。例えば。

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 

私は他の方法の一つは、私は信じていActiveRecord::Rollbackを上げる場合は、そのトランザクションブロックから抜け出すことができる唯一の他の方法があるとして最初にfalseにRETURN_VALUEを設定しました。

+0

+1、私が来たのと本質的に同じ結論。 –

+0

感謝します! Rails 3.2.8で有効です。 [ドキュメント](http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html)から、 'raise ActiveRecord :: Rollback'がトランザクションの終了後に行にジャンプすることは私には明らかではありませんでした。ロールバックは実際にはプログラムの流れを全く中断しないように、ちょうど落ちているように見えました。 –

10

ので、 ActiveRecord::Rollback例外は処理されますが、ActiveRecord::Transactionによって再発行されませんでした。トランザクションブロックから戻り値を移動して、トランザクションがロールバックされた後に値を返すことができました。少しリファクタリングで

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

私は少し遅れているかもしれませんが、私は同じ問題に遭遇し、ちょうどトランザクションブロック内で例外を発生させ、その1つを救済することができることを発見しました... Railsはトランザクション全体を暗黙的にロールバックします。したがって、ActiveRecord :: Rollbackの必要はありません。例えば

:私はRailsの3.2.15とRuby 1.9.3で働いている

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 

関連する問題