aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/has_many_through_association.rb
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2011-02-14 23:14:42 +0000
committerJon Leighton <j@jonathanleighton.com>2011-02-18 00:00:13 +0000
commit91fd6510563f84ee473bb217bc63ed598abe3f24 (patch)
tree8d9a4996e5a7b9663e8bd7869d62c9701e5385de /activerecord/lib/active_record/associations/has_many_through_association.rb
parentf0b98050296b57d95dbc789f8e52fa82499d151a (diff)
downloadrails-91fd6510563f84ee473bb217bc63ed598abe3f24.tar.gz
rails-91fd6510563f84ee473bb217bc63ed598abe3f24.tar.bz2
rails-91fd6510563f84ee473bb217bc63ed598abe3f24.zip
Allow building and then later saving has_many :through records, such that the join record is automatically saved too. This requires the :inverse_of option to be set on the source association in the join model. See the CHANGELOG for details. [#4329 state:resolved]
Diffstat (limited to 'activerecord/lib/active_record/associations/has_many_through_association.rb')
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb50
1 files changed, 46 insertions, 4 deletions
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 9f74d57c4d..c4d3ef8fef 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -37,16 +37,44 @@ module ActiveRecord
def insert_record(record, validate = true)
return if record.new_record? && !record.save(:validate => validate)
-
- through_association = @owner.send(@reflection.through_reflection.name)
- through_association.create!(construct_join_attributes(record))
-
+ through_record(record).save!
update_counter(1)
record
end
private
+ def through_record(record)
+ through_association = @owner.send(:association_proxy, @reflection.through_reflection.name)
+ attributes = construct_join_attributes(record)
+
+ through_record = Array.wrap(through_association.target).find { |candidate|
+ candidate.attributes.slice(*attributes.keys) == attributes
+ }
+
+ unless through_record
+ through_record = through_association.build(attributes)
+ through_record.send("#{@reflection.source_reflection.name}=", record)
+ end
+
+ through_record
+ end
+
+ def build_record(attributes)
+ record = super(attributes)
+
+ inverse = @reflection.source_reflection.inverse_of
+ if inverse
+ if inverse.macro == :has_many
+ record.send(inverse.name) << through_record(record)
+ elsif inverse.macro == :has_one
+ record.send("#{inverse.name}=", through_record(record))
+ end
+ end
+
+ record
+ end
+
def target_reflection_has_associated_record?
if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.foreign_key].blank?
false
@@ -79,6 +107,8 @@ module ActiveRecord
count = scope.delete_all
end
+ delete_through_records(through, records)
+
if @reflection.through_reflection.macro == :has_many && update_through_counter?(method)
update_counter(-count, @reflection.through_reflection)
end
@@ -86,6 +116,18 @@ module ActiveRecord
update_counter(-count)
end
+ def delete_through_records(through, records)
+ if @reflection.through_reflection.macro == :has_many
+ records.each do |record|
+ through.target.delete(through_record(record))
+ end
+ else
+ records.each do |record|
+ through.target = nil if through.target == through_record(record)
+ end
+ end
+ end
+
def find_target
return [] unless target_reflection_has_associated_record?
scoped.all