从ActiveRecord事务向用户传递错误

时间:2016-02-04 12:23:29

标签: ruby-on-rails activerecord transactions

我有这样的交易

  def accept_transaction
    Purchase.transaction do
      save! #Validate and Save purchase
      product.update_bought
      user.charge!(product.price)
      Investment.add_spent(user_id: user.id,
                                spent: product.price)
  end

如果事务未完成,我想要完成的是向Errors对象添加相应的错误消息。所以期望的方法看起来像

  def accept_transaction
    Purchase.transaction do
      save! #Validate and Save purchase(adds validation errors by default)
      add_out_of_stock_error unless product.update_bought
      add_no_money_error unless user.charge!(product.price)
      other_error unless Investment.add_spent(user_id: user.id,
                                spent: product.price)
  end

  def add_out_of_stock_error
    errors[:base].add("Product not available")
  end
  def no_money_error
   ...
  end
  def other_error
  ...
  end

现在我无法获得所需的结果,这些操作会在出现故障时引发ActiveRecord::Rollback错误,并且不会触发错误方法。

2 个答案:

答案 0 :(得分:0)

听起来您想使用save而不是save!

如果验证失败,

save!会引发异常 http://apidock.com/rails/ActiveRecord/Base/save

save返回false http://apidock.com/rails/ActiveRecord/Base/save

所以你可以这样做: unless save # add errors end

但请注意回滚事务。

答案 1 :(得分:0)

我提出的解决方案(也感谢@lcguida)。是一个有点简单的

def accept_transaction
    Purchase.transaction do
      save! #Validate and Save purchase(adds validation errors by default)
      catch_out_of_stock_error { product.update_bought }
      catch_no_money_error { user.charge!(product.price) }
      catch_other_error { Investment.add_spent(user_id: user.id,
                                spent: product.price) }
  end

  def catch_out_of_stock_error &block
    begin
      yield
    rescue ActiveRecord::Rollback => e
      errors.add(:base,"Product not available")
      raise e
    end
  end
  def catch_no_money_error &block
   ...
  end
  def catch_other_error &block
  ...
  end

我的想法是,对于每个错误,我都有一个单独的方法,在那里我传入可能导致错误的方法。然后我在一个孤立的环境中从ActiveRecord::Rollback救出,追加错误并重新引发同样的错误。

如果有更容易/更好的事情,请发布另一个答案。