Added :synchronize_keys to the import options so importing can synchronize on fields beside the primary key.

https://github.com/zdennis/activerecord-import/issues/16
This commit is contained in:
Zach Dennis 2011-04-06 14:20:53 -04:00
parent e00e9d7d59
commit 7958ed18c2
3 changed files with 43 additions and 10 deletions

View file

@ -212,7 +212,8 @@ class ActiveRecord::Base
end end
if options[:synchronize] if options[:synchronize]
synchronize( options[:synchronize] ) sync_keys = options[:synchronize_keys] || [self.primary_key]
synchronize( options[:synchronize], sync_keys)
end end
return_obj.num_inserts = 0 if return_obj.num_inserts.nil? return_obj.num_inserts = 0 if return_obj.num_inserts.nil?

View file

@ -8,17 +8,29 @@ module ActiveRecord # :nodoc:
# #
# This uses one query for all instance updates and then updates existing # This uses one query for all instance updates and then updates existing
# instances rather sending one query for each instance # instances rather sending one query for each instance
def self.synchronize(instances, key=self.primary_key) def self.synchronize(instances, keys=[self.primary_key])
return if instances.empty? return if instances.empty?
keys = instances.map(&"#{key}".to_sym)
klass = instances.first.class
fresh_instances = klass.find( :all, :conditions=>{ key=>keys }, :order=>"#{key} ASC" )
instances.each_with_index do |instance, index| conditions = {}
instance.clear_aggregation_cache order = ""
instance.clear_association_cache
instance.instance_variable_set '@attributes', fresh_instances[index].attributes key_values = keys.map { |key| instances.map(&"#{key}".to_sym) }
keys.zip(key_values).each { |key, values| conditions[key] = values }
order = keys.map{ |key| "#{key} ASC" }.join(",")
klass = instances.first.class
fresh_instances = klass.find( :all, :conditions=>conditions, :order=>order )
instances.each do |instance|
matched_instance = fresh_instances.detect do |fresh_instance|
keys.all?{ |key| fresh_instance.send(key) == instance.send(key) }
end
if matched_instance
instance.clear_aggregation_cache
instance.clear_association_cache
instance.instance_variable_set '@attributes', matched_instance.attributes
end
end end
end end

View file

@ -66,6 +66,26 @@ describe "#import" do
end end
end end
context "with :synchronize option" do
context "synchronizing on new records" do
let(:new_topics) { Build(3, :topics) }
it "doesn't reload any data (doesn't work)" do
Topic.import new_topics, :synchronize => new_topics
assert new_topics.all?(&:new_record?), "No record should have been reloaded"
end
end
context "synchronizing on new records with explicit conditions" do
let(:new_topics) { Build(3, :topics) }
it "reloads data for existing in-memory instances" do
Topic.import(new_topics, :synchronize => new_topics, :synchronize_key => [:title] )
assert new_topics.all?(&:new_record?), "Records should have been reloaded"
end
end
end
context "with an array of unsaved model instances" do context "with an array of unsaved model instances" do
let(:topic) { Build(:topic, :title => "The RSpec Book", :author_name => "David Chelimsky")} let(:topic) { Build(:topic, :title => "The RSpec Book", :author_name => "David Chelimsky")}
let(:topics) { Build(9, :topics) } let(:topics) { Build(9, :topics) }