activerecord-precount 0.4.3 → 0.5.0
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 +34 -13
- data/benchmark.rb +17 -8
- data/lib/active_record/precount/base_extension.rb +1 -1
- data/lib/active_record/precount/join_dependency_extension.rb +39 -13
- data/lib/active_record/precount/relation_extension.rb +27 -1
- data/lib/active_record/precount/version.rb +1 -1
- data/sample/app/controllers/application_controller.rb +24 -4
- data/sample/app/views/application/index.html.erb +6 -4
- data/sample/db/migrate/20141122002518_create_tweets.rb +1 -0
- data/sample/db/migrate/20141122002548_create_favorites.rb +1 -0
- data/sample/db/schema.rb +4 -0
- data/test/cases/associations/eager_count_test.rb +46 -0
- data/test/cases/associations/eager_load_test.rb +19 -6
- data/test/cases/associations/precount_test.rb +13 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b808e109c4ff4e66a60696bef99eb38a78f2c729
|
4
|
+
data.tar.gz: abed0c7c35ef630938eec845e4439a7e62f54e90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36e5f8ad70b97aa1d58990791ec8a7de3e075783700d25e8ffc93be693c2d9f39b29da237b46d5159e750654ea3b50b80c99bf32c31118618025e63957aac590
|
7
|
+
data.tar.gz: c08a6d413c210e044e609e36c1e297803476ff52e6b54c6bd8be48b89c4aeafc3b2d759da86314b84858d18fdf29ecb0c9fda2b1f8403bbdce44dfa829bd5aa0
|
data/README.md
CHANGED
@@ -15,36 +15,55 @@ Tweet.all.each do |tweet|
|
|
15
15
|
p tweet.favorites.count
|
16
16
|
end
|
17
17
|
# SELECT `tweets`.* FROM `tweets`
|
18
|
-
# SELECT COUNT(*) FROM `
|
19
|
-
# SELECT COUNT(*) FROM `
|
20
|
-
# SELECT COUNT(*) FROM `
|
21
|
-
# SELECT COUNT(*) FROM `
|
22
|
-
# SELECT COUNT(*) FROM `
|
18
|
+
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 1
|
19
|
+
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 2
|
20
|
+
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 3
|
21
|
+
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 4
|
22
|
+
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 5
|
23
23
|
```
|
24
24
|
|
25
25
|
### Count eager loading
|
26
26
|
|
27
|
+
#### precount
|
28
|
+
|
27
29
|
With activerecord-precount gem installed, you can use `precount` method
|
28
30
|
to eagerly load counts of associated records.
|
31
|
+
Like `preload`, it loads counts by multiple queries
|
29
32
|
|
30
33
|
```rb
|
31
34
|
Tweet.all.precount(:favorites).each do |tweet|
|
32
35
|
p tweet.favorites.count
|
33
36
|
end
|
34
37
|
# SELECT `tweets`.* FROM `tweets`
|
35
|
-
# SELECT COUNT(`
|
38
|
+
# SELECT COUNT(`favorites`.`tweet_id`), `favorites`.`tweet_id` FROM `favorites` WHERE `favorites`.`tweet_id` IN (1, 2, 3, 4, 5) GROUP BY `favorites`.`tweet_id`
|
39
|
+
```
|
40
|
+
|
41
|
+
#### eager\_count
|
42
|
+
|
43
|
+
Like `eager_load`, `eager_count` method allows you to load counts by one JOIN query.
|
44
|
+
|
45
|
+
```rb
|
46
|
+
Tweet.all.eager_count(:favorites).each do |tweet|
|
47
|
+
p tweet.favorites.count
|
48
|
+
end
|
49
|
+
# SELECT `tweets`.`id` AS t0_r0, `tweets`.`tweet_id` AS t0_r1, `tweets`.`user_id` AS t0_r2, `tweets`.`created_at` AS t0_r3, `tweets`.`updated_at` AS t0_r4, COUNT(`favorites`.`id`) AS t1_r0 FROM `tweets` LEFT OUTER JOIN `favorites` ON `favorites`.`tweet_id` = `tweets`.`id` GROUP BY tweets.id
|
36
50
|
```
|
37
51
|
|
38
52
|
## Benchmark
|
39
53
|
|
40
|
-
|
41
|
-
|
42
|
-
|
54
|
+
The [result](https://travis-ci.org/k0kubun/activerecord-precount/jobs/49061937) of
|
55
|
+
[this benchmark](https://github.com/k0kubun/activerecord-precount/blob/40765d36ff0e0627cd0941b2c0a0f6573290c67e/benchmark.rb).
|
56
|
+
|
57
|
+
| | N+1 query | precount | eager\_count |
|
58
|
+
|:-- |:----------|:---------|:-------------|
|
59
|
+
| Time | 1.401 | 0.176 | 0.119 |
|
60
|
+
| Ratio | 1.0x | **7.9x faster** | **11.7x faster** |
|
43
61
|
|
44
62
|
```rb
|
45
63
|
# Tweet count is 50, and each tweet has 10 favorites
|
46
|
-
Tweet.
|
47
|
-
Tweet.
|
64
|
+
Tweet.all.map{ |t| t.favorites.count } # N+1 query
|
65
|
+
Tweet.precount(:favorites).map(&:favorites_count) # precount
|
66
|
+
Tweet.eager_count(:favorites).map(&:favorites_count) # eager_count
|
48
67
|
```
|
49
68
|
|
50
69
|
## Installation
|
@@ -69,8 +88,8 @@ gem 'activerecord-precount'
|
|
69
88
|
## Advanced Usage
|
70
89
|
|
71
90
|
### Nested eager loading
|
72
|
-
`Foo.precount(:bars)` automatically defines `bars_count` association for `Foo`.
|
73
|
-
|
91
|
+
`Foo.precount(:bars)` or `Foo.eager_count(:bars)` automatically defines `bars_count` association for `Foo`.
|
92
|
+
That enables you to preload the association and call `foo.bars_count`.
|
74
93
|
|
75
94
|
You can manually define `bars_count` with follwoing code.
|
76
95
|
|
@@ -103,9 +122,11 @@ Though precounted `bars.count` is faster than not-precounted one, the fallback i
|
|
103
122
|
```rb
|
104
123
|
# slow
|
105
124
|
Foo.precount(:bars).map { |f| f.bars.count }
|
125
|
+
Foo.eager_count(:bars).map { |f| f.bars.count }
|
106
126
|
|
107
127
|
# fast (recommended)
|
108
128
|
Foo.precount(:bars).map { |f| f.bars_count }
|
129
|
+
Foo.eager_count(:bars).map { |f| f.bars_count }
|
109
130
|
```
|
110
131
|
|
111
132
|
## License
|
data/benchmark.rb
CHANGED
@@ -8,8 +8,10 @@ require 'models/tweet'
|
|
8
8
|
RBench.run(50) do
|
9
9
|
column :counter_cache, title: 'counter_cache'
|
10
10
|
column :left_join, title: 'LEFT JOIN'
|
11
|
-
column :
|
12
|
-
column :precount, title: '
|
11
|
+
column :eager_count, title: 'eager_count'
|
12
|
+
column :precount, title: 'precount'
|
13
|
+
column :slow_eager_count, title: 'slow eager_count'
|
14
|
+
column :slow_precount, title: 'slow precount'
|
13
15
|
column :has_many, title: 'preload'
|
14
16
|
column :count_query, title: 'N+1 COUNT'
|
15
17
|
|
@@ -17,6 +19,9 @@ RBench.run(50) do
|
|
17
19
|
select('tweets.*, COUNT(favorites.id) AS joined_count').group('tweets.id')
|
18
20
|
|
19
21
|
def prepare_records(tweets_count, favorites_count)
|
22
|
+
Tweet.delete_all
|
23
|
+
Favorite.delete_all
|
24
|
+
|
20
25
|
tweets_count.times do
|
21
26
|
t = Tweet.create(favorites_count_cache: 0)
|
22
27
|
favorites_count.times { Favorite.create(tweet: t) }
|
@@ -34,12 +39,16 @@ RBench.run(50) do
|
|
34
39
|
prepare_records(tweets_count, favorites_count)
|
35
40
|
|
36
41
|
report "N = #{tweets_count}, count = #{favorites_count}" do
|
37
|
-
counter_cache { Tweet.
|
38
|
-
left_join {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
42
|
+
counter_cache { Tweet.all.map(&:favorites_count_cache) }
|
43
|
+
left_join { Tweet.joins('LEFT JOIN favorites ON tweets.id = favorites.tweet_id').
|
44
|
+
select('tweets.*, COUNT(favorites.id) AS joined_count').
|
45
|
+
group('tweets.id').map(&:joined_count) }
|
46
|
+
eager_count { Tweet.eager_count(:favorites).map(&:favorites_count) }
|
47
|
+
precount { Tweet.precount(:favorites).map(&:favorites_count) }
|
48
|
+
slow_eager_count { Tweet.eager_count(:favorites).map { |t| t.favorites.count } }
|
49
|
+
slow_precount { Tweet.precount(:favorites).map { |t| t.favorites.count } }
|
50
|
+
has_many { Tweet.preload(:favorites).map{ |t| t.favorites.size } }
|
51
|
+
count_query { Tweet.all.map{ |t| t.favorites.count } }
|
43
52
|
end
|
44
53
|
end
|
45
54
|
end
|
@@ -1,22 +1,48 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# This imitates EagerLoadPolymorphicError
|
3
|
-
class EagerLoadCountLoaderError < ActiveRecordError
|
4
|
-
def initialize(reflection)
|
5
|
-
super("Cannot eagerly load the count_loader association #{reflection.name.inspect}")
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
2
|
module Precount
|
10
3
|
module JoinDependencyExtension
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
class CountTable < Associations::JoinDependency::Aliases::Table
|
5
|
+
def column_aliases
|
6
|
+
columns.map { |column| table[column.name].count.as Arel.sql column.alias }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def aliases
|
11
|
+
Associations::JoinDependency::Aliases.new join_root.each_with_index.map { |join_part,i|
|
12
|
+
if join_part.is_a?(Associations::JoinDependency::JoinAssociation) && join_part.reflection.macro == :count_loader
|
13
|
+
# select COUNT(primary_key)
|
14
|
+
column_name = join_part.reflection.klass.primary_key
|
15
|
+
column = Associations::JoinDependency::Aliases::Column.new column_name, "t#{i}_r0"
|
16
|
+
CountTable.new(join_part, [column])
|
17
|
+
else
|
18
|
+
# original aliases' internal function
|
19
|
+
columns = join_part.column_names.each_with_index.map { |column_name,j|
|
20
|
+
Associations::JoinDependency::Aliases::Column.new column_name, "t#{i}_r#{j}"
|
21
|
+
}
|
22
|
+
Associations::JoinDependency::Aliases::Table.new(join_part, columns)
|
16
23
|
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# instantiate only count_loader and pass others to super
|
30
|
+
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
31
|
+
normal_children = []
|
32
|
+
|
33
|
+
parent.children.each do |node|
|
34
|
+
if node.reflection.macro != :count_loader
|
35
|
+
normal_children << node
|
36
|
+
next
|
37
|
+
end
|
38
|
+
|
39
|
+
key = aliases.column_alias(node, node.primary_key)
|
40
|
+
ar_parent.association(node.reflection.name).target = row[key].to_i
|
17
41
|
end
|
42
|
+
return if normal_children.blank?
|
18
43
|
|
19
|
-
|
44
|
+
normal_parent = Associations::JoinDependency::JoinBase.new(parent.base_klass, normal_children)
|
45
|
+
super(ar_parent, normal_parent, row, rs, seen, model_cache, aliases)
|
20
46
|
end
|
21
47
|
end
|
22
48
|
end
|
@@ -13,11 +13,23 @@ module ActiveRecord
|
|
13
13
|
self
|
14
14
|
end
|
15
15
|
|
16
|
+
def eager_count(*args)
|
17
|
+
check_if_method_has_arguments!(:eager_count, args)
|
18
|
+
spawn.eager_count!(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def eager_count!(*args)
|
22
|
+
define_count_loader!(*args)
|
23
|
+
|
24
|
+
self.eager_load_values += args.map { |arg| :"#{arg}_count" }
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
16
28
|
private
|
17
29
|
|
18
30
|
def define_count_loader!(*args)
|
19
31
|
args.each do |arg|
|
20
|
-
raise ArgumentError, "#{
|
32
|
+
raise ArgumentError, "Association named '#{arg}' was not found on #{klass.name}." unless has_reflection?(arg)
|
21
33
|
next if has_reflection?(counter_name = :"#{arg}_count")
|
22
34
|
|
23
35
|
options = reflection_for(arg).options.slice(*Associations::Builder::CountLoader.valid_options)
|
@@ -25,6 +37,20 @@ module ActiveRecord
|
|
25
37
|
Reflection.add_reflection(model, counter_name, reflection)
|
26
38
|
end
|
27
39
|
end
|
40
|
+
|
41
|
+
def apply_join_dependency(relation, join_dependency)
|
42
|
+
relation = super(relation, join_dependency)
|
43
|
+
|
44
|
+
# to count associated records in JOIN query, group scope is necessary
|
45
|
+
join_dependency.reflections.each do |reflection|
|
46
|
+
if reflection.macro == :count_loader
|
47
|
+
ar = reflection.active_record
|
48
|
+
relation = relation.group("#{ar.table_name}.#{ar.primary_key}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
relation
|
53
|
+
end
|
28
54
|
end
|
29
55
|
end
|
30
56
|
end
|
@@ -1,13 +1,33 @@
|
|
1
1
|
class ApplicationController < ActionController::Base
|
2
|
+
AVAILABLE_LOADERS = %w[N+1 precount eager_count].freeze
|
3
|
+
DEFAULT_LOADER = 'N+1'
|
4
|
+
|
2
5
|
def index
|
3
6
|
@tweets = Tweet.all
|
4
|
-
|
7
|
+
case loader
|
8
|
+
when 'precount'
|
5
9
|
@tweets = @tweets.precount(:replies).preload(in_reply_to: :favorites_count)
|
10
|
+
when 'eager_count'
|
11
|
+
@tweets = @tweets.eager_count(:replies).eager_load(in_reply_to: :favorites_count)
|
6
12
|
end
|
7
13
|
end
|
8
14
|
|
9
|
-
def
|
10
|
-
|
15
|
+
def precount?
|
16
|
+
loader == 'precount'
|
17
|
+
end
|
18
|
+
helper_method :precount?
|
19
|
+
|
20
|
+
def eager_count?
|
21
|
+
loader == 'eager_count'
|
22
|
+
end
|
23
|
+
helper_method :eager_count?
|
24
|
+
|
25
|
+
def loader
|
26
|
+
if AVAILABLE_LOADERS.include?(params[:loader])
|
27
|
+
params[:loader]
|
28
|
+
else
|
29
|
+
DEFAULT_LOADER
|
30
|
+
end
|
11
31
|
end
|
12
|
-
helper_method :
|
32
|
+
helper_method :loader
|
13
33
|
end
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<%= tweet.id %>
|
24
24
|
</td>
|
25
25
|
<td>
|
26
|
-
<%= tweet.replies.count %>
|
26
|
+
<%= (precount? || eager_count?) ? tweet.replies_count : tweet.replies.count %>
|
27
27
|
</td>
|
28
28
|
<td>
|
29
29
|
<%= tweet.in_reply_to.try(:id) %>
|
@@ -38,12 +38,14 @@
|
|
38
38
|
<% finish = Time.now %>
|
39
39
|
|
40
40
|
<p>
|
41
|
-
|
41
|
+
Loader: <%= loader %>
|
42
42
|
</p>
|
43
43
|
<p>
|
44
44
|
Time: <%= "%.1f" % ((finish - start) * 1000) %>ms
|
45
45
|
</p>
|
46
46
|
|
47
47
|
<p>
|
48
|
-
|
49
|
-
|
48
|
+
<% ApplicationController::AVAILABLE_LOADERS.each do |ld| %>
|
49
|
+
<%= link_to_unless loader == ld, ld, url_for(loader: ld) %>
|
50
|
+
<% end %>
|
51
|
+
</p>
|
data/sample/db/schema.rb
CHANGED
@@ -20,6 +20,8 @@ ActiveRecord::Schema.define(version: 20141122002555) do
|
|
20
20
|
t.datetime "updated_at"
|
21
21
|
end
|
22
22
|
|
23
|
+
add_index "favorites", ["tweet_id"], name: "index_favorites_on_tweet_id", using: :btree
|
24
|
+
|
23
25
|
create_table "tweets", force: :cascade do |t|
|
24
26
|
t.integer "in_reply_to_tweet_id", limit: 4
|
25
27
|
t.integer "user_id", limit: 4
|
@@ -27,6 +29,8 @@ ActiveRecord::Schema.define(version: 20141122002555) do
|
|
27
29
|
t.datetime "updated_at"
|
28
30
|
end
|
29
31
|
|
32
|
+
add_index "tweets", ["in_reply_to_tweet_id"], name: "index_tweets_on_in_reply_to_tweet_id", using: :btree
|
33
|
+
|
30
34
|
create_table "users", force: :cascade do |t|
|
31
35
|
t.datetime "created_at"
|
32
36
|
t.datetime "updated_at"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'cases/helper'
|
2
|
+
|
3
|
+
class EagerCountTest < ActiveRecord::CountLoader::TestCase
|
4
|
+
def setup
|
5
|
+
tweets_count.times.map do |index|
|
6
|
+
tweet = Tweet.create
|
7
|
+
index.times { Favorite.create(tweet: tweet) }
|
8
|
+
end
|
9
|
+
|
10
|
+
if Tweet.has_reflection?(:favs_count)
|
11
|
+
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 2
|
12
|
+
Tweet._reflections.delete('favs_count')
|
13
|
+
else
|
14
|
+
Tweet._reflections.delete(:favs_count)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
[Tweet, Favorite].each(&:delete_all)
|
21
|
+
end
|
22
|
+
|
23
|
+
def tweets_count
|
24
|
+
3
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_eager_count_defines_count_loader
|
28
|
+
assert_equal(Tweet.has_reflection?(:favs_count), false)
|
29
|
+
Tweet.eager_count(:favs).map(&:favs_count)
|
30
|
+
assert_equal(Tweet.has_reflection?(:favs_count), true)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_eager_count_has_many_with_count_loader_does_not_execute_n_1_queries
|
34
|
+
assert_queries(1 + tweets_count) { Tweet.all.map { |t| t.favorites.count } }
|
35
|
+
assert_queries(1 + tweets_count) { Tweet.all.map(&:favorites_count) }
|
36
|
+
assert_queries(1) { Tweet.eager_count(:favorites).map { |t| t.favorites.count } }
|
37
|
+
assert_queries(1) { Tweet.eager_count(:favorites).map(&:favorites_count) }
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_eager_count_has_many_counts_properly
|
41
|
+
expected = Tweet.order(id: :asc).map { |t| t.favorites.count }
|
42
|
+
assert_equal(Tweet.order(id: :asc).map(&:favorites_count), expected)
|
43
|
+
assert_equal(Tweet.order(id: :asc).eager_count(:favorites).map { |t| t.favorites.count }, expected)
|
44
|
+
assert_equal(Tweet.order(id: :asc).eager_count(:favorites).map(&:favorites_count), expected)
|
45
|
+
end
|
46
|
+
end
|
@@ -4,17 +4,30 @@ require 'models/tweet'
|
|
4
4
|
|
5
5
|
class EagerLoadTest < ActiveRecord::CountLoader::TestCase
|
6
6
|
def setup
|
7
|
-
|
8
|
-
|
7
|
+
tweets_count.times.map do |index|
|
8
|
+
tweet = Tweet.create
|
9
|
+
index.times { Favorite.create(tweet: tweet) }
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
13
|
def teardown
|
12
14
|
[Tweet, Favorite].each(&:delete_all)
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def tweets_count
|
18
|
+
3
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_eager_load_does_not_execute_n_1_queries
|
22
|
+
assert_queries(1 + tweets_count) { Tweet.all.map { |t| t.favorites.count } }
|
23
|
+
assert_queries(1 + tweets_count) { Tweet.all.map(&:favorites_count) }
|
24
|
+
assert_queries(1) { Tweet.eager_load(:favorites_count).map(&:favorites_count) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_eager_loaded_count_loader_counts_properly
|
28
|
+
expected = Tweet.order(id: :asc).map { |t| t.favorites.count }
|
29
|
+
assert_equal(Tweet.order(id: :asc).map(&:favorites_count), expected)
|
30
|
+
assert_equal(Tweet.order(id: :asc).eager_load(:favorites_count).map { |t| t.favorites.count }, expected)
|
31
|
+
assert_equal(Tweet.order(id: :asc).eager_load(:favorites_count).map(&:favorites_count), expected)
|
19
32
|
end
|
20
33
|
end
|
@@ -6,6 +6,14 @@ class PrecountTest < ActiveRecord::CountLoader::TestCase
|
|
6
6
|
tweet = Tweet.create
|
7
7
|
index.times { Favorite.create(tweet: tweet) }
|
8
8
|
end
|
9
|
+
|
10
|
+
if Tweet.has_reflection?(:favs_count)
|
11
|
+
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 2
|
12
|
+
Tweet._reflections.delete('favs_count')
|
13
|
+
else
|
14
|
+
Tweet._reflections.delete(:favs_count)
|
15
|
+
end
|
16
|
+
end
|
9
17
|
end
|
10
18
|
|
11
19
|
def teardown
|
@@ -16,11 +24,10 @@ class PrecountTest < ActiveRecord::CountLoader::TestCase
|
|
16
24
|
3
|
17
25
|
end
|
18
26
|
|
19
|
-
def
|
20
|
-
assert_equal(Tweet.
|
21
|
-
|
22
|
-
|
23
|
-
assert_queries(2) { Tweet.precount(:favs).map(&:favs_count) }
|
27
|
+
def test_precount_defines_count_loader
|
28
|
+
assert_equal(Tweet.has_reflection?(:favs_count), false)
|
29
|
+
Tweet.precount(:favs).map(&:favs_count)
|
30
|
+
assert_equal(Tweet.has_reflection?(:favs_count), true)
|
24
31
|
end
|
25
32
|
|
26
33
|
def test_precount_has_many_with_count_loader_does_not_execute_n_1_queries
|
@@ -33,6 +40,7 @@ class PrecountTest < ActiveRecord::CountLoader::TestCase
|
|
33
40
|
def test_precount_has_many_counts_properly
|
34
41
|
expected = Tweet.all.map { |t| t.favorites.count }
|
35
42
|
assert_equal(Tweet.all.map(&:favorites_count), expected)
|
43
|
+
assert_equal(Tweet.precount(:favorites).map { |t| t.favorites.count }, expected)
|
36
44
|
assert_equal(Tweet.precount(:favorites).map(&:favorites_count), expected)
|
37
45
|
end
|
38
46
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-precount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takashi Kokubun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01
|
11
|
+
date: 2015-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -251,6 +251,7 @@ files:
|
|
251
251
|
- sample/public/robots.txt
|
252
252
|
- sample/vendor/assets/javascripts/.keep
|
253
253
|
- sample/vendor/assets/stylesheets/.keep
|
254
|
+
- test/cases/associations/eager_count_test.rb
|
254
255
|
- test/cases/associations/eager_load_test.rb
|
255
256
|
- test/cases/associations/includes_test.rb
|
256
257
|
- test/cases/associations/precount_test.rb
|