Kkulikovskis
Kkulikovskis

Reputation: 2088

Passing errors to user from ActiveRecord transaction

I have a transaction like this

  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

What I would like to accomplish is add corresponding error messages to the Errorsobject if the transaction is not completed. So the desired method would look something like

  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

Right now I can't get the desired result, those actions, in case of a failure, raise ActiveRecord::Rollback error and don't trigger error methods.

Upvotes: 1

Views: 1205

Answers (2)

Kkulikovskis
Kkulikovskis

Reputation: 2088

The solution I came up with (also thanks to @lcguida). Is a somewhat simple one

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

The idea being that for each error I have a separate method, where I pass in the method that can cause the error. Then I rescue from ActiveRecord::Rollback in an isolated environment, append error and re-raise the same error.

Please post another answer if there is something easier/better.

Upvotes: 1

Andy
Andy

Reputation: 135

It sounds like you want to use save and not save!

save! raises an exception if the validations fail http://apidock.com/rails/ActiveRecord/Base/save!

save returns false http://apidock.com/rails/ActiveRecord/Base/save

so you can do: unless save # add errors end

but note both rollback the transaction.

Upvotes: 1

Related Questions