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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1621a4f3c4f5d53c03ad4f6e2a62e9e23119a734
4
- data.tar.gz: c0c2e63f3ef20f39f98ca38a0339395212597104
3
+ metadata.gz: 462619d22d3bb8cbc486321638ba93bf37f67495
4
+ data.tar.gz: d208201ce3549375e459fea3dba479b21e60c17d
5
5
  SHA512:
6
- metadata.gz: 9a29284daa7c6fa23bb26cdc713fa7a9b71449a19bbd65b89eb7e48fe664c0dfe54fc3c64e38ee4227de8122728ed3421a6cb1287f73596593bf7f6d8c2c7c47
7
- data.tar.gz: a07629cc1a93ccb7c250d74a1be50f7c143f615b32785eeaae059a56ec5dc1d0b19ad2cb7964de2a3af68cf190c6e8a5706e69c91f4b529effd8645a20997352
6
+ metadata.gz: 15b8082a44e4a542d9033daa594dd8987f709d0306a666c6d1c3ff3a86e5d7926cf79fd3e6758713f4ce52fbf60b17611b9466d422d0bdace802a91f5d223b59
7
+ data.tar.gz: 5e59714fc6686f6b8baf927bbe199e1480e762363fe84257ac409cf21d347f1d2b89e6edf05286e3f64b87cfd180eea7544509eb5b37f85e3d5746ef837345ca
data/README.md CHANGED
@@ -1,66 +1,46 @@
1
1
  # ActiveRecord::Precount [![Build Status](https://travis-ci.org/k0kubun/activerecord-precount.svg?branch=master)](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
- ## Why ActiveRecord::Precount?
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
- ## Installation
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
- The option defines an additional association whose name is `favorites_count`.
34
- Its association type is not an ordinary one (i.e. `has_many`, `belongs_to`) but `count_loader`.
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
- @tweets = Tweet.all
41
- @tweets.each do |tweet|
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
- You can eagerly load `count_loader` association by `includes` or `preload`.
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
- @tweets = Tweet.all.preload(:favorites_count)
50
- @tweets.each do |tweet|
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
- Since it is association, you can preload nested `count_loader` association.
38
+ ## Installation
56
39
 
57
- ```rb
58
- @users = User.all.preload(tweets: :favorites_count)
59
- @users.each do |user|
60
- user.tweets.each do |tweet|
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
- ### Databases
74
-
75
- - sqlite
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
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Precount
3
- VERSION = "0.4.0"
3
+ VERSION = "0.4.1"
4
4
  end
5
5
  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.0
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