lib/enumerate_by.rb in enumerate_by-0.4.3 vs lib/enumerate_by.rb in enumerate_by-0.4.4

- old
+ new

@@ -15,11 +15,15 @@ def self.extended(base) #:nodoc: base.class_eval do # Tracks which associations are backed by an enumeration # {"foreign key" => "association name"} class_inheritable_accessor :enumeration_associations - self.enumeration_associations = {} + + # Fix existing models not getting the default value + ([self] + subclasses).each do |model| + model.enumeration_associations = {} + end end end # Indicates that this class is an enumeration. # @@ -176,11 +180,11 @@ # This allows for enumerations to permanently cache find queries, avoiding # unnecessary lookups in the database. [:find_by_sql, :exists?, :calculate].each do |method| define_method(method) do |*args| if EnumerateBy.perform_caching && perform_enumerator_caching - enumerator_cache_store.fetch([method] + args) { super(*args) } + shallow_clone(enumerator_cache_store.fetch([method] + args) { super(*args) }) else super(*args) end end end @@ -207,10 +211,20 @@ enumerator else enumerator.is_a?(Symbol) ? enumerator.to_s : enumerator end end + + # Generates a copy of the given record(s), keeping intact the original id + def shallow_clone(result) + case result + when Array + result.map {|item| shallow_clone(item)} + when ActiveRecord::Base + result.class.send(:instantiate, result.instance_variable_get(:@attributes)) + end + end end module Bootstrapped # Synchronizes the given records with existing ones. This ensures that # only the correct and most up-to-date records exist in the database. @@ -263,33 +277,35 @@ # existing records in the database. However, the +html+ attribute will # only be synchronized if the attribute is nil in the database. # Otherwise, any changes to that column remain there. def bootstrap(*records) uncached do + primary_key = self.primary_key.to_sym + # Remove records that are no longer being used records.flatten! - ids = records.map {|record| record[:id]}.compact - delete_all(ids.any? ? ['id NOT IN (?)', ids] : nil) + ids = records.map {|record| record[primary_key]}.compact + delete_all(ids.any? ? ["#{primary_key} NOT IN (?)", ids] : nil) # Find remaining existing records (to be updated) - existing = all.inject({}) {|existing, record| existing[record.id] = record; existing} + existing = all.inject({}) {|existing, record| existing[record.send(primary_key)] = record; existing} records.map! do |attributes| attributes.symbolize_keys! defaults = attributes.delete(:defaults) # Update with new attributes record = - if record = existing[attributes[:id]] + if record = existing[attributes[primary_key]] attributes.merge!(defaults.delete_if {|attribute, value| record.send("#{attribute}?")}) if defaults record.attributes = attributes record else attributes.merge!(defaults) if defaults new(attributes) end - record.id = attributes[:id] + record.send("#{primary_key}=", attributes[primary_key]) # Force failed saves to stop execution record.save! record end @@ -315,30 +331,32 @@ # This produces a significant performance increase when bootstrapping more # than several hundred records. # # See EnumerateBy::Bootstrapped#bootstrap for information about usage. def fast_bootstrap(*records) + primary_key = self.primary_key.to_sym + # Remove records that are no longer being used records.flatten! - ids = records.map {|record| record[:id]}.compact - delete_all(ids.any? ? ['id NOT IN (?)', ids] : nil) + ids = records.map {|record| record[primary_key]}.compact + delete_all(ids.any? ? ["#{primary_key} NOT IN (?)", ids] : nil) # Find remaining existing records (to be updated) quoted_table_name = self.quoted_table_name - existing = connection.select_all("SELECT * FROM #{quoted_table_name}").inject({}) {|existing, record| existing[record['id'].to_i] = record; existing} + existing = connection.select_all("SELECT * FROM #{quoted_table_name}").inject({}) {|existing, record| existing[record[primary_key.to_s].to_i] = record; existing} records.each do |attributes| attributes.stringify_keys! if defaults = attributes.delete('defaults') defaults.stringify_keys! end - id = attributes['id'] + id = attributes[primary_key.to_s] if existing_attributes = existing[id] # Record exists: Update attributes - attributes.delete('id') + attributes.delete(primary_key.to_s) attributes.merge!(defaults.delete_if {|attribute, value| !existing_attributes[attribute].nil?}) if defaults - update_all(attributes, :id => id) + update_all(attributes, primary_key => id) else # Record doesn't exist: create new one attributes.merge!(defaults) if defaults column_names = [] values = []