class ActiveRecord::Base
def self.synchronize(instances, keys = [primary_key])
posts.first.address # => "1245 Foo Ln" instead of whatever it was
Post.synchronize posts, [:name] # queries on the :name column and not the :id column
<.. out of system changes occur to change the address of author 'Zach' to 1245 Foo Ln ..>
posts = Post.where(author: "Zach").first
# Synchronizing using custom key fields
posts.first.author # => "Zachary" instead of Zach
Post.synchronize posts
<.. out of system changes occur to change author name from Zach to Zachary..>
posts = Post.where(author: "Zach").first
# Synchronizing existing models by matching on the primary key field
== Examples
instances rather sending one query for each instance
This uses one query for all instance updates and then updates existing
ActiveRecord instance but it is intended for use on multiple instances.
from the database. This is like calling reload on an individual
Synchronizes the passed in ActiveRecord instances with data
:nodoc:
:nodoc:
def self.synchronize(instances, keys = [primary_key]) return if instances.empty? conditions = {} 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.where(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 next unless matched_instance instance.send :clear_aggregation_cache instance.send :clear_association_cache instance.instance_variable_set :@attributes, matched_instance.instance_variable_get(:@attributes) if instance.respond_to?(:clear_changes_information) instance.clear_changes_information # Rails 4.2 and higher else instance.instance_variable_set :@attributes_cache, {} # Rails 4.0, 4.1 instance.changed_attributes.clear # Rails 3.2 instance.previous_changes.clear end # Since the instance now accurately reflects the record in # the database, ensure that instance.persisted? is true. instance.instance_variable_set '@new_record', false instance.instance_variable_set '@destroyed', false end end