28

フォークを使用するモデルがありacts_as_nested_set、モデルを保存してノードを1つのトランザクションでセットに移動するメソッドをモデルに追加しました。このメソッドは検証メソッドを呼び出して、移動が有効であることを確認します。これにより、trueまたはfalseが返されます。検証が失敗した場合は、saveメソッドをActiveRecord::Rollback起動してトランザクションをロールバックしますが、呼び出し元にfalseを返します。

私のモデルは次のようになります。

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

ただし、失敗する状況でsaveを呼び出すと、トランザクションはロールバックされますが、関数は次を返します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"]
4

3 に答える 3

34

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

  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

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

于 2009-06-29T15:21:52.837 に答える
15

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
于 2009-06-29T15:20:17.487 に答える
3

少し遅れるかもしれませんが、同じ問題が発生し、トランザクションブロック内で例外を発生させて、その例外をレスキューできることがわかりました...Railsは暗黙的にトランザクション全体をロールバックします。したがって、ActiveRecord::Rollbackは必要ありません。

例えば:

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

Rails3.2.15とRuby1.9.3を使用しています。

于 2016-06-13T13:42:49.740 に答える