Update the counter cache column after paranoid record restore (act_as_paranoid)

There is a column in my model counter_cache

. I use acts_as_paranoid

for this model ( Paranoia gem ). How do I update the counter cache column for a related record when restoring the record?

+3


source to share


3 answers


You can use a callback before_restore

. Place the .increment_counter

method
for the associated entry inside this callback before_restore

.



reset_counters

method
or += 1

won't work.

+1


source


Below is a typical solution to this problem.

You should already have the paranoid gem installed if you encounter this problem, but for completeness, include the paranoid gem in your Gemfile and install it using the "bundle" command.

# Gemfile
gem 'paranoid'

      

Create a problem.

# app/models/concerns/paranoid_deletable.rb
module ParanoidDeletable
  extend ActiveSupport::Concern

  included do
    # activate the paranoid behavior
    acts_as_paranoid
    # before restoring the record, manually increment the counter
    before_restore :increment_counter_cache
  end

    module ClassMethods

      def counter_column_name
        "#{self.name.underscore.pluralize}_count"
      end

    end

    def counter_associations
        associated_counters = []
        # get all belongs_to associations
        self.reflect_on_all_associations(:belongs_to).collect do |association|
          return unless association.options[:counter_cache]
          associated_klass_name = association.options[:polymorphic] ? self.send("#{association.name}_type") : association.class_name
          associated_klass_name.constantize.column_names.each do |column_name|
            # collect the association names and their classes if a counter cache column exists for this (self) class.
            associated_counters << { association_name: association.name, klass_name: associated_klass_name } if(column_name == self.class.counter_column_name)
          end
        end
        # return the array of { association_name, association_klass } hashes
        associated_counters
      end

    private

    def increment_counter_cache
      # before restore...
      self.counter_associations.each do |counter_association|
        association_name = counter_association[:association_name]
        klass_name = counter_association[:klass_name]
        # ...increment all associated counters 
        klass_name.constantize.increment_counter(self.class.counter_column_name.to_sym, self.send("#{association_name}_id".to_sym))
      end
    end
  end

      

Then in your model.

# app/models/post.rb
class Post < ActiveRecord::Base
  include ParanoidDeletable
  belongs_to :user, :counter_cache => true
  ...
end

      

A few notes on installation:



1) your pluralized is owned_to: join_name must be the same as the counter cache column name: "# {community_name} _count". For example:

# Will work
console > user.posts_count
          => 212
# Won't work
console > user.how_much_they_talk_count
          => 212

      

2a) If you are using polymorphic relationships, the associations must be configured appropriately in both models. For example:

# app/models/post.rb
....
has_many :comments, as: :commentable
....

# app/models/comment.rb
...
belongs_to :commentable, polymorphic: true, counter_cache: true
...

      

2b) If you are using a polymorphic relationship, the reference type field must be named like this: "# {community_name} _type". For example:

# Will work
console > comment.commentable_type
          => "Post"
# Won't work
console > comment.commentable_class_name
          => "Post"

      

Disclaimer: I tried to make this modular, but this is the first pass and I haven't tested it fully.

+1


source


This is a fairly straightforward solution, although some will disagree with the semantics.

module ActiveRecord
  # trigger create callbacks for counter_culture when restoring
  class Base
    class << self
      def acts_as_paranoid_with_counter_culture
        acts_as_paranoid
        simulate_create = lambda do |model|
          model.run_callbacks(:create)
          model.run_callbacks(:commit)
        end
        after_restore(&simulate_create)
      end
    end
  end
end

      

Then you just need to replace your calls acts_as_paranoid

with acts_as_paranoid_with_counter_culture

.

+1


source







All Articles