From 4e07fa5fc941b925a13d716380c4e27a582914ad Mon Sep 17 00:00:00 2001 From: Emil Kampp Date: Tue, 21 Dec 2021 08:45:34 +0100 Subject: [PATCH 1/2] Initial counter_cache implementation --- lib/closure_tree/has_closure_tree.rb | 3 ++- lib/closure_tree/model.rb | 4 +++- spec/support/models.rb | 2 +- spec/support/schema.rb | 1 + spec/support/tag_examples.rb | 5 +++++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/closure_tree/has_closure_tree.rb b/lib/closure_tree/has_closure_tree.rb index b0bc5b1a..be09be95 100644 --- a/lib/closure_tree/has_closure_tree.rb +++ b/lib/closure_tree/has_closure_tree.rb @@ -11,7 +11,8 @@ def has_closure_tree(options = {}) :dont_order_roots, :numeric_order, :touch, - :with_advisory_lock + :with_advisory_lock, + :counter_cache ) class_attribute :_ct diff --git a/lib/closure_tree/model.rb b/lib/closure_tree/model.rb index d874c5f4..719a7aed 100644 --- a/lib/closure_tree/model.rb +++ b/lib/closure_tree/model.rb @@ -11,7 +11,9 @@ module Model foreign_key: _ct.parent_column_name, inverse_of: :children, touch: _ct.options[:touch], - optional: true) + optional: true, + counter_cache: _ct.options[:counter_cache], + ) order_by_generations = -> { Arel.sql("#{_ct.quoted_hierarchy_table_name}.generations ASC") } diff --git a/spec/support/models.rb b/spec/support/models.rb index 9927af41..bbb18ef7 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -15,7 +15,7 @@ def add_destroyed_tag class UUIDTag < ActiveRecord::Base self.primary_key = :uuid before_create :set_uuid - has_closure_tree dependent: :destroy, order: 'name', parent_column_name: 'parent_uuid' + has_closure_tree dependent: :destroy, order: 'name', parent_column_name: 'parent_uuid', counter_cache: true before_destroy :add_destroyed_tag def set_uuid diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 6d530117..95e92e37 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -27,6 +27,7 @@ t.string "title" t.string "parent_uuid" t.integer "sort_order" + t.integer "uuid_tags_count", default: 0, null: false t.timestamps null: false end diff --git a/spec/support/tag_examples.rb b/spec/support/tag_examples.rb index 6a21ee69..05994a0a 100644 --- a/spec/support/tag_examples.rb +++ b/spec/support/tag_examples.rb @@ -23,6 +23,11 @@ expected_parent_column_name = tag_class == UUIDTag ? 'parent_uuid' : 'parent_id' expect(tag_class._ct.parent_column_name).to eq(expected_parent_column_name) end + + it 'should counter_cache parent relationship' do + expected_counter_cache = tag_class == UUIDTag ? true : nil + expect(tag_class._ct.options[:counter_cache]).to be expected_counter_cache + end end describe 'from empty db' do From b62775a40c3f01ecd9854cf147dfd073dafc5f49 Mon Sep 17 00:00:00 2001 From: Emil Kampp Date: Tue, 21 Dec 2021 08:58:38 +0100 Subject: [PATCH 2/2] Add documentation --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index d6856b9c..9ed8f5e5 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,26 @@ child1.ancestry_path => ["Grandparent", "Parent", "First Child"] ``` +### Counter cache + +It's possible to use Rails' counter cache option for the `parent` relationship by adding the `counter_cache: true` option to `has_closure_tree`. + +```ruby +class Tag < ApplicationRecord + has_closure_tree counter_cache: true +end +``` + +You will need to generate one additional migration: + +```ruby +class AddTagsCountToTag < ActiveRecord::Migration + def change + add_column :tags, :tags_count, :integer, null: false, default: 0 + end +end +``` + ### find_or_create_by_path You can `find` as well as `find_or_create` by "ancestry paths".