counter cache
This commit is contained in:
@@ -235,10 +235,20 @@ module HasAuxTable
|
||||
if self.method_defined?(column_name.to_sym)
|
||||
raise "invariant: method #{column_name} already defined"
|
||||
end
|
||||
|
||||
config.define_aux_attribute_delegate(column_name)
|
||||
config.define_aux_attribute_delegate(:"#{column_name}?")
|
||||
config.define_aux_attribute_delegate(:"#{column_name}=")
|
||||
[
|
||||
"",
|
||||
"_in_database",
|
||||
"?",
|
||||
"=",
|
||||
"_changed?",
|
||||
"_change",
|
||||
%w[clear_ _change]
|
||||
].each do |mod|
|
||||
prefix, suffix = mod.is_a?(Array) ? mod : ["", mod]
|
||||
config.define_aux_attribute_delegate(
|
||||
:"#{prefix}#{column_name}#{suffix}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
|
||||
@@ -879,25 +879,79 @@ RSpec.describe HasAuxTable do
|
||||
end
|
||||
|
||||
describe "counter cache" do
|
||||
before do
|
||||
@reader = Reader.create!(name: "John Doe", reading_speed: 100)
|
||||
@book =
|
||||
Book.create!(title: "The Great Gatsby", author: "F. Scott Fitzgerald")
|
||||
def verify_counter_cache(model, assoc_name, expected_count)
|
||||
expect(model.send("#{assoc_name}_count")).to eq(expected_count)
|
||||
expect(model.send(assoc_name).count).to eq(expected_count || 0)
|
||||
model.reload
|
||||
expect(model.send("#{assoc_name}_count")).to eq(expected_count)
|
||||
expect(model.send(assoc_name).count).to eq(expected_count || 0)
|
||||
end
|
||||
|
||||
let(:reader) { Reader.create!(name: "John Doe", reading_speed: 100) }
|
||||
let(:book) do
|
||||
Book.create!(title: "The Great Gatsby", author: "F. Scott Fitzgerald")
|
||||
end
|
||||
|
||||
it "updates counter caches that are on the aux model" do
|
||||
@reader.read_books << @book
|
||||
expect(@reader.read_book_joins_count).to eq(1)
|
||||
expect(@reader.read_book_joins.count).to eq(1)
|
||||
end
|
||||
|
||||
it "updates counter caches that are on the main table of an aux model" do
|
||||
verify_counter_cache(reader, :read_book_joins, nil)
|
||||
reader.read_books << book
|
||||
verify_counter_cache(reader, :read_book_joins, 1)
|
||||
end
|
||||
|
||||
it "updates counter caches on a non-aux model" do
|
||||
@reader.read_books << @book
|
||||
expect(@book.read_book_joins_count).to eq(1)
|
||||
expect(@book.read_book_joins.count).to eq(1)
|
||||
verify_counter_cache(book, :read_book_joins, nil)
|
||||
reader.read_books << book
|
||||
verify_counter_cache(book, :read_book_joins, 1)
|
||||
end
|
||||
|
||||
it "has_one is a base class, belongs_to is a subclass, created via subclass" do
|
||||
a = ModelA.create!(a_field1: "a_0")
|
||||
verify_counter_cache(a, :model_bs, nil)
|
||||
|
||||
5.times do |i|
|
||||
ModelB.create!(model_a: a, b_field1: "b_#{i}")
|
||||
verify_counter_cache(a, :model_bs, i + 1)
|
||||
end
|
||||
end
|
||||
|
||||
it "has_one is a base class, belongs_to is a subclass, created via association" do
|
||||
a = ModelA.create!(a_field1: "a_0")
|
||||
verify_counter_cache(a, :model_bs, nil)
|
||||
|
||||
5.times do |i|
|
||||
a.model_bs.create!(b_field1: "b_#{i}")
|
||||
verify_counter_cache(a, :model_bs, i + 1)
|
||||
end
|
||||
end
|
||||
|
||||
it "has_one is a subclass, belongs_to is a subclass, created via subclass" do
|
||||
a = ModelA2.create!(a_field1: "a2_0", a2_field1: "a2_0")
|
||||
verify_counter_cache(a, :model_b2s, nil)
|
||||
|
||||
5.times do |i|
|
||||
a.model_b2s.create!(b_field1: "b_#{i}")
|
||||
verify_counter_cache(a, :model_b2s, i + 1)
|
||||
end
|
||||
end
|
||||
|
||||
it "has_one is a subclass, belongs_to is a subclass, created via association" do
|
||||
a = ModelA2.create!(a_field1: "a2_0", a2_field1: "a2_0")
|
||||
verify_counter_cache(a, :model_b2s, nil)
|
||||
|
||||
5.times do |i|
|
||||
a.model_b2s.create!(b_field1: "b_#{i}")
|
||||
verify_counter_cache(a, :model_b2s, i + 1)
|
||||
end
|
||||
end
|
||||
|
||||
it "has_one is subclass, belongs_to is a vanilla class" do
|
||||
a = ModelA1.create!(a_field1: "a1_0", a1_field1: "a1_0")
|
||||
verify_counter_cache(a, :model_cs, nil)
|
||||
|
||||
5.times do |i|
|
||||
a.model_cs.create!(c_field1: "c_#{i}")
|
||||
verify_counter_cache(a, :model_cs, i + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -32,8 +32,6 @@ ActiveRecord::Schema.define do
|
||||
|
||||
create_base_table :people do |t|
|
||||
t.string :name
|
||||
t.integer :friends_count
|
||||
t.integer :lovers_count
|
||||
t.timestamps
|
||||
|
||||
t.create_aux :driver do |t|
|
||||
@@ -286,29 +284,89 @@ class User < ActiveRecord::Base
|
||||
has_many :posts, inverse_of: :user, class_name: "Post"
|
||||
end
|
||||
|
||||
# class TwitterUser < User
|
||||
# aux_table :twitter
|
||||
# validates :twitter_handle, presence: true
|
||||
# has_many :posts, inverse_of: :user, class_name: "TwitterPost"
|
||||
# end
|
||||
ActiveRecord::Schema.define do
|
||||
create_base_table :model_as do |t|
|
||||
t.string :a_field1
|
||||
t.integer :model_bs_count
|
||||
|
||||
# class RedditUser < User
|
||||
# aux_table :reddit
|
||||
# validates :reddit_handle, presence: true
|
||||
# has_many :posts, inverse_of: :user, class_name: "RedditPost"
|
||||
# end
|
||||
t.create_aux :a1 do |t|
|
||||
t.integer :a1_field1
|
||||
t.integer :model_cs_count
|
||||
end
|
||||
|
||||
# class Post < ActiveRecord::Base
|
||||
# include HasAuxTable
|
||||
# belongs_to :user, inverse_of: :posts
|
||||
# end
|
||||
t.create_aux :a2 do |t|
|
||||
t.string :a2_field1
|
||||
t.integer :model_b2s_count
|
||||
end
|
||||
end
|
||||
|
||||
# class TwitterPost < Post
|
||||
# aux_table :twitter
|
||||
# belongs_to :user, inverse_of: :posts, class_name: "TwitterUser"
|
||||
# end
|
||||
create_base_table :model_bs do |t|
|
||||
t.string :b_field1
|
||||
t.references :model_a, foreign_key: { to_table: :model_as }
|
||||
|
||||
# class RedditPost < Post
|
||||
# aux_table :reddit
|
||||
# belongs_to :user, inverse_of: :posts, class_name: "RedditUser"
|
||||
# end
|
||||
t.create_aux :b1 do |t|
|
||||
t.integer :b1_field1
|
||||
end
|
||||
|
||||
t.create_aux :b2 do |t|
|
||||
t.references :model_a2,
|
||||
foreign_key: {
|
||||
to_table: :model_as_a2_aux,
|
||||
primary_key: :base_table_id
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
create_table :model_cs do |t|
|
||||
t.string :c_field1
|
||||
t.references :model_a1,
|
||||
foreign_key: {
|
||||
to_table: :model_as_a1_aux,
|
||||
primary_key: :base_table_id
|
||||
}
|
||||
end
|
||||
|
||||
# A* has_many B
|
||||
# A1 has_many C
|
||||
# B belongs_to A
|
||||
# C belongs_to A1
|
||||
end
|
||||
|
||||
class ModelA < ActiveRecord::Base
|
||||
include HasAuxTable
|
||||
has_many :model_bs, inverse_of: :model_a
|
||||
validates :a_field1, presence: true, uniqueness: true
|
||||
end
|
||||
|
||||
class ModelA1 < ModelA
|
||||
aux_table :a1
|
||||
has_many :model_cs, inverse_of: :model_a1
|
||||
validates :a1_field1, presence: true, uniqueness: true
|
||||
end
|
||||
|
||||
class ModelA2 < ModelA
|
||||
aux_table :a2
|
||||
has_many :model_b2s, inverse_of: :model_a2
|
||||
validates :a2_field1, presence: true, uniqueness: true
|
||||
end
|
||||
|
||||
class ModelB < ActiveRecord::Base
|
||||
include HasAuxTable
|
||||
belongs_to :model_a, inverse_of: :model_bs, counter_cache: true
|
||||
validates :b_field1, presence: true, uniqueness: true
|
||||
end
|
||||
|
||||
class ModelB1 < ModelB
|
||||
aux_table :b1
|
||||
validates :b1_field1, presence: true, uniqueness: true
|
||||
end
|
||||
|
||||
class ModelB2 < ModelB
|
||||
aux_table :b2
|
||||
belongs_to :model_a2, inverse_of: :model_b2s, counter_cache: true
|
||||
end
|
||||
|
||||
class ModelC < ActiveRecord::Base
|
||||
belongs_to :model_a1, inverse_of: :model_cs, counter_cache: true
|
||||
validates :c_field1, presence: true, uniqueness: true
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user