From bfe6a759c25ad02b4bfcb2dd16999d8ba72e2df8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 16 Jun 2005 05:35:10 +0000 Subject: Added actual database-changing behavior to collection assigment for has_many and has_and_belongs_to_many #1425 [Sebastian Kanthak] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1428 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/lib/active_record/associations.rb | 18 +++++++++++++++++- .../associations/association_collection.rb | 16 +++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 93960f87e4..65549aca0c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -200,6 +200,8 @@ module ActiveRecord # * collection<<(object, ...) - adds one or more objects to the collection by setting their foreign keys to the collection's primary key. # * collection.delete(object, ...) - removes one or more objects from the collection by setting their foreign keys to NULL. # This will also destroy the objects if they're declared as belongs_to and dependent on this model. + # * collection=objects - replaces the collections content by deleting and adding objects as appropriate. + # * collection_singular_ids=ids - replace the collection by the objects identified by the primary keys in +ids+ # * collection.clear - removes every object from the collection. This does not destroy the objects. # * collection.empty? - returns true if there are no associated objects. # * collection.size - returns the number of associated objects. @@ -215,6 +217,8 @@ module ActiveRecord # * Firm#clients (similar to Clients.find :all, :conditions => "firm_id = #{id}") # * Firm#clients<< # * Firm#clients.delete + # * Firm#clients= + # * Firm#client_ids= # * Firm#clients.clear # * Firm#clients.empty? (similar to firm.clients.size == 0) # * Firm#clients.size (similar to Client.count "firm_id = #{id}") @@ -463,6 +467,8 @@ module ActiveRecord # (collection.concat_with_attributes is an alias to this method). # * collection.delete(object, ...) - removes one or more objects from the collection by removing their associations from the join table. # This does not destroy the objects. + # * collection=objects - replaces the collections content by deleting and adding objects as appropriate. + # * collection_singular_ids=ids - replace the collection by the objects identified by the primary keys in +ids+ # * collection.clear - removes every object from the collection. This does not destroy the objects. # * collection.empty? - returns true if there are no associated objects. # * collection.size - returns the number of associated objects. @@ -474,6 +480,8 @@ module ActiveRecord # * Developer#projects<< # * Developer#projects.push_with_attributes # * Developer#projects.delete + # * Developer#projects= + # * Developer#project_ids= # * Developer#projects.clear # * Developer#projects.empty? # * Developer#projects.size @@ -634,6 +642,10 @@ module ActiveRecord association.replace(new_value) association end + + define_method("#{Inflector.singularize(association_name)}_ids=") do |new_value| + send("#{association_name}=", association_class_name.constantize.find(new_value)) + end end def require_association_class(class_name) @@ -687,7 +699,11 @@ module ActiveRecord instance_variable_set("@#{association_name}", association) end - association.send(constructor, attributees, replace_existing) + if association_proxy_class == HasOneAssociation + association.send(constructor, attributees, replace_existing) + else + association.send(constructor, attributees) + end end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 1eac8a2a0b..4495d92151 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -1,3 +1,5 @@ +require 'set' + module ActiveRecord module Associations class AssociationCollection < AssociationProxy #:nodoc: @@ -83,11 +85,19 @@ module ActiveRecord collection.inject([]) { |uniq_records, record| uniq_records << record unless uniq_records.include?(record); uniq_records } end + # Replace this collection with +other_array+ + # This will perform a diff and delete/add only records that have changed. def replace(other_array) - other_array.each{ |val| raise_on_type_mismatch(val) } + other_array.each { |val| raise_on_type_mismatch(val) } + + load_target + other = other_array.size < 100 ? other_array : other_array.to_set + current = @target.size < 100 ? @target : @target.to_set - @target = other_array - @loaded = true + @owner.transaction do + delete(@target.select { |v| !other.include?(v) }) + concat(other_array.select { |v| !current.include?(v) }) + end end private -- cgit v1.2.3