Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return false and rollback in after_save callback

In ActiveRecord model after_save callback I need to ROLLBACK transaction and return false.

def after_save_callback
  if mycondition?
    raise ActiveRecord::Rollback
  end
end

This callback rolls-back transaction but mymodel.save! returns true. How to make it return false and rollback?

like image 746
Jonas Avatar asked Mar 26 '12 17:03

Jonas


2 Answers

If you want to abort a save in an after_save callback, you should

raise ActiveRecord::RecordInvalid.new(self)

rather than

raise ActiveRecord::Rollback

This will not only roll back the transaction (callbacks always happen inside a possibly-implicit transaction as part of the save or create) but also cause save to return false.

Here is an article with more details: http://tech.taskrabbit.com/blog/2013/05/23/rollback-after-save/

like image 60
Paul A Jungwirth Avatar answered Nov 14 '22 14:11

Paul A Jungwirth


def around_save
  ActiveRecord::Base.transaction do
    raise ActiveRecord::Rollback # this will actually ROLLBACK
    yield # calls the actual save method
    raise ActiveRecord::Rollback # this will cause a COMMIT!!! because it affect only this internal transaction.
    # OTHER ACTIONS NOT EXECUTED BUT BEING A INTERNAL TRANSACTION, THE PARENT WILL COMMIT, because parent hasn't failed.
  end
end

So... I think around_save come already on a transaction block, so you don't need to add that extra ActiveRecord::Base.transaction do block because rollbacks doesnt propagate up

So if you want to rollback before or after yield, you need to remove that internal transaction.

def around_save
  #ActiveRecord::Base.transaction do
    raise ActiveRecord::Rollback # this will actually ROLLBACK
    yield # calls the actual save method
    raise ActiveRecord::Rollback # this will actually ROLLBACK
  # end
end

EDIT: Reading what I wrote... now seem difficult to understand. The point is: if you are gonna use aroud_save don't wrapp again with ActiveRecord::Base.transaction (do like in the last example) because rails will wrap the call to around_save with is own ActiveRecord::Base.transaction so when you raise ActiveRecord::Rollback you are only rolling back the most internal transaction, so you can end with extrange results and partial saves (like in the first example whic is FAIL).

like image 9
tyoc213 Avatar answered Nov 14 '22 13:11

tyoc213