diff options
author | Jon Leighton <j@jonathanleighton.com> | 2011-02-14 23:14:42 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2011-02-18 00:00:13 +0000 |
commit | 91fd6510563f84ee473bb217bc63ed598abe3f24 (patch) | |
tree | 8d9a4996e5a7b9663e8bd7869d62c9701e5385de /activerecord/lib/active_record/associations/has_many_through_association.rb | |
parent | f0b98050296b57d95dbc789f8e52fa82499d151a (diff) | |
download | rails-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.rb | 50 |
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 |