Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3: rollback for after_create

I have an enrollment form.

When the user enrolls, the app is supposed to save the data in the enrollments table and in the users table. (I need this separation because the user's profile can change but the data he entered for that particular enrollment has to be archived. So even if later the user changes his last name, in the enrollment form I'll have his initial information.)

So I was thinking about saving data in the enrollments table then have a after_create call, like this...

class Enrollment < ActiveRecord::Base

  after_create :save_corresponding_user

  def save_corresponding_user
    user = User.new
    user.full_name = self.user_full_name
    user.email = self.user_email
    user.mobile_phone = self.user_mobile_phone
    user.save
  end
end

The issue is, what if saving the user fails for any reason. How can I rollback and destroy the just saved data from the enrollments table?

like image 850
leonel Avatar asked Jul 27 '12 17:07

leonel


4 Answers

Returning false from after_create will do nothing.

The whole callback chain is wrapped in a transaction. If any before callback method returns exactly false or raises an exception, the execution chain gets halted and a ROLLBACK is issued; after callbacks can only accomplish that by raising an exception.

Also, you must raise ActiveRecord::Rollback:

Any exception that is not ActiveRecord::Rollback will be re-raised by Rails after the callback chain is halted. Raising an exception other than ActiveRecord::Rollback may break code that does not expect methods like save and update_attributes (which normally try to return true or false) to raise an exception.

http://guides.rubyonrails.org/active_record_callbacks.html#halting-execution

I do something like this:

after_create do
  if condition
    errors.add(:attr, 'Blah blah blah.')
    raise ActiveRecord::Rollback
  end
end

For Rails 3: http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#halting-execution

like image 101
atomkirk Avatar answered Nov 20 '22 08:11

atomkirk


after_create is a part of the transaction saving the current model. Therefore, if your code crashes or if after_create returns false, it should rollback the current transaction and invalidate the enrollment saving.

If you want to simulate this, add this to your after_create and see if everything works as expected :

raise Exception.new("CRASH")
like image 32
Anthony Alberto Avatar answered Nov 20 '22 08:11

Anthony Alberto


As @anthonyalberto mentioned, after_create is already part of the transaction. To define a transaction you would use something like this in your controller:

Enrollment.transaction do
  @enrollment.save!
end

That is really all you need to do, if the save of enrollment fails or the save of user fails it will roll back your entire transaction. Here is more information: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

like image 1
Jeff Steil Avatar answered Nov 20 '22 06:11

Jeff Steil


In rails 5, you need to use throw :abort to rollback ActiveRecord chain.

enter image description here

You can see the list of operation handling in different versions of Rails along with before and after callbacks. For better clarification, please look at the concept of halting executions. Please follow the the given link below.

https://guides.rubyonrails.org/active_record_callbacks.html#halting-execution

like image 1
V K Singh Avatar answered Nov 20 '22 07:11

V K Singh