Class: CounterCulture::Reconciler::Reconciliation

Inherits:
Object
  • Object
show all
Defined in:
lib/counter_culture/reconciler.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(counter, changes_holder, options, relation_class) ⇒ Reconciliation

Returns a new instance of Reconciliation.



63
64
65
66
67
# File 'lib/counter_culture/reconciler.rb', line 63

def initialize(counter, changes_holder, options, relation_class)
  @counter, @options, = counter, options
  @relation_class = relation_class
  @changes_holder = changes_holder
end

Instance Attribute Details

#counterObject (readonly)

Returns the value of attribute counter.



58
59
60
# File 'lib/counter_culture/reconciler.rb', line 58

def counter
  @counter
end

#optionsObject (readonly)

Returns the value of attribute options.



58
59
60
# File 'lib/counter_culture/reconciler.rb', line 58

def options
  @options
end

#relation_classObject (readonly)

Returns the value of attribute relation_class.



58
59
60
# File 'lib/counter_culture/reconciler.rb', line 58

def relation_class
  @relation_class
end

Instance Method Details

#performObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/counter_culture/reconciler.rb', line 69

def perform
  log "Performing reconciling of #{counter.model}##{counter.relation.to_sentence}."
  # 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

  counter_column_names = column_names || {nil => counter_cache_name}

  if options[:column_name]
    counter_column_names = counter_column_names.select{ |_, v| options[:column_name].to_s == v }
  end

  # 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

    relation_class_table_name = quote_table_name(relation_class.table_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(relation_class)}, " \
      "#{count_select} AS count, " \
      "MAX(#{relation_class_table_name}.#{column_name}) AS #{column_name}"
    )

    # we need to join together tables until we get back to the table this class itself lives in
    join_clauses(where).each do |join|
      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 = options.fetch(:batch_size, CounterCulture.config.batch_size)

    counts_query = counts_query.where(options[:where]).group(full_primary_key(relation_class))

    find_in_batches_args = { batch_size: batch_size }
    find_in_batches_args[:start] = options[:start] if options[:start].present?
    find_in_batches_args[:finish] = options[:finish] if options[:finish].present?

    counts_query.find_in_batches(**find_in_batches_args).with_index(1) do |records, index|
      log "Processing batch ##{index}."
      # now iterate over all the models and see whether their counts are right
      update_count_for_batch(column_name, records)
      log "Finished batch ##{index}."
    end
  end
  log_without_newline "\n"
  log "Finished reconciling of #{counter.model}##{counter.relation.to_sentence}."
end