rico_mac rico_mac - 6 months ago 13
MySQL Question

Rails saving two different model instances as a singular transaction

I am writing a service object for a sign up form that records data for a User and a Company model (FYI, I flat out refuse to use nested_attributes).

A User cannot exist without the presence of a Company (belongs_to).

IF the company succeeds and the user does not succeed in saving, how do I roll back the creation of the company?

I have tests copied below to demonstrate the point..

context 'when both are valid?' do
subject { -> { sign_up_object.save } }
it { should change(Company, :count).by(1) }
it { should change(sign_up_object, :company).to be_a Company }
it { should change(User, :count).by(1) }
it { should change(sign_up_object, :user).to be_a User }
end
context 'when COMPANY is invalid' do
subject { -> { sign_up_object.save } }
before { allow_any_instance_of(Company).to receive(:save!).and_return false }
it { should change(User, :count).by(0) }
it { should change(Company, :count).by(0) }
end
context 'when USER is invalid' do
before { allow_any_instance_of(User).to receive(:save!).and_return false }
subject { -> { sign_up_object.save } }
it { should change(User, :count).by(0) }
it { should change(Company, :count).by(0) } ->>>> this one fails!!!
end


The code I have at the moment looks something like this

class SignUp

......

def save_resources
ActiveRecord::Base.transaction do
save_company
save_user
end
end

def save_company
company = new_company
self.company = company if company.save!
end

def save_user
user = new_user
self.user = user if user.save!
end
end


I'm sure the
ActiveRecord::Base.transaction
block isn't actually doing anything, as my tests show the user spec as the only one as failing, as a company count is going up by 1.

Answer

You could manually rollback the transaction when user is not created:

Taking up your example:

class SignUp

  ......

  def save_resources
    ActiveRecord::Base.transaction do
      save_company
      save_user
      raise ActiveRecord::Rollback if self.user.nil?
    end
  end

  def save_company
    company = new_company
    self.company = company if company.save!
  end

  def save_user
   user = new_user
   self.user = user if user.save!
 end
end
Comments