diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 571dc6d21a..e08612cc34 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Fix the has_and_belongs_to_many #create doesn't populate the join for new records. Closes #3692 [josh@hasmanythrough.com]
+
* Provide Association Extensions access to the instance that the association is being accessed from.
Closes #4433 [josh@hasmanythrough.com]
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index e1a0289039..ff10763cbf 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -763,6 +763,10 @@ module ActiveRecord
# * collection.size - returns the number of associated objects.
# * collection.find(id) - finds an associated object responding to the +id+ and that
# meets the condition that it has to be associated with this object.
+ # * collection.build(attributes = {}) - returns a new object of the collection type that has been instantiated
+ # with +attributes+ and linked to this object through the join table but has not yet been saved.
+ # * collection.create(attributes = {}) - returns a new object of the collection type that has been instantiated
+ # with +attributes+ and linked to this object through the join table and that has already been saved (if it passed the validation).
#
# Example: An Developer class declares has_and_belongs_to_many :projects, which will add:
# * Developer#projects
@@ -775,6 +779,8 @@ module ActiveRecord
# * Developer#projects.empty?
# * Developer#projects.size
# * Developer#projects.find(id)
+ # * Developer#projects.build (similar to Project.new("project_id" => id))
+ # * Developer#projects.create (similar to c = Project.new("project_id" => id); c.save; c)
# The declaration may include an options hash to specialize the behavior of the association.
#
# Options are:
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index cc6549f3ae..42cf549dcb 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -13,6 +13,17 @@ module ActiveRecord
record
end
+ def create(attributes = {})
+ # Can't use Base.create since the foreign key may be a protected attribute.
+ if attributes.is_a?(Array)
+ attributes.collect { |attr| create(attr) }
+ else
+ record = build(attributes)
+ insert_record(record) unless @owner.new_record?
+ record
+ end
+ end
+
def find_first
load_target.first
end
diff --git a/activerecord/test/associations_test.rb b/activerecord/test/associations_test.rb
index 3667f71238..c7092bb1f9 100755
--- a/activerecord/test/associations_test.rb
+++ b/activerecord/test/associations_test.rb
@@ -1354,6 +1354,20 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
devel.save
assert !proj.new_record?
assert_equal devel.projects.last, proj
+ assert_equal Developer.find(1).projects.last, proj # prove join table is updated
+ end
+
+ def test_build_by_new_record
+ devel = Developer.new(:name => "Marcel", :salary => 75000)
+ proj1 = devel.projects.build(:name => "Make bed")
+ proj2 = devel.projects.build(:name => "Lie in it")
+ assert_equal devel.projects.last, proj2
+ assert proj2.new_record?
+ devel.save
+ assert !devel.new_record?
+ assert !proj2.new_record?
+ assert_equal devel.projects.last, proj2
+ assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
def test_create
@@ -1361,6 +1375,20 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
proj = devel.projects.create("name" => "Projekt")
assert_equal devel.projects.last, proj
assert !proj.new_record?
+ assert_equal Developer.find(1).projects.last, proj # prove join table is updated
+ end
+
+ def test_create_by_new_record
+ devel = Developer.new(:name => "Marcel", :salary => 75000)
+ proj1 = devel.projects.create(:name => "Make bed")
+ proj2 = devel.projects.create(:name => "Lie in it")
+ assert_equal devel.projects.last, proj2
+ assert proj2.new_record?
+ devel.save
+ assert !devel.new_record?
+ assert !proj2.new_record?
+ assert_equal devel.projects.last, proj2
+ assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
def test_uniq_after_the_fact