Ok, so there were multiple problems with my code. I've weeded through them all and wanted to post the final working solution, in case someone else is dealing with has_many through associations and nested forms.
My final models look like this (using Rails 3.2.13):
Company.rb
class Company < ActiveRecord::Base
attr_accessible :locations_attributes, :contacts_attributes, :company_name
has_many :locations, dependent: :destroy
has_many :addresses, through: :locations
has_many :contacts
accepts_nested_attributes_for :locations, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :addresses
accepts_nested_attributes_for :contacts
end
Location.rb
class Location < ActiveRecord::Base
attr_accessible :address_attributes, :address, :created_by, :is_active, :location_name, :location_type, :region_id, :updated_by, :website
belongs_to :company
belongs_to :address
accepts_nested_attributes_for :address, :reject_if => :all_blank
end
Address.rb
class Address < ActiveRecord::Base
attr_accessible :address_line_1, :address_line_2, :address_line_3, :address_line_4, :address_line_5, :city, :country, :county, :created_by, :province, :state, :updated_by, :zipcode
has_many :locations, dependent: :destroy
has_many :companies, through: :location
accepts_nested_attributes_for :locations
end
companies_controller.rb
class CompaniesController < ApplicationController
def new
@company = Company.new
@location= @company.locations.build
@address = @company.addresses.build
@contact = @company.contacts.build
end
def show
@company = Company.find(params[:id])
end
def create
@company = Company.new(params[:company])
if @company.save
#handle a successful save
flash[:success] = "Company Created Successfully"
redirect_to @company
else
render 'new'
end
end
end
new.html.erb
<% provide(:title, 'Create Company')%>
<h1>Create Company</h1>
<div class="container">
<%= form_for(@company) do |f| %>
<% if @company.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@company.errors.count, "error") %> prohibited this company from being saved:</h2>
<ul>
<% @company.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<%= f.label :company_name, "Company Name" %>
<%= f.text_field :company_name %>
<hr>
</div>
<div class="row">
<div class="col-md-6"><h3>Primary Location</h3><hr></div>
<div class="col-md-6"><h3>Location Information</h3><hr></div>
</div>
<%= f.fields_for(:locations) do |lf| %>
<%= lf.fields_for(:address_attributes) do |af| %>
<%= f.fields_for(:contacts) do |cf| %>
<div class="row" >
<div class="col-md-6"><%= af.label "Address 1"%><%= af.text_field :address_line_1 %></div>
<div class="col-md-6"><%= lf.label "Location Type" %> <%= lf.select(:location_type, options_for_select([["Headquarters",11], ["Office", 12]])) %></div>
</div>
<div class="row" >
<div class="col-md-6"><%= af.label "Address 2"%><%= af.text_field :address_line_2 %></div>
<div class="col-md-6"><%= lf.label "Location Name" %> <%= lf.text_field :location_name %></div>
</div>
<div class="row" >
<div class="col-md-6"><%= af.label "Address 3"%><%= af.text_field :address_line_3 %></div>
<div class="col-md-6"><h3>Other Location Information</h3><hr></div>
</div>
<div class="row" >
<div class="col-md-6"><%= af.label "City"%><%= af.text_field :city %></div>
<div class="col-md-3"><button type="button" class="btn btn-primary btn-small btn-block">Services Offered</button></div>
</div>
<div class="row" >
<div class="col-md-6"><%= af.label "State"%><%= af.select(:state, options_for_select([["Alabama","AL"], ["Alaska","AK"]])) %></div>
<div class="col-md-3"><button type="button" class="btn btn-primary btn-small btn-block">Materials Accepted</button></div>
</div>
<div class="row" >
<div class="col-md-6"><%= af.label "Country"%><%= af.select(:country, options_for_select([["United States","USA"], ["United Kingdom","UK"]])) %></div>
<div class="col-md-3"><button type="button" class="btn btn-primary btn-small btn-block">Location Certifications</button></div>
</div>
<div class="row" >
<div class="col-md-6"><%= af.label "Zipcode"%><%= af.text_field :zipcode %></div>
</div>
<div class="row" >
<div class="col-md-12"><h3>Tell us about you</h3></div>
<hr>
</div>
<div class="row">
<div class="col-md-6"><%= cf.label "First Name"%><%= cf.text_field :first_name %></div>
</div>
<div class="row">
<div class="col-md-6"><%= cf.label "Last Name"%><%= cf.text_field :last_name %></div>
</div>
<div class="row">
<div class="col-md-6"><%= cf.label "Title"%><%= cf.text_field :title %></div>
</div>
<div class="row">
<div class="col-md-2"><%= f.submit "Save Info", class: "btn btn-small btn-primary" %></div>
</div>
<% end %>
<% end %>
<% end%>
<% end %>
The things to notice are how the accepts_nested_attributes_for statements link up the associations, the need to add the appropriate model_attributes in each models attr_accessible statement, and how the form nests the address form (af) within the locations_form (lf) using :addresses_attributes.
Additionally, I had to remove foreign Key (company_id, address_id) validations from Location.rb because they caused the transaction to rollback prior to the Address or Company record being created (a prereq for creating a location)