ruby-on-railsmodel-associationsacts-as-tenant

ActsAsTenant with tenantable nested attributes causes ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet


I'm working on the admin page for an application with a model called DealerBranch and a tenanted nested association called Address. I have a controller that looks like this for creating a new dealer branch:

class Admin::DealerBranchesController < Admin::AdminApplicationController
  def create
    @dealer_branch = DealerBranch.new(dealer_branch_attributes)
    if @dealer_branch.save
      render :success
    else
      render :new
    end
  end
end

When create runs it includes all of the attributes necessary to create the associated Address. However, the tenant for address is not yet created because we're building both the tenant (DealerBranch) and the associated tenanted (Address). On the line with the assignment to @dealer_branch I get the error ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet

What's the proper way of handling nested attributes like this?


Solution

  • This ended up being sort of a chicken and egg problem. Can't create the Address yet because it needs a DealerBranch, which Address needs to be belong to. The parent object DealerBranch has not been saved yet. In order for the nesting to work I created a create_with_address method which breaks it down to save the DealerBranch first:

      # We need this method to help create a DealerBranch with nested Address because address needs DealerBranch
      # to exist to satisfy ActsAsTenant
      def self.create_with_address(attributes)
        address_attributes = attributes.delete(:address_attributes)
        dealer_branch = DealerBranch.new(attributes)
    
        begin
          ActiveRecord::Base.transaction do
            dealer_branch.save!
            ActsAsTenant.with_tenant(dealer_branch) do
              dealer_branch.create_address!(address_attributes)
            end
          end
        rescue StandardError => e
          if dealer_branch.address.nil?
            # rebuild the attributes for display of form in case they were corrupted and became nil
            ActsAsTenant.with_tenant(dealer_branch) { dealer_branch.build_address(address_attributes) }
          end
    
          unless dealer_branch.valid? && dealer_branch.address.valid?
            return dealer_branch
          else
            raise e
          end
        end
    
        dealer_branch.reload
      end