views:

323

answers:

2

I created a simple example as a sanity check and still can not seem to destroy an item on either side of a has_and_belongs_to_many relationship in rails.

Whenever I try to delete an object from either table, I get the dreaded NameError / "uninitialized constant" error message.

To demonstrate, I created a sample rails app with a Boy class and Dog class. I used the basic scaffold for each and created a linking table called boys_dogs. I then added a simple before_save routine to create a new 'dog' any time a boy was created and establish a relationship, just to get things setup easily.

dog.rb

class Dog < ActiveRecord::Base  
  has_and_belongs_to_many :Boys  
end  

boy.rb

class Boy < ActiveRecord::Base  
  has_and_belongs_to_many :Dogs  

  def before_save  
    self.Dogs.build( :name => "Rover" )  
  end  

end  

schema.rb

ActiveRecord::Schema.define(:version => 20100118034401) do

  create_table "boys", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "boys_dogs", :id => false, :force => true do |t|
    t.integer  "boy_id"
    t.integer  "dog_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "dogs", :force => true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end

I've seen lots of posts here and elsewhere about similar problems, but the solutions are normally using belongs_to and the plural/singular class names being confused. I don't think that is the case here, but I tried switching the habtm statement to use the singular name just to see if it helped (with no luck). I seem to be missing something simple here.

The actual error message is:

NameError in BoysController#destroy
uninitialized constant Boy::Dogs

The trace looks like:

/Library/Ruby/Gems/1.8/gems/activesupport-2.3.4/lib/active_support/dependencies.rb:105:in const_missing'
(eval):3:in
destroy_without_callbacks'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/callbacks.rb:337:in destroy_without_transactions'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:229:in
send'
...

Thanks.

+2  A: 

I don't see your destroy callback, but I do see a couple of problems. First, your associations need to be lowercase. So dog.rb should be:

class Dog < ActiveRecord::Base  
  has_and_belongs_to_many :boys  
end  

and boy.rb should be:

class Boy < ActiveRecord::Base  
  has_and_belongs_to_many :dogs  

  def before_save  
    self.dogs.build( :name => "Rover" )  
  end
end

Second, I believe you want to use self.dogs.create instead of self.dogs.build above, since build won't actually save the new dog object.

Jaime Bellmyer
Awesome. Thanks. Changing to lower case did the trick. Note: Using self.dogs.create won't work unless the boy object has already been saved. But I could probably move that to the after_save. The self.dogs.build command seems to work as is. Thanks again.
Bradley
Hi Bradley-Glad it helped. And I bet the model save is actually saving all the built associations too, which is something I didn't know before.I noticed you're new to the site. It helps me out if you accept my answer, since it was the one that solved your problem. That way, I get credit for it.Thanks,Jaime
Jaime Bellmyer
A: 

The accepted answer here solved my problem, only to create another one.

Here are my model objects:

class Complex < ActiveRecord::Base
   set_table_name "Complexes"
   set_primary_key "ComplexID"
   has_and_belongs_to_many :amenities
end

class Amenity < ActiveRecord::Base
   set_table_name "Amenities"
   set_primary_key "AmenityID"
end

Rails uses the name of the association as the table name when creating the select query. My application runs on Unix against a legacy MySQL database and my table names are case-sensitive and don't conform to Rails conventions. Whenever my app actually tried to load the association, I would get an exception that MySQL couldn't find table amenities:

SELECT * FROM `amenities` 
INNER JOIN `ComplexAmenities` ON `amenities`.AmenityID = `ComplexAmenities`.AmenityID  
WHERE (`ComplexAmenities`.ComplexID = 147 )

I searched and searched and could not find a way to tell Rails to use the correct case for the table name. Out of desperation, I tried passing a :table_name option to habtm and it worked. My new Complex model looks like this:

class Complex < ActiveRecord::Base
   set_table_name "Complexes"
   set_primary_key "ComplexID"
   has_and_belongs_to_many :amenities, :table_name => 'Amenities'
end

This works under Rails 2.3.5.

This option is not mentioned in the Ruby on Rails docs.

splattered bits