activerecord-precount 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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