counter_culture 1.8.0 → 2.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +65 -0
- data/.travis.yml +27 -9
- data/Appraisals +22 -0
- data/CHANGELOG.md +169 -0
- data/Gemfile +6 -41
- data/README.md +142 -21
- data/Rakefile +7 -34
- data/counter_culture.gemspec +39 -168
- data/gemfiles/rails_4.2.gemfile +10 -0
- data/gemfiles/rails_5.0.gemfile +10 -0
- data/gemfiles/rails_5.1.gemfile +10 -0
- data/gemfiles/rails_5.2.gemfile +10 -0
- data/gemfiles/rails_6.0.gemfile +10 -0
- data/lib/counter_culture.rb +1 -1
- data/lib/counter_culture/counter.rb +56 -38
- data/lib/counter_culture/extensions.rb +58 -48
- data/lib/counter_culture/reconciler.rb +83 -13
- data/lib/counter_culture/version.rb +3 -0
- data/lib/generators/counter_culture_generator.rb +7 -1
- data/lib/generators/templates/counter_culture_migration.rb.erb +6 -12
- data/run_tests_locally.sh +20 -0
- metadata +100 -131
- data/VERSION +0 -1
- data/spec/counter_culture_spec.rb +0 -1857
- data/spec/models/another_post.rb +0 -13
- data/spec/models/another_post_comment.rb +0 -4
- data/spec/models/candidate.rb +0 -3
- data/spec/models/candidate_profile.rb +0 -3
- data/spec/models/categ.rb +0 -13
- data/spec/models/category.rb +0 -3
- data/spec/models/company.rb +0 -11
- data/spec/models/conditional_dependent.rb +0 -7
- data/spec/models/conditional_main.rb +0 -3
- data/spec/models/conversation.rb +0 -4
- data/spec/models/has_string_id.rb +0 -4
- data/spec/models/industry.rb +0 -2
- data/spec/models/person.rb +0 -4
- data/spec/models/poly_employee.rb +0 -3
- data/spec/models/poly_image.rb +0 -15
- data/spec/models/poly_product.rb +0 -4
- data/spec/models/post.rb +0 -10
- data/spec/models/post_comment.rb +0 -6
- data/spec/models/product.rb +0 -7
- data/spec/models/review.rb +0 -33
- data/spec/models/simple_dependent.rb +0 -5
- data/spec/models/simple_main.rb +0 -3
- data/spec/models/simple_review.rb +0 -3
- data/spec/models/soft_delete.rb +0 -6
- data/spec/models/subcateg.rb +0 -14
- data/spec/models/transaction.rb +0 -15
- data/spec/models/twitter_review.rb +0 -6
- data/spec/models/user.rb +0 -40
- data/spec/rails_app/.gitignore +0 -15
- data/spec/rails_app/Gemfile +0 -41
- data/spec/rails_app/Gemfile.lock +0 -150
- data/spec/rails_app/README.rdoc +0 -261
- data/spec/rails_app/Rakefile +0 -7
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +0 -15
- data/spec/rails_app/app/assets/stylesheets/application.css +0 -13
- data/spec/rails_app/app/controllers/application_controller.rb +0 -3
- data/spec/rails_app/app/helpers/application_helper.rb +0 -2
- data/spec/rails_app/app/mailers/.gitkeep +0 -0
- data/spec/rails_app/app/models/.gitkeep +0 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +0 -14
- data/spec/rails_app/config.ru +0 -4
- data/spec/rails_app/config/application.rb +0 -58
- data/spec/rails_app/config/boot.rb +0 -6
- data/spec/rails_app/config/database.yml +0 -25
- data/spec/rails_app/config/environment.rb +0 -5
- data/spec/rails_app/config/environments/development.rb +0 -32
- data/spec/rails_app/config/environments/test.rb +0 -31
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/rails_app/config/initializers/inflections.rb +0 -15
- data/spec/rails_app/config/initializers/mime_types.rb +0 -5
- data/spec/rails_app/config/initializers/paper_trail.rb +0 -1
- data/spec/rails_app/config/initializers/secret_token.rb +0 -7
- data/spec/rails_app/config/initializers/session_store.rb +0 -8
- data/spec/rails_app/config/initializers/wrap_parameters.rb +0 -14
- data/spec/rails_app/config/locales/en.yml +0 -5
- data/spec/rails_app/config/routes.rb +0 -58
- data/spec/rails_app/db/seeds.rb +0 -7
- data/spec/rails_app/lib/assets/.gitkeep +0 -0
- data/spec/rails_app/lib/tasks/.gitkeep +0 -0
- data/spec/rails_app/log/.gitkeep +0 -0
- data/spec/rails_app/public/404.html +0 -26
- data/spec/rails_app/public/422.html +0 -26
- data/spec/rails_app/public/500.html +0 -25
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/index.html +0 -241
- data/spec/rails_app/public/robots.txt +0 -5
- data/spec/rails_app/script/rails +0 -6
- data/spec/rails_app/test/fixtures/.gitkeep +0 -0
- data/spec/rails_app/test/functional/.gitkeep +0 -0
- data/spec/rails_app/test/integration/.gitkeep +0 -0
- data/spec/rails_app/test/performance/browsing_test.rb +0 -12
- data/spec/rails_app/test/test_helper.rb +0 -13
- data/spec/rails_app/test/unit/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/rails_app/vendor/plugins/.gitkeep +0 -0
- data/spec/schema.rb +0 -227
- data/spec/spec_helper.rb +0 -32
- data/test_rails_versions.sh +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 74fff95505913ba522b3ec93bc9101c375c7de5678744145a0e4959f2c267361
|
4
|
+
data.tar.gz: bc284f87da6f949e7d875b7f30c43accbe23ce5716601af9fc4143eb6346954d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e63708745c4ffbb727a1b4da61a0d027de76d7256ee5d9669fa0f18dfcc2b183f26ddc109f45d6af6fb68a79c040e73872f647e8453c7b801e390036601878eb
|
7
|
+
data.tar.gz: c58c36d4aaf450450d913ad93487c7012de5de0ba8bb4f99c83a3994b64e8aed19f04a0d62c0df21ceda862312b9c010af042857709d891f746e20403ef893f2
|
data/.gitignore
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# appraisal lockfiles
|
2
|
+
gemfiles/*.lock
|
3
|
+
|
4
|
+
# spec coverage
|
5
|
+
coverage
|
6
|
+
coverage.data
|
7
|
+
|
8
|
+
# rdoc generated
|
9
|
+
rdoc
|
10
|
+
|
11
|
+
# yard generated
|
12
|
+
doc
|
13
|
+
.yardoc
|
14
|
+
|
15
|
+
# bundler
|
16
|
+
.bundle
|
17
|
+
|
18
|
+
# jeweler generated
|
19
|
+
pkg
|
20
|
+
|
21
|
+
# this changes all the time when we change multiple Rails versions
|
22
|
+
Gemfile.lock
|
23
|
+
|
24
|
+
# allow people to manage their own Ruby versions / gemsets
|
25
|
+
.ruby-version
|
26
|
+
.ruby-gemset
|
27
|
+
|
28
|
+
# rspec pass/fail status
|
29
|
+
.rspec_status
|
30
|
+
|
31
|
+
# sqlite3 db
|
32
|
+
db
|
33
|
+
|
34
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
35
|
+
#
|
36
|
+
# * Create a file at ~/.gitignore
|
37
|
+
# * Include files you want ignored
|
38
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
39
|
+
#
|
40
|
+
# After doing this, these files will be ignored in all your git projects,
|
41
|
+
# saving you from having to 'pollute' every project you touch with them
|
42
|
+
#
|
43
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
44
|
+
#
|
45
|
+
# For MacOS:
|
46
|
+
#
|
47
|
+
#.DS_Store
|
48
|
+
|
49
|
+
# For TextMate
|
50
|
+
#*.tmproj
|
51
|
+
#tmtags
|
52
|
+
|
53
|
+
# For emacs:
|
54
|
+
#*~
|
55
|
+
#\#*
|
56
|
+
#.\#*
|
57
|
+
|
58
|
+
# For vim:
|
59
|
+
#*.swp
|
60
|
+
|
61
|
+
# For redcar:
|
62
|
+
#.redcar
|
63
|
+
|
64
|
+
# For rubinius:
|
65
|
+
#*.rbc
|
data/.travis.yml
CHANGED
@@ -1,14 +1,32 @@
|
|
1
1
|
language: ruby
|
2
|
+
services:
|
3
|
+
- postgresql
|
4
|
+
- mysql
|
5
|
+
addons:
|
6
|
+
postgresql: "9.6"
|
2
7
|
rvm:
|
3
|
-
- "2.
|
4
|
-
- "2.
|
8
|
+
- "2.5.7"
|
9
|
+
- "2.6.5"
|
10
|
+
- "2.7.0"
|
11
|
+
gemfile:
|
12
|
+
- gemfiles/rails_4.2.gemfile
|
13
|
+
- gemfiles/rails_5.0.gemfile
|
14
|
+
- gemfiles/rails_5.1.gemfile
|
15
|
+
- gemfiles/rails_5.2.gemfile
|
16
|
+
- gemfiles/rails_6.0.gemfile
|
17
|
+
matrix:
|
18
|
+
exclude:
|
19
|
+
- rvm: "2.7.0"
|
20
|
+
gemfile: gemfiles/rails_4.2.gemfile
|
5
21
|
env:
|
6
|
-
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
|
10
|
-
-
|
11
|
-
- "RAILS_VERSION=5.1.0"
|
22
|
+
- DB=postgresql
|
23
|
+
- DB=sqlite3
|
24
|
+
- DB=mysql2
|
25
|
+
before_install:
|
26
|
+
- gem install bundler -v '1.17.3'
|
12
27
|
install:
|
13
|
-
- bundle update
|
28
|
+
- bundle _1.17.3_ update
|
29
|
+
before_script:
|
30
|
+
- psql -c 'create database counter_culture_test;' -U postgres
|
31
|
+
- mysql -e 'CREATE DATABASE counter_culture_test;'
|
14
32
|
script: TRAVIS=true bundle exec rake spec
|
data/Appraisals
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
%w[
|
2
|
+
4.2
|
3
|
+
5.0
|
4
|
+
5.1
|
5
|
+
5.2
|
6
|
+
6.0
|
7
|
+
].each do |rails_version|
|
8
|
+
gem_rails_version = Gem::Version.new(rails_version)
|
9
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') &&
|
10
|
+
gem_rails_version >= Gem::Version.new('6.0.0.beta')
|
11
|
+
|
12
|
+
# Rails 6 requires Ruby >= 2.5
|
13
|
+
next
|
14
|
+
end
|
15
|
+
appraise "rails-#{rails_version}" do
|
16
|
+
gem 'rails', "~> #{rails_version}.0"
|
17
|
+
|
18
|
+
gem 'pg', gem_rails_version < Gem::Version.new('5.0') ? '~> 0.15' : '~> 1.0'
|
19
|
+
gem 'mysql2'
|
20
|
+
gem 'sqlite3', gem_rails_version < Gem::Version.new(5.2) ? '~> 1.3.0' : '~> 1.4'
|
21
|
+
end
|
22
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,172 @@
|
|
1
|
+
## 2.7.0 (November 16, 2020)
|
2
|
+
|
3
|
+
Improvements:
|
4
|
+
- New `column_name` option for `counter_culture_fix_counts` (#300)
|
5
|
+
|
6
|
+
## 2.6.2 (October 21, 2020)
|
7
|
+
|
8
|
+
Improvements:
|
9
|
+
- Remove superfluous dependency to after_commit_action gem (#297)
|
10
|
+
|
11
|
+
## 2.6.1 (September 8, 2020)
|
12
|
+
|
13
|
+
Bugfixes:
|
14
|
+
- Address Ruby 2.7.0 deprecation warning (#292)
|
15
|
+
|
16
|
+
## 2.6.0 (July 29, 2020)
|
17
|
+
|
18
|
+
Improvements:
|
19
|
+
- Use []= method instead of attribute writer method internally to avoid conflicts with read-only attributes (#287)
|
20
|
+
- More detailed logging when performing reconciliation (#288)
|
21
|
+
|
22
|
+
## 2.5.1 (May 18, 2020)
|
23
|
+
|
24
|
+
Bugfixes:
|
25
|
+
- Fix migration generation in Rails 6+ (#281)
|
26
|
+
|
27
|
+
## 2.5.0 (May 12, 2020)
|
28
|
+
|
29
|
+
Bugfixes:
|
30
|
+
- Fix `counter_culture_fix_counts` with Rails 4.2
|
31
|
+
|
32
|
+
Changes:
|
33
|
+
- Dropped support for Ruby 2.3 and 2.4
|
34
|
+
|
35
|
+
## 2.4.0 (May 9, 2020)
|
36
|
+
|
37
|
+
Improvements:
|
38
|
+
- Allow specifying `start` and `finish` options to `counter_culture_fix_counts` (#279)
|
39
|
+
|
40
|
+
## 2.3.0 (January 28, 2020)
|
41
|
+
|
42
|
+
Improvements:
|
43
|
+
- Allow using scopes in `column_names` (#272)
|
44
|
+
|
45
|
+
## 2.2.4 (August 21, 2019)
|
46
|
+
|
47
|
+
Bugfixes:
|
48
|
+
- Test and fix behavior in Rails 6.0.0 release (#268)
|
49
|
+
|
50
|
+
## 2.2.3 (June 20, 2019)
|
51
|
+
|
52
|
+
Improvements:
|
53
|
+
- Start testing against MySQL and PostgreSQL as well as sqlite3 (#257)
|
54
|
+
|
55
|
+
Bugfixes:
|
56
|
+
- Fix edge cases in MySQL (#257)
|
57
|
+
|
58
|
+
## 2.2.2 (May 5, 2019)
|
59
|
+
|
60
|
+
Bugfixes:
|
61
|
+
- Don't fail reconciliation in PostgreSQL if the Rails-level primary key is not a DB primary key (#254)
|
62
|
+
|
63
|
+
## 2.2.1 (April 17, 2019)
|
64
|
+
|
65
|
+
Improvements:
|
66
|
+
- Improve logging when `verbose` is set (#256)
|
67
|
+
|
68
|
+
## 2.2.0 (April 9, 2019)
|
69
|
+
|
70
|
+
Improvements:
|
71
|
+
- Add `where` option to `counter_culture_fix_counts` (#250)
|
72
|
+
- Add `verbose` option to `counter_culture_fix_counts` (#251)
|
73
|
+
|
74
|
+
Changes:
|
75
|
+
- Dropped support for Ruby 2.2
|
76
|
+
- Dropped support for Rails 3.2, 4.0 and 4.1
|
77
|
+
|
78
|
+
## 2.1.4 (January 21, 2019)
|
79
|
+
|
80
|
+
Improvements:
|
81
|
+
- Avoid instantiating model class during `counter_culture` call (#246)
|
82
|
+
|
83
|
+
## 2.1.3 (January 19, 2019)
|
84
|
+
|
85
|
+
Bugfixes:
|
86
|
+
- Don't update running total on soft-deleted records (#244)
|
87
|
+
|
88
|
+
## 2.1.2 (December 7, 2018)
|
89
|
+
|
90
|
+
Bugfixes:
|
91
|
+
- Properly handle `destroy` and `really_destroy` when using Paranoia (#239)
|
92
|
+
|
93
|
+
## 2.1.1 (November 7, 2018)
|
94
|
+
|
95
|
+
Bugfixes:
|
96
|
+
- Don't double-decrement when discarding and then hard-destroying a record (#237)
|
97
|
+
|
98
|
+
## 2.1.0 (October 19, 2018)
|
99
|
+
|
100
|
+
Bugfixes:
|
101
|
+
- Fix behavior with Models that are part of a module (ex: `Users::Admin`) (#234)
|
102
|
+
|
103
|
+
## 2.0.1 (August 19, 2018)
|
104
|
+
|
105
|
+
Bugfixes:
|
106
|
+
- Properly set timestamps of PaperTrail versions (#224, #225, #226)
|
107
|
+
|
108
|
+
## 2.0.0 (June 12, 2018)
|
109
|
+
|
110
|
+
Breaking changes:
|
111
|
+
- execute_after_commit was removed
|
112
|
+
- Removed workaround for incorrect counts when triggering updates from an `after_create` hook. Your options if this applies to you:
|
113
|
+
* continue using counter_culture 1.12.0
|
114
|
+
* upgrade to Rails 5.1.5 which fixes the underlying issue in Rails
|
115
|
+
* avoid triggering further updates on the same model in `after_create`; simply set the attribute in `before_create` instead
|
116
|
+
|
117
|
+
Bugfixes:
|
118
|
+
- Multiple updates in one transaction will now be processed correctly (#222)
|
119
|
+
|
120
|
+
## 1.12.0 (June 8, 2018)
|
121
|
+
|
122
|
+
Improvements:
|
123
|
+
- Adds support for the [Discard](https://github.com/jhawthorn/discard) soft-delete gem (#220)
|
124
|
+
|
125
|
+
## 1.11.0 (May 4, 2018)
|
126
|
+
|
127
|
+
Bugfixes:
|
128
|
+
- Fix `with_papertrail` behavior while still addressing the deprecation warning, and actually recording the correct counts (#218, see [airblade/paper_trail#1076](https://github.com/airblade/paper_trail/issues/1076))
|
129
|
+
|
130
|
+
## 1.10.1 (April 20, 2018)
|
131
|
+
|
132
|
+
Improvements:
|
133
|
+
- Added the ability to update timestamps while fixing count by passing `touch: true` to `counter_culture_fix_counts` (#212)
|
134
|
+
|
135
|
+
## 1.9.2 (April 13, 2018)
|
136
|
+
|
137
|
+
Bugfixes:
|
138
|
+
- When using paranoia, call increment / decrement only once when calling destroy or restore on the same model multiple times
|
139
|
+
|
140
|
+
Improvements:
|
141
|
+
- Address deprecation warning when enabling paper_trail touch in paper_trail version 9.0
|
142
|
+
- Address `Arel` deprecation warnings in Rails 5.2
|
143
|
+
|
144
|
+
Changes:
|
145
|
+
- Test against Ruby 2.2 through 2.5, Rails 3.2 through 5.2
|
146
|
+
- Don't test Rails 3.2 through 4.1 against Ruby 2.4 or 2.5 as those versions of Rails will not work with those versions of Ruby
|
147
|
+
- Avoid various deprecation warnings being triggered in the test
|
148
|
+
|
149
|
+
## 1.9.1 (March 1, 2018)
|
150
|
+
|
151
|
+
Bugfixes:
|
152
|
+
- Address an edge-case test regression caused by Rails 5.1.5 that was causing counts to be off when saving changes from an `after_create` callback
|
153
|
+
|
154
|
+
## 1.9.0 (November 29, 2017)
|
155
|
+
|
156
|
+
Improvements:
|
157
|
+
- Switch generated migration files to use new hash syntax
|
158
|
+
- Support for Rails 5 migration file format for generated migrations
|
159
|
+
|
160
|
+
## 1.8.2 (September 27, 2017)
|
161
|
+
|
162
|
+
Bugfixes:
|
163
|
+
- Actually use `batch_size` parameter in `counter_culture_fix_counts` (#200)
|
164
|
+
|
165
|
+
## 1.8.1 (September 5, 2017)
|
166
|
+
|
167
|
+
Improvements:
|
168
|
+
- Use ActiveRecord version, not Rails version, in `Reconciler`, makeing it possible to use `counter_culture_fix_counts` without Rails
|
169
|
+
|
1
170
|
## 1.8.0 (August 30, 2017)
|
2
171
|
|
3
172
|
Improvements:
|
data/Gemfile
CHANGED
@@ -1,44 +1,9 @@
|
|
1
|
-
source
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
1
|
+
source 'https://rubygems.org'
|
5
2
|
|
6
|
-
|
7
|
-
gem "activerecord", ">= 3.0.0"
|
8
|
-
gem "activesupport", ">= 3.0.0"
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
9
4
|
|
10
|
-
#
|
11
|
-
|
12
|
-
group :development, :test do
|
13
|
-
gem "rake"
|
5
|
+
# Specify the gem's dependencies in counter_culture.gemspec
|
6
|
+
gemspec
|
14
7
|
|
15
|
-
|
16
|
-
|
17
|
-
{github: "rails/rails"}
|
18
|
-
when nil, ""
|
19
|
-
">= 3.1.0"
|
20
|
-
else
|
21
|
-
"~> #{ENV["RAILS_VERSION"]}"
|
22
|
-
end
|
23
|
-
gem "rails", rails
|
24
|
-
|
25
|
-
gem "rspec", "~> 3.0"
|
26
|
-
gem "awesome_print"
|
27
|
-
gem "timecop"
|
28
|
-
|
29
|
-
# to test the integration
|
30
|
-
gem "paranoia"
|
31
|
-
gem "paper_trail"
|
32
|
-
end
|
33
|
-
|
34
|
-
group :development do
|
35
|
-
gem "rdoc", "~> 3.12"
|
36
|
-
gem "bundler", ">= 1.2.0"
|
37
|
-
gem "jeweler", "~> 2.1"
|
38
|
-
end
|
39
|
-
|
40
|
-
group :test do
|
41
|
-
gem "sqlite3"
|
42
|
-
gem "rspec-extra-formatters"
|
43
|
-
gem "database_cleaner", ">= 1.1.1"
|
44
|
-
end
|
8
|
+
# Ensure we have the default DB adapter for testing
|
9
|
+
gem 'sqlite3'
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# counter_culture [![Build Status](https://travis-ci.
|
1
|
+
# counter_culture [![Build Status](https://travis-ci.com/magnusvk/counter_culture.svg)](https://travis-ci.com/magnusvk/counter_culture)
|
2
2
|
|
3
3
|
Turbo-charged counter caches for your Rails app. Huge improvements over the Rails standard counter caches:
|
4
4
|
|
@@ -7,14 +7,20 @@ Turbo-charged counter caches for your Rails app. Huge improvements over the Rail
|
|
7
7
|
* Supports dynamic column names, making it possible to split up the counter cache for different types of objects
|
8
8
|
* Can keep a running count, or a running total
|
9
9
|
|
10
|
-
Tested against Ruby 2.2.5 and 2.
|
10
|
+
Tested against Ruby 2.3.8, 2.4.7, 2.5.6 and 2.6.4 and against the latest patch releases of Rails 4.2, 5.0, 5.1, 5.2 and 6.0.
|
11
|
+
|
12
|
+
Please note that -- unlike Rails' built-in counter-caches -- counter_culture does not currently change the behavior of the `.size` method on ActiveRecord associations. If you want to avoid a database query and read the cached value, please use the attribute name containing the counter cache directly.
|
13
|
+
```
|
14
|
+
product.categories.size # => will lead to a SELECT COUNT(*) query
|
15
|
+
product.categories_count # => will use counter cache without query
|
16
|
+
```
|
11
17
|
|
12
18
|
## Installation
|
13
19
|
|
14
20
|
Add counter_culture to your Gemfile:
|
15
21
|
|
16
22
|
```ruby
|
17
|
-
gem 'counter_culture', '~>
|
23
|
+
gem 'counter_culture', '~> 2.0'
|
18
24
|
```
|
19
25
|
|
20
26
|
Then run `bundle install`
|
@@ -39,6 +45,8 @@ If you are adding counter caches to existing data, you must add code to [manuall
|
|
39
45
|
|
40
46
|
### Simple counter-cache
|
41
47
|
|
48
|
+
#### Has many association
|
49
|
+
|
42
50
|
```ruby
|
43
51
|
class Product < ActiveRecord::Base
|
44
52
|
belongs_to :category
|
@@ -52,6 +60,30 @@ end
|
|
52
60
|
|
53
61
|
Now, the ```Category``` model will keep an up-to-date counter-cache in the ```products_count``` column of the ```categories``` table.
|
54
62
|
|
63
|
+
#### Many to many association
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class User < ActiveRecord::Base
|
67
|
+
has_many :group_memberships
|
68
|
+
has_many :groups, through: :group_memberships
|
69
|
+
end
|
70
|
+
|
71
|
+
class Group < ActiveRecord::Base
|
72
|
+
has_many :group_memberships
|
73
|
+
has_many :members, through: :group_memberships, class: "User"
|
74
|
+
end
|
75
|
+
|
76
|
+
class Membership < ActiveRecord::Base
|
77
|
+
belongs_to :group
|
78
|
+
belongs_to :member, class: "User"
|
79
|
+
counter_culture :group, column_name: "members_count"
|
80
|
+
# If you'd like to also touch the group when `members_count` is updated
|
81
|
+
# counter_culture :group, column_name: "members_count", touch: true
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
Now, the `Group` model will have an up to date count of its members in the `members_count` column
|
86
|
+
|
55
87
|
### Multi-level counter-cache
|
56
88
|
|
57
89
|
```ruby
|
@@ -116,7 +148,7 @@ end
|
|
116
148
|
```ruby
|
117
149
|
class Product < ActiveRecord::Base
|
118
150
|
belongs_to :category
|
119
|
-
counter_culture :category, column_name: :weight, delta_magnitude: proc { model.product_type == 'awesome' ? 2 : 1 }
|
151
|
+
counter_culture :category, column_name: :weight, delta_magnitude: proc {|model| model.product_type == 'awesome' ? 2 : 1 }
|
120
152
|
end
|
121
153
|
|
122
154
|
class Category < ActiveRecord::Base
|
@@ -155,6 +187,8 @@ end
|
|
155
187
|
|
156
188
|
Now, the ```Category``` model will keep the counter cache in ```special_count``` up-to-date. Only products where ```special?``` returns true will affect the special_count.
|
157
189
|
|
190
|
+
If you would like to use this with `counter_culture_fix_counts`, make sure to also provide [the `column_names` configuration](#handling-dynamic-column-names).
|
191
|
+
|
158
192
|
### Totaling instead of counting
|
159
193
|
|
160
194
|
Instead of keeping a running count, you may want to automatically track a running total.
|
@@ -216,16 +250,6 @@ You may also specify a custom timestamp column that gets updated only when a par
|
|
216
250
|
|
217
251
|
With this option, any time the `category_counter_cache` changes both the `category_count_changed` and `updated_at` columns will get updated.
|
218
252
|
|
219
|
-
### Executing counter cache updates after commit
|
220
|
-
|
221
|
-
By default, counter_culture will run counter cache updates inside of the same ActiveRecord transaction that triggered it. (Note that this bevavior [changed from version 0.2.3 to 1.0.0](CHANGELOG.md#100-november-15-2016).) If you would like to run counter cache updates outside of that transaction, for example because you are experiencing [deadlocks with older versions of PostgreSQL](http://mina.naguib.ca/blog/2010/11/22/postgresql-foreign-key-deadlocks.html), you can enable that behavior:
|
222
|
-
```ruby
|
223
|
-
counter_culture :category, execute_after_commit: true
|
224
|
-
```
|
225
|
-
|
226
|
-
Please note that using `execute_after_commit` in conjunction with transactional
|
227
|
-
fixtures will lead to your tests no longer seeing updated counter values.
|
228
|
-
|
229
253
|
### Manually populating counter cache values
|
230
254
|
|
231
255
|
You will sometimes want to populate counter-cache values from primary data. This is required when adding counter-caches to existing data. It is also recommended to run this regularly (at BestVendor, we run it once a week) to catch any incorrect values in the counter caches.
|
@@ -234,19 +258,29 @@ You will sometimes want to populate counter-cache values from primary data. This
|
|
234
258
|
Product.counter_culture_fix_counts
|
235
259
|
# will automatically fix counts for all counter caches defined on Product
|
236
260
|
|
237
|
-
Product.counter_culture_fix_counts
|
261
|
+
Product.counter_culture_fix_counts exclude: :category
|
238
262
|
# will automatically fix counts for all counter caches defined on Product, except for the :category relation
|
239
263
|
|
240
264
|
Product.counter_culture_fix_counts only: :category
|
241
265
|
# will automatically fix counts only on the :category relation on Product
|
242
266
|
|
243
|
-
# :
|
267
|
+
# :exclude and :only also accept arrays of one level relations
|
244
268
|
# if you want to fix counts on a more than one level relation you need to use convention below:
|
245
269
|
|
246
270
|
Product.counter_culture_fix_counts only: [[:subcategory, :category]]
|
247
271
|
# will automatically fix counts only on the two-level [:subcategory, :category] relation on Product
|
248
272
|
|
273
|
+
Product.counter_culture_fix_counts column_name: :reviews_count
|
274
|
+
# will automatically fix counts only on the :reviews_count column on Product
|
275
|
+
# This allows us to skip the columns that have already been processed. This is useful after running big DB changes that affect only one counter cache column.
|
276
|
+
|
249
277
|
# :except and :only also accept arrays
|
278
|
+
|
279
|
+
Product.counter_culture_fix_counts verbose: true
|
280
|
+
# prints some logs to STDOUT
|
281
|
+
|
282
|
+
Product.counter_culture_fix_counts only: :category, where: { categories: { id: 1 } }
|
283
|
+
# will automatically fix counts only on the :category with id 1 relation on Product
|
250
284
|
```
|
251
285
|
|
252
286
|
The ```counter_culture_fix_counts``` counts method uses batch processing of records to keep the memory consumption low. The default batch size is 1000 but is configurable like so
|
@@ -272,6 +306,37 @@ Product.counter_culture_fix_counts batch_size: 100
|
|
272
306
|
|
273
307
|
```counter_culture_fix_counts``` is optimized to minimize the number of queries and runs very quickly.
|
274
308
|
|
309
|
+
Similarly to `counter_culture`, it is possible to update the records' timestamps, when fixing counts. If you would like to update the default timestamp field, pass `touch: true` option:
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
Product.counter_culture_fix_counts touch: true
|
313
|
+
```
|
314
|
+
|
315
|
+
If you have specified a custom timestamps column, pass its name as the value for the `touch` option:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
Product.counter_culture_fix_counts touch: 'category_count_changed'
|
319
|
+
```
|
320
|
+
|
321
|
+
|
322
|
+
#### Parallelizing fix counter cache in multiple workers
|
323
|
+
|
324
|
+
The options start and finish are especially useful if you want multiple workers dealing with the same processing queue. You can make worker 1 handle all the records between id 1 and 9999 and worker 2 handle from 10000 and beyond by setting the :start and :finish option on each worker.
|
325
|
+
|
326
|
+
```ruby
|
327
|
+
Product.counter_culture_fix_counts start: 10_000
|
328
|
+
# will fix counts for all counter caches defined on Product from record 10000 and onwards.
|
329
|
+
|
330
|
+
Product.counter_culture_fix_counts finish: 10_000
|
331
|
+
# let's process until 10000 records.
|
332
|
+
|
333
|
+
Product.counter_culture_fix_counts start: 1000, finish: 2000
|
334
|
+
# In worker 1, lets process from 1000 to 2000
|
335
|
+
|
336
|
+
Product.counter_culture_fix_counts start: 2001, finish: 3000
|
337
|
+
# In worker 2, lets process from 2001 to 3000
|
338
|
+
```
|
339
|
+
|
275
340
|
#### Handling dynamic column names
|
276
341
|
|
277
342
|
Manually populating counter caches with dynamic column names requires additional configuration:
|
@@ -289,16 +354,45 @@ class Product < ActiveRecord::Base
|
|
289
354
|
end
|
290
355
|
```
|
291
356
|
|
357
|
+
You can specify a scope instead of a where condition string for `column_names`:
|
358
|
+
|
359
|
+
```ruby
|
360
|
+
class Product < ActiveRecord::Base
|
361
|
+
belongs_to :category
|
362
|
+
scope :awesomes, ->{ where "products.product_type = ?", 'awesome' }
|
363
|
+
scope :suckys, ->{ where "products.product_type = ?", 'sucky' }
|
364
|
+
|
365
|
+
counter_culture :category,
|
366
|
+
column_name: proc {|model| "#{model.product_type}_count" },
|
367
|
+
column_names: {
|
368
|
+
Product.awesomes => :awesome_count,
|
369
|
+
Product.suckys => :sucky_count
|
370
|
+
}
|
371
|
+
end
|
372
|
+
```
|
373
|
+
|
374
|
+
If you would like to avoid this configuration and simply skip counter caches with
|
375
|
+
dynamic column names, while still fixing those counters on the model that are not
|
376
|
+
dynamic, you can pass `skip_unsupported`:
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
Product.counter_culture_fix_counts skip_unsupported: true
|
380
|
+
```
|
381
|
+
|
292
382
|
#### Handling over-written, dynamic foreign keys
|
293
383
|
|
294
384
|
Manually populating counter caches with dynamically over-written foreign keys (```:foreign_key_values``` option) is not supported. You will have to write code to handle this case yourself.
|
295
385
|
|
296
|
-
### Soft-deletes
|
386
|
+
### Soft-deletes with `paranoia` or `discard`
|
387
|
+
|
388
|
+
This gem will keep counters correctly updated in Rails 4.2 or later when using
|
389
|
+
[paranoia](https://github.com/rubysherpas/paranoia) or
|
390
|
+
[discard](https://github.com/jhawthorn/discard) for soft-delete support.
|
391
|
+
However, to ensure that counts are incremented after a restore you have
|
392
|
+
to make sure to set up soft deletion (via `acts_as_paranoid` or
|
393
|
+
`include Discard::Model`) before the call to `counter_culture` in your model:
|
297
394
|
|
298
|
-
|
299
|
-
soft-delete support. However, to ensure that counts are incremented after a
|
300
|
-
restore you have to make sure that the call to `acts_as_paranoid` comes before
|
301
|
-
the call to `counter_culture` in your model:
|
395
|
+
#### Paranoia
|
302
396
|
|
303
397
|
```ruby
|
304
398
|
class SoftDelete < ActiveRecord::Base
|
@@ -309,6 +403,33 @@ class SoftDelete < ActiveRecord::Base
|
|
309
403
|
end
|
310
404
|
```
|
311
405
|
|
406
|
+
#### Discard
|
407
|
+
|
408
|
+
```ruby
|
409
|
+
class SoftDelete < ActiveRecord::Base
|
410
|
+
include Discard::Model
|
411
|
+
|
412
|
+
belongs_to :company
|
413
|
+
counter_culture :company
|
414
|
+
end
|
415
|
+
```
|
416
|
+
|
417
|
+
### PaperTrail integration
|
418
|
+
|
419
|
+
If you are using the [`paper_trail` gem](https://github.com/airblade/paper_trail)
|
420
|
+
and would like new versions to be created when the counter cache columns are
|
421
|
+
changed by counter_culture, you can set the `with_papertrail` option:
|
422
|
+
|
423
|
+
```ruby
|
424
|
+
class Review < ActiveRecord::Base
|
425
|
+
counter_culture :product, with_papertrail: true
|
426
|
+
end
|
427
|
+
|
428
|
+
class Product < ActiveRecord::Base
|
429
|
+
has_paper_trail
|
430
|
+
end
|
431
|
+
```
|
432
|
+
|
312
433
|
#### Polymorphic associations
|
313
434
|
|
314
435
|
counter_culture now supports polymorphic associations of one level only.
|