Created
August 24, 2022 12:31
-
-
Save RStankov/24241d6892eeed49ae435a2f549be999 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# Complex counter counter caches | |
# | |
# Usage: | |
# | |
# In parent model | |
# class Post < ApplicationRecord | |
# ExplicitCounterCache.define(self, votes_count, -> { votes.active }) | |
# ExplicitCounterCache.define(self, comments_count, -> { votes.visible }) | |
# end | |
# | |
# In children models | |
# class Vote < ApplicationRecord | |
# ExplicitCounterCache::Refresh.define(self, :post, votes_count) | |
# end | |
# | |
# class Comment < ApplicationRecord | |
# ExplicitCounterCache::Refresh.define(self, :comment, votes_count) | |
# end | |
# | |
module ExplicitCounterCache | |
def self.define(model, counter_name, fetch_relation) | |
model.define_method "refresh_#{counter_name}" do | |
relation_or_count = instance_exec(&fetch_relation) | |
new_count = relation_or_count.is_a?(Numeric) ? relation_or_count : relation_or_count.count | |
return if self[counter_name] == new_count | |
return if destroyed? | |
# We intentionally avoid callbacks/validations here | |
# since these might be too expensive in some cases, e.g. checking | |
# another field for uniqueness even though we're just updating a counter. | |
update_columns counter_name => new_count, updated_at: Time.current | |
end | |
end | |
module Refresh | |
def self.define(model, parent_model, counter_name, same_transaction: false) | |
method_name = "refresh_#{parent_model}_#{counter_name}".to_sym | |
model.define_method method_name do | |
public_send(parent_model)&.public_send("refresh_#{counter_name}") | |
end | |
model.instance_eval do | |
if same_transaction | |
after_create method_name | |
after_destroy method_name | |
else | |
after_commit method_name | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment