Class: CounterCulture::Reconciler
- Inherits:
-
Object
- Object
- CounterCulture::Reconciler
- Defined in:
- lib/counter_culture/reconciler.rb
Instance Attribute Summary collapse
-
#changes ⇒ Object
readonly
Returns the value of attribute changes.
-
#counter ⇒ Object
readonly
Returns the value of attribute counter.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
-
#initialize(counter, options = {}) ⇒ Reconciler
constructor
A new instance of Reconciler.
- #reconcile! ⇒ Object
Constructor Details
#initialize(counter, options = {}) ⇒ Reconciler
Returns a new instance of Reconciler.
11 12 13 14 15 16 |
# File 'lib/counter_culture/reconciler.rb', line 11 def initialize(counter, ={}) @counter, = counter, @changes = [] @reconciled = false end |
Instance Attribute Details
#changes ⇒ Object (readonly)
Returns the value of attribute changes.
6 7 8 |
# File 'lib/counter_culture/reconciler.rb', line 6 def changes @changes end |
#counter ⇒ Object (readonly)
Returns the value of attribute counter.
6 7 8 |
# File 'lib/counter_culture/reconciler.rb', line 6 def counter @counter end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
6 7 8 |
# File 'lib/counter_culture/reconciler.rb', line 6 def end |
Instance Method Details
#reconcile! ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/counter_culture/reconciler.rb', line 18 def reconcile! return false if @reconciled if [:skip_unsupported] return false if (foreign_key_values || (counter_cache_name.is_a?(Proc) && !column_names) || delta_magnitude.is_a?(Proc)) else raise "Fixing counter caches is not supported when using :foreign_key_values; you may skip this relation with :skip_unsupported => true" if foreign_key_values raise "Must provide :column_names option for relation #{relation.inspect} when :column_name is a Proc; you may skip this relation with :skip_unsupported => true" if counter_cache_name.is_a?(Proc) && !column_names raise "Fixing counter caches is not supported when :delta_magnitude is a Proc; you may skip this relation with :skip_unsupported => true" if delta_magnitude.is_a?(Proc) end # if we're provided a custom set of column names with conditions, use them; just use the # column name otherwise # which class does this relation ultimately point to? that's where we have to start scope = relation_class # respect the deleted_at column if it exists scope = scope.where("#{model.table_name}.deleted_at IS NULL") if model.column_names.include?('deleted_at') counter_column_names = column_names || {nil => counter_cache_name} # iterate over all the possible counter cache column names counter_column_names.each do |where, column_name| # if the column name is nil, that means those records don't affect # counts; we don't need to do anything in that case. but we allow # specifying that condition regardless to make the syntax less # confusing next unless column_name # select join column and count (from above) as well as cache column ('column_name') for later comparison counts_query = scope.select("#{relation_class.table_name}.#{relation_class.primary_key}, #{relation_class.table_name}.#{relation_reflect(relation).association_primary_key}, #{count_select} AS count, #{relation_class.table_name}.#{column_name}") # we need to join together tables until we get back to the table this class itself lives in # conditions must also be applied to the join on which we are counting join_clauses.each_with_index do |join,index| if index == join_clauses.size - 1 && where join += " AND (#{model.send(:sanitize_sql_for_conditions, where)})" end counts_query = counts_query.joins(join) end # iterate in batches; otherwise we might run out of memory when there's a lot of # instances and we try to load all their counts at once batch_size = .fetch(:batch_size, CounterCulture.config.batch_size) counts_query.group(full_primary_key(relation_class)).find_in_batches(batch_size: batch_size) do |records| # now iterate over all the models and see whether their counts are right ActiveRecord::Base.transaction do records.each do |record| count = record.read_attribute('count') || 0 next if record.read_attribute(column_name) == count track_change(record, column_name, count) # use update_all because it's faster and because a fixed counter-cache shouldn't update the timestamp relation_class.where(relation_class.primary_key => record.send(relation_class.primary_key)).update_all(column_name => count) end end end end @reconciled = true end |