title: How to rollback all transactions in transaction block on error in Ruby on Rails
body: <p>I have a Mission model which has the following associations:</p>
  <pre class="lang-rb prettyprint-override"><code>...
    # === missions.rb (model) ===
    has_many :addresses, as: :addressable, dependent: :destroy
    accepts_nested_attributes_for :addresses
    has_many :phones, as: :phoneable, dependent: :destroy
    accepts_nested_attributes_for :phones
    has_one :camera_spec, as: :camerable, dependent: :destroy
    accepts_nested_attributes_for :camera_spec
    has_one :drone_spec, as: :droneable, dependent: :destroy
    accepts_nested_attributes_for :drone_spec
  ...
  </code></pre>
  <p>When a user creates a Mission, they input all of the information for the Mission, Phone, Address, CameraSpec, and DroneSpec into one large form. I'm able to save records correctly when all of the information is correct. However, if there is an error in any of the models, I want to rollback <strong>ALL</strong> transactions and render the form with errors.</p>
  <p>This topic has been covered in other places, however, I'm unable to rollback all transactions using the methods I've seen. Currently, if there is a DB/ActiveRecord error one of the models, let's say CameraSpec, then the previously created Mission, Address, and Phone are not rolled back. I've tried nested transactions like:</p>
  <pre class="lang-rb prettyprint-override"><code>Mission.transaction do
    begin
      # Create the mission
      Mission.create(mission_params)
  
      # Create the Address
      raise ActiveRecord::Rollback unless Address.transaction(requires_new: true) do
        Address.create(address_params)
        raise ActiveRecord::Rollback
      end
  
  ...
  
    rescue ActiveRecord::Rollback =&gt; e
  
  ...
  
    end
  end
  </code></pre>
  <p>I've tried throwing different kinds of errors such as <code>ActiveRecord::Rollback</code>. I'm always able to catch the error, but the DB doesn't rollback. I've tried both using and not using a begin-rescue statement. My current attempt is to not nest the transactions and instead commit them in a single transaction block, but this also is not working. Here is my current code.</p>
  <pre class="lang-rb prettyprint-override"><code># === missions_controller.rb ===
    def create
      # Authorize the user
  
      # Prepare records to be saved using form data
      mission_params = create_params.except(:address, :phone, :camera_spec, :drone_spec)
      address_params = create_params[:address]
      phone_params = create_params[:phone]
      camera_spec_params = create_params[:camera_spec]
      drone_spec_params = create_params[:drone_spec]
  
      @mission = Mission.new(mission_params)
      @address = Address.new(address_params)
      @phone = Phone.new(phone_params)
      @camera_spec = CameraSpec.new(camera_spec_params)
      @drone_spec = DroneSpec.new(drone_spec_params)
  
      # Try to save the company, phone number, and address
      # Rollback all if error on any save
      ActiveRecord::Base.transaction do
        begin
          # Add the current user's id to the mission
          @mission.assign_attributes({
            user_id: current_user.id
          })
  
          # Try to save the Mission
          unless @mission.save!
            raise ActiveRecord::Rollback, @mission.errors.full_messages
          end
  
          # Add the mission id to the address
          @address.assign_attributes({
            addressable_id: @mission.id,
            addressable_type: &quot;Mission&quot;,
            address_type_id: AddressType.get_id_by_slug(&quot;takeoff&quot;)
          })
          
          # Try to save any Addresses
          unless @address.save!
            raise ActiveRecord::Rollback, @address.errors.full_messages
          end
  
          # Add the mission id to the phone number
          @phone.assign_attributes({
            phoneable_id: @mission.id,
            phoneable_type: &quot;Mission&quot;,
            phone_type_id: PhoneType.get_id_by_slug(&quot;mobile&quot;)
          })
  
          # Try to save the phone
          unless @phone.save!
            raise ActiveRecord::Rollback, @phone.errors.full_messages
          end
  
          # Add the mission id to the CameraSpecs
          @camera_spec.assign_attributes({
            camerable_id: @mission.id,
            camerable_type: &quot;Mission&quot;
          })
  
          # Try to save any CameraSpecs
          unless @camera_spec.save!
            raise ActiveRecord::Rollback, @camera_spec.errors.full_messages
          end
  
          # Add the mission id to the DroneSpecs
          @drone_spec.assign_attributes({
            droneable_id: @mission.id,
            droneable_type: &quot;Mission&quot;
          })
  
          # Try to save any DroneSpecs
          unless @drone_spec.save!
            raise ActiveRecord::Rollback, @drone_spec.errors.full_messages
          end
  
        # If something goes wrong, render :new again
        # rescue ActiveRecord::Rollback =&gt; e
        rescue =&gt; e
          # Ensure validation messages exist on each instance variable
          @user = current_user
          @addresses = @user.addresses
          @phones = @user.phones
          @mission.valid?
          @address.valid?
          @phone.valid?
          @camera_spec.valid?
          @drone_spec.valid?
  
          render :new and return
        else
          # Everything is good, so redirect to the show page
          redirect_to mission_path(@mission), notice: t(&quot;.mission_created&quot;)
        end
      end
    end
  </code></pre>