如何引发ActiveRecord :: Rollback异常并一起返回一个值?

Dan*_*uis 24 ruby transactions ruby-on-rails rollback

我有一个使用acts_as_nested_setfork 的模型,我已经为模型添加了一个方法来保存模型并在一个事务中将节点移动到集合中.此方法调用验证方法以确保移动有效,返回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
Run Code Online (Sandbox Code Playgroud)

但是,当我在失败的情况下调用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"]
Run Code Online (Sandbox Code Playgroud)

Sha*_*ell 29

您可以将要从函数返回的值存储在变量中,并将其返回到事务块之外.例如

  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
Run Code Online (Sandbox Code Playgroud)

我最初将return_value设置为false,因为唯一可以摆脱该事务块的另一种方法是,如果其他方法之一提出,ActiveRecord::Rollback我相信.

  • 谢谢你!在 Rails 3.2.8 中仍然有效。从[文档](http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html)中我不清楚“raise ActiveRecord::Rollback”会跳转到事务结束后的行。看起来它只是失败了,好像回滚实际上根本没有中断程序流程。 (4认同)

Dan*_*uis 11

因为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
Run Code Online (Sandbox Code Playgroud)