activerecord-precount 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +30 -52
- data/lib/active_record/precount/base_extension.rb +12 -0
- data/lib/active_record/precount/collection_proxy_extension.rb +18 -0
- data/lib/active_record/precount/extend.rb +2 -0
- data/lib/active_record/precount/relation_extension.rb +0 -12
- data/lib/active_record/precount/version.rb +1 -1
- data/test/cases/associations/precount_test.rb +2 -0
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 462619d22d3bb8cbc486321638ba93bf37f67495
|
4
|
+
data.tar.gz: d208201ce3549375e459fea3dba479b21e60c17d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15b8082a44e4a542d9033daa594dd8987f709d0306a666c6d1c3ff3a86e5d7926cf79fd3e6758713f4ce52fbf60b17611b9466d422d0bdace802a91f5d223b59
|
7
|
+
data.tar.gz: 5e59714fc6686f6b8baf927bbe199e1480e762363fe84257ac409cf21d347f1d2b89e6edf05286e3f64b87cfd180eea7544509eb5b37f85e3d5746ef837345ca
|
data/README.md
CHANGED
@@ -1,66 +1,46 @@
|
|
1
1
|
# ActiveRecord::Precount [](https://travis-ci.org/k0kubun/activerecord-precount)
|
2
2
|
|
3
|
-
N+1 count query killer for ActiveRecord.
|
3
|
+
N+1 count query killer for ActiveRecord. Yet another counter\_cache alternative.
|
4
4
|
ActiveRecord::Precount allows you to cache count of associated records by eager loading.
|
5
5
|
|
6
|
-
##
|
7
|
-
Rails provides a way to resolve N+1 count query, which is [belongs\_to's counter\_cache option](http://guides.rubyonrails.org/association_basics.html#counter-cache).
|
8
|
-
It requires a column to cache the count. But adding a column just for count cache is overkill.
|
9
|
-
|
10
|
-
Thus this plugin enables you to preload counts in the same way as `has_many` and `belongs_to`.
|
11
|
-
`count_loader` is an ActiveRecord's association, which is preloadable by `preload` or `includes`.
|
6
|
+
## Synopsis
|
12
7
|
|
13
|
-
|
14
|
-
|
15
|
-
Add this line to your application's Gemfile:
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
gem 'activerecord-precount'
|
19
|
-
```
|
20
|
-
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
### Enable count\_loader option
|
24
|
-
First, enable your has\_many association's count\_loader option.
|
25
|
-
|
26
|
-
```diff
|
27
|
-
class Tweet
|
28
|
-
- has_many :favorites
|
29
|
-
+ has_many :favorites, count_loader: true
|
30
|
-
end
|
31
|
-
```
|
8
|
+
### N+1 count query
|
32
9
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
### Preload the association
|
37
|
-
This association works well by default.
|
10
|
+
Sometimes you may see many count queries for one association.
|
11
|
+
You can use counter\_cache to solve it, but it costs much to use counter\_cache.
|
38
12
|
|
39
13
|
```rb
|
40
|
-
|
41
|
-
|
42
|
-
p tweets.favorites_count # same as tweets.favorites.count
|
14
|
+
Tweet.all.each do |tweet|
|
15
|
+
p tweet.favorites.count
|
43
16
|
end
|
17
|
+
# SELECT `tweets`.* FROM `tweets`
|
18
|
+
# SELECT COUNT(*) FROM `tweets` WHERE `tweets`.`tweet_id` = 1
|
19
|
+
# SELECT COUNT(*) FROM `tweets` WHERE `tweets`.`tweet_id` = 2
|
20
|
+
# SELECT COUNT(*) FROM `tweets` WHERE `tweets`.`tweet_id` = 3
|
21
|
+
# SELECT COUNT(*) FROM `tweets` WHERE `tweets`.`tweet_id` = 4
|
22
|
+
# SELECT COUNT(*) FROM `tweets` WHERE `tweets`.`tweet_id` = 5
|
44
23
|
```
|
45
24
|
|
46
|
-
|
25
|
+
### Count eager loading
|
26
|
+
|
27
|
+
With activerecord-precount gem installed, you can use `precount` method
|
28
|
+
to eagerly load counts of associated records.
|
47
29
|
|
48
30
|
```rb
|
49
|
-
|
50
|
-
|
51
|
-
p tweets.favorites_count # this line doesn't execute an additional query
|
31
|
+
Tweet.all.precount(:favorites).each do |tweet|
|
32
|
+
p tweet.favorites.count
|
52
33
|
end
|
34
|
+
# SELECT `tweets`.* FROM `tweets`
|
35
|
+
# SELECT `tweets`.`in_reply_to_tweet_id` FROM `tweets` WHERE `tweets`.`tweet_id` IN (1, 2, 3, 4, 5)
|
53
36
|
```
|
54
37
|
|
55
|
-
|
38
|
+
## Installation
|
56
39
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
p tweet.favorites_count # this line doesn't execute an additional query
|
62
|
-
end
|
63
|
-
end
|
40
|
+
Add this line to your application's Gemfile:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
gem 'activerecord-precount'
|
64
44
|
```
|
65
45
|
|
66
46
|
## Supported Versions
|
@@ -69,12 +49,10 @@ end
|
|
69
49
|
- 2.0, 2.1, 2.2
|
70
50
|
- Rails
|
71
51
|
- 4.1, 4.2
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
-
|
76
|
-
- mysql
|
77
|
-
- postgresql
|
52
|
+
- Databases
|
53
|
+
- sqlite
|
54
|
+
- mysql
|
55
|
+
- postgresql
|
78
56
|
|
79
57
|
## Testing
|
80
58
|
|
@@ -2,6 +2,18 @@ module ActiveRecord
|
|
2
2
|
module Precount
|
3
3
|
module BaseExtension
|
4
4
|
delegate :precount, to: :all
|
5
|
+
|
6
|
+
def has_reflection?(name)
|
7
|
+
reflection_for(name).present?
|
8
|
+
end
|
9
|
+
|
10
|
+
def reflection_for(name)
|
11
|
+
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 2
|
12
|
+
reflections[name.to_s]
|
13
|
+
else
|
14
|
+
reflections[name.to_sym]
|
15
|
+
end
|
16
|
+
end
|
5
17
|
end
|
6
18
|
end
|
7
19
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Precount
|
3
|
+
module CollectionProxyExtension
|
4
|
+
def count(*args)
|
5
|
+
return super(*args) if args.present?
|
6
|
+
|
7
|
+
counter_name = :"#{@association.reflection.name}_count"
|
8
|
+
owner = @association.owner
|
9
|
+
|
10
|
+
if owner.class.has_reflection?(counter_name)
|
11
|
+
owner.send(counter_name)
|
12
|
+
else
|
13
|
+
super(*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "active_record/precount/base_extension"
|
2
|
+
require "active_record/precount/collection_proxy_extension"
|
2
3
|
require "active_record/precount/has_many_extension"
|
3
4
|
require "active_record/precount/join_dependency_extension"
|
4
5
|
require "active_record/precount/preloader_extension"
|
@@ -12,6 +13,7 @@ ActiveSupport.on_load(:active_record) do
|
|
12
13
|
Reflection.send(:prepend, Precount::ReflectionExtension)
|
13
14
|
Associations::Preloader.send(:prepend, Precount::PreloaderExtension)
|
14
15
|
Associations::JoinDependency.send(:prepend, Precount::JoinDependencyExtension)
|
16
|
+
Associations::CollectionProxy.send(:prepend, Precount::CollectionProxyExtension)
|
15
17
|
Associations::Builder::HasMany.send(:prepend, Precount::Builder::HasManyExtension)
|
16
18
|
Reflection::AssociationReflection.send(:prepend, Precount::AssociationReflectionExtension)
|
17
19
|
end
|
@@ -25,18 +25,6 @@ module ActiveRecord
|
|
25
25
|
Reflection.add_reflection(model, counter_name, reflection)
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
def has_reflection?(name)
|
30
|
-
reflection_for(name).present?
|
31
|
-
end
|
32
|
-
|
33
|
-
def reflection_for(name)
|
34
|
-
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 2
|
35
|
-
reflections[name.to_s]
|
36
|
-
else
|
37
|
-
reflections[name.to_sym]
|
38
|
-
end
|
39
|
-
end
|
40
28
|
end
|
41
29
|
end
|
42
30
|
end
|
@@ -19,12 +19,14 @@ class PrecountTest < ActiveRecord::CountLoader::TestCase
|
|
19
19
|
def test_precount_has_many_does_not_execute_n_1_queries
|
20
20
|
assert_equal(Tweet.reflections['favs_count'].present?, false)
|
21
21
|
assert_queries(1 + tweets_count) { Tweet.all.map { |t| t.favs.count } }
|
22
|
+
assert_queries(2) { Tweet.precount(:favs).map { |t| t.favs.count } }
|
22
23
|
assert_queries(2) { Tweet.precount(:favs).map(&:favs_count) }
|
23
24
|
end
|
24
25
|
|
25
26
|
def test_precount_has_many_with_count_loader_does_not_execute_n_1_queries
|
26
27
|
assert_queries(1 + tweets_count) { Tweet.all.map { |t| t.favorites.count } }
|
27
28
|
assert_queries(1 + tweets_count) { Tweet.all.map(&:favorites_count) }
|
29
|
+
assert_queries(2) { Tweet.precount(:favorites).map { |t| t.favorites.count } }
|
28
30
|
assert_queries(2) { Tweet.precount(:favorites).map(&:favorites_count) }
|
29
31
|
end
|
30
32
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-precount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takashi Kokubun
|
@@ -186,6 +186,7 @@ files:
|
|
186
186
|
- lib/active_record/associations/count_loader.rb
|
187
187
|
- lib/active_record/associations/preloader/count_loader.rb
|
188
188
|
- lib/active_record/precount/base_extension.rb
|
189
|
+
- lib/active_record/precount/collection_proxy_extension.rb
|
189
190
|
- lib/active_record/precount/extend.rb
|
190
191
|
- lib/active_record/precount/has_many_extension.rb
|
191
192
|
- lib/active_record/precount/join_dependency_extension.rb
|