HABTM join table not being updated

I’ve been working with the Advanced Railes Recipies PDF tutorial but
seem to have hit a snag. When adding a HABTM element, locations in
place of tasks in my usage, the location (address) is added to the
locations table; however, the join is not added to my
locations_profiles table (profile_id,location_id). Would I need to do
something more than just @profile.save in the profiles controller for
this to be saved properly?

I will post code when I get home but it is essentially the same as the
tutorial (which doesn’t mention HABTM associations unfortunetally).

As I stated before, the location is added to the locations table and
the profile is added to the profiles table (if new). The problem is
that the join table is not updated to match the association.

Here is my profiles controller:

GET /profiles/new

def new
@devices = Device.find(:all)
@profile = Profile.new
@profile.websites.build # creates first website fieldset
@profile.numbers.build # creates first number fieldset
@profile.emails.build # creates first email fieldset
@profile.locations.build # creates first location fieldset
@profile.notes.build # creates first note fieldset
end

GET /profiles/1/edit

def edit
@profile = Profile.find(params[:id])
end

POST /profiles

def create
@profile = Profile.new(params[:profile])

if @profile.save
  flash[:notice] = "Successfully created profile!"
  redirect_to profile_path(@profile)
else
  render :action => 'new'
end

end

PUT /profiles/1

def update
params[:profile][:existing_email_attributes] ||= {}
params[:profile][:existing_website_attributes] ||= {}
params[:profile][:existing_location_attributes] ||= {}
params[:profile][:existing_number_attributes] ||= {}
params[:profile][:existing_note_attributes] ||= {}

@profile = Profile.find(params[:id])

if @profile.update_attributes(params[:profile])
  flash[:notice] = "Successfully updated profile!"
  redirect_to profile_path(@profile)
else
  render :action => 'edit'
end

end

profile.rb

class Profile < ActiveRecord::Base

after_update :save_numbers
after_update :save_emails
after_update :save_locations
after_update :save_websites
after_update :save_notes

belongs_to :user
belongs_to :type
has_many :numbers,
:dependent => :delete_all
has_many :emails,
:dependent => :delete_all
has_many :websites,
:dependent => :delete_all
has_many :notes,
:dependent => :delete_all
has_and_belongs_to_many :locations

Emails

def new_email_attributes=(email_attributes)
email_attributes.each do |attributes|
emails.build(attributes)
end
end
def existing_email_attributes=(email_attributes)
emails.reject(&:new_record?).each do |email|
attributes = email_attributes[email.id.to_s]
if attributes
email.attributes = attributes
else
emails.delete(email)
end
end
end
def save_emails
emails.each do |email|
email.save(false)
end
end

Numbers

def new_number_attributes=(number_attributes)
number_attributes.each do |attributes|
numbers.build(attributes)
end
end
def existing_number_attributes=(number_attributes)
numbers.reject(&:new_record?).each do |number|
attributes = number_attributes[number.id.to_s]
if attributes
number.attributes = attributes
else
numbers.delete(number)
end
end
end
def save_numbers
numbers.each do |number|
number.save(false)
end
end

Websites

def new_website_attributes=(website_attributes)
website_attributes.each do |attributes|
websites.build(attributes)
end
end
def existing_website_attributes=(website_attributes)
websites.reject(&:new_record?).each do |website|
attributes = website_attributes[website.id.to_s]
if attributes
website.attributes = attributes
else
websites.delete(website)
end
end
end
def save_websites
websites.each do |website|
website.save(false)
end
end

Locations

def new_location_attributes=(location_attributes)
location_attributes.each do |attributes|
locations.build(attributes)
end
end
def existing_location_attributes=(location_attributes)
locations.reject(&:new_record?).each do |location|
attributes = location_attributes[location.id.to_s]
if attributes
location.attributes = attributes
else
locations.delete(location)
end
end
end
def save_locations
locations.each do |location|
location.save(false)
end
end

Notes

def new_note_attributes=(note_attributes)
note_attributes.each do |attributes|
notes.build(attributes)
end
end
def existing_note_attributes=(note_attributes)
notes.reject(&:new_record?).each do |note|
attributes = note_attributes[note.id.to_s]
if attributes
note.attributes = attributes
else
notes.delete(note)
end
end
end
def save_notes
notes.each do |note|
note.save(false)
end
end

end

location.rb
class Location < ActiveRecord::Base

belongs_to :state
has_and_belongs_to_many :profiles

validates_presence_of :address_line_1,
:message => “If you do not wish to add this
location,
please click remove.”
validates_presence_of :city,
:message => “If you do not wish to add this
location,
please click remove.”
validates_presence_of :state_id,
:message => “If you do not wish to add this
location,
please click remove.”
validates_numericality_of :zip,
:allow_nil => false,
:message => “Please enter a zip code
consisting of
numbers only”
end

_location.html.erb

<% new_or_existing = location.new_record? ? 'new' : 'existing' %> <% prefix = "profile[#{new_or_existing}_location_attributes][]" %> <% fields_for prefix, location do |location_form| -%>

Address Line 1: <%= location_form.text_field :address_line_1 %>
Address Line 2: <%= location_form.text_field :address_line_2 %>
City: <%= location_form.text_field :city %>
State: <%= location_form.select :state_id, State.find(:all).collect {|p| [ p.name, p.id ] }, { :prompt => "Select a state" } %> Zip Code: <%= location_form.text_field :zip %>
Description: <%= location_form.text_field :description %>
<%= link_to_function "remove" , "$(this).up('.location').remove()" %>

<% end -%>

_form.html.erb
<%= f.hidden_field :id %>

First name: <%= f.text_field :first_name %>

Last name: <%= f.text_field :last_name %>

Title: <%= f.text_field :title %>

Company: <%= f.text_field :company %>

User: <%= f.text_field :user_id %>

Type: <%= f.text_field :type_id %>

Websites

<%= render :partial => 'website', :collection => @profile.websites %>
<%= add_website_link "Add a Website" %>

Numbers

<%= render :partial => 'number', :collection => @profile.numbers %>
<%= add_number_link "Add a Number" %>

Emails

<%= render :partial => 'email', :collection => @profile.emails %>
<%= add_email_link "Add an Email" %>

Locations

<%= render :partial => 'location', :collection => @profile.locations %>
<%= add_location_link "Add a Location" %>

Notes

<%= render :partial => 'note', :collection => @profile.notes %>
<%= add_note_link "Add a Note" %>

profile helper
module ProfilesHelper

def add_website_link(name)
link_to_function name do |page|
page.insert_html :bottom, :websites, :partial =>
‘website’ , :object => Website.new
end
end

def add_number_link(name)
link_to_function name do |page|
page.insert_html :bottom, :numbers, :partial => ‘number’ , :object
=> Number.new
end
end

def add_email_link(name)
link_to_function name do |page|
page.insert_html :bottom, :emails, :partial => ‘email’ , :object
=>
Email.new
end
end

def add_location_link(name)
link_to_function name do |page|
page.insert_html :bottom, :locations, :partial =>
‘location’ , :object => Location.new
end
end

def add_note_link(name)
link_to_function name do |page|
page.insert_html :bottom, :notes, :partial => ‘note’ , :object =>
Note.new
end
end

end