activerecord-has_count 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.travis.yml +7 -0
- data/README.md +3 -3
- data/activerecord-has_count.gemspec +2 -2
- data/benchmarks/Gemfile +9 -0
- data/benchmarks/README.md +16 -0
- data/benchmarks/benchmark.rb +60 -0
- data/lib/active_record/associations/has_count.rb +1 -14
- data/lib/activerecord-has_count/model.rb +23 -0
- data/lib/{active_record/has_count → activerecord-has_count}/version.rb +1 -1
- data/lib/activerecord-has_count.rb +6 -6
- data/spec/database.yml +13 -0
- data/spec/eager_load_spec.rb +28 -0
- data/spec/includes_spec.rb +57 -0
- data/spec/models/reply.rb +3 -0
- data/spec/models/tweet.rb +4 -0
- data/spec/preload_spec.rb +57 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/assert_queries.rb +54 -0
- data/spec/support/factories.rb +4 -0
- metadata +31 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69e774d016a1d4b507eb55f8f0f9504cbf198a3b
|
4
|
+
data.tar.gz: abbd2d57baa8ebdf82d65ce8f6080febafaaaf57
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ef3dacda31025b0d12def2faa3b14a75b43ab114a0389489a7962bc0e1c53542402991e0809198192ba8c28cc77c0bfd56c7533f79ca05a5cc6fe0406c65b94
|
7
|
+
data.tar.gz: 30460a520bfd15c0f131ec19091415976834fb0b876bc4c362148bc604f34a71827b39e76e33f00d1f07d03f8e4148e1e480191829e007ad4daad26dad74a73c
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-c
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# ActiveRecord::HasCount
|
1
|
+
# ActiveRecord::HasCount [![Build Status](https://travis-ci.org/k0kubun/activerecord-has_count.png?branch=master)](https://travis-ci.org/k0kubun/activerecord-has_count)
|
2
2
|
|
3
3
|
N+1 count query killer for ActiveRecord
|
4
4
|
ActiveRecord::HasCount allows you to cache count of associated records by eager loading
|
@@ -20,7 +20,7 @@ gem 'activerecord-has_count'
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
### Add
|
23
|
+
### Add has\_count scope
|
24
24
|
First, call `has_count` with an association whose count you want to preload
|
25
25
|
|
26
26
|
```rb
|
@@ -63,7 +63,7 @@ end
|
|
63
63
|
|
64
64
|
## Contributing
|
65
65
|
|
66
|
-
1. Fork it ( https://github.com/k0kubun/has_count/fork )
|
66
|
+
1. Fork it ( https://github.com/k0kubun/activerecord-has_count/fork )
|
67
67
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
68
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
69
|
4. Push to the branch (`git push origin my-new-feature`)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
lib = File.expand_path('../lib', __FILE__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require '
|
3
|
+
require 'activerecord-has_count/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "activerecord-has_count"
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
19
|
spec.required_ruby_version = ">= 1.9.2"
|
20
|
-
spec.add_runtime_dependency "activerecord", ">= 3.0"
|
20
|
+
spec.add_runtime_dependency "activerecord", ">= 3.2.0"
|
21
21
|
spec.add_development_dependency "rspec", "~> 3.0.0"
|
22
22
|
spec.add_development_dependency "factory_girl", "~> 4.2.0"
|
23
23
|
spec.add_development_dependency "sqlite3"
|
data/benchmarks/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# N+1 COUNT query benchmarks
|
2
|
+
|
3
|
+
## Preparation
|
4
|
+
|
5
|
+
```bash
|
6
|
+
$ git clone git@github.com:k0kubun/activerecord-has_count
|
7
|
+
$ cd activerecord-has_count
|
8
|
+
$ mysql -uroot -e"create database bench"
|
9
|
+
$ bundle install
|
10
|
+
```
|
11
|
+
|
12
|
+
## Run
|
13
|
+
|
14
|
+
```bash
|
15
|
+
$ bundle exec ruby benchmark.rb
|
16
|
+
```
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "benchmark"
|
2
|
+
require "active_record"
|
3
|
+
require "activerecord-import"
|
4
|
+
require "activerecord-has_count"
|
5
|
+
|
6
|
+
spec_dir = File.expand_path("../../spec", __FILE__)
|
7
|
+
Dir[File.join(spec_dir, "models/*.rb")].each { |f| require f }
|
8
|
+
|
9
|
+
database_yml = File.join(spec_dir, "database.yml")
|
10
|
+
ActiveRecord::Base.configurations["bench"] = YAML.load_file(database_yml)["bench"]
|
11
|
+
ActiveRecord::Base.establish_connection :bench
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define do
|
14
|
+
create_table :tweets, force: true do |t|
|
15
|
+
t.column :created_at, :datetime
|
16
|
+
t.column :updated_at, :datetime
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :replies, force: true do |t|
|
20
|
+
t.column :tweet_id, :integer
|
21
|
+
t.column :created_at, :datetime
|
22
|
+
t.column :updated_at, :datetime
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
[Tweet, Reply].each(&:delete_all)
|
27
|
+
|
28
|
+
TWEET_COUNT = 5
|
29
|
+
REPLY_COUNT = 10000
|
30
|
+
|
31
|
+
TWEET_COUNT.times do
|
32
|
+
tweet = Tweet.create
|
33
|
+
|
34
|
+
replies = REPLY_COUNT.times.map do
|
35
|
+
Reply.new(tweet: tweet)
|
36
|
+
end
|
37
|
+
Reply.import(replies, validate: false)
|
38
|
+
end
|
39
|
+
|
40
|
+
Benchmark.bmbm do |bench|
|
41
|
+
bench.report("COUNT association") do
|
42
|
+
tweets = Tweet.first(TWEET_COUNT)
|
43
|
+
|
44
|
+
tweets.each { |t| t.replies.count }
|
45
|
+
end
|
46
|
+
|
47
|
+
bench.report("LEFT JOIN") do
|
48
|
+
tweets = Tweet.joins('LEFT JOIN replies ON tweets.id = replies.tweet_id').
|
49
|
+
select('tweets.*, COUNT(replies.id) AS replies_count').
|
50
|
+
group('tweets.id').first(TWEET_COUNT)
|
51
|
+
|
52
|
+
tweets.each { |t| t.replies_count }
|
53
|
+
end
|
54
|
+
|
55
|
+
bench.report("size of preloaded association") do
|
56
|
+
tweets = Tweet.preload(:replies).first(TWEET_COUNT)
|
57
|
+
|
58
|
+
tweets.each { |t| t.replies.size }
|
59
|
+
end
|
60
|
+
end
|
@@ -1,20 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
|
-
module ClassMethods
|
4
|
-
module HasCount
|
5
|
-
private
|
6
|
-
|
7
|
-
def has_count(name, scope = nil, options = {}, &extension)
|
8
|
-
name_with_count = :"#{name}_count"
|
9
|
-
|
10
|
-
reflection = Builder::HasCount.build(self, name_with_count, scope, options, &extension)
|
11
|
-
Reflection.add_reflection(self, name_with_count, reflection)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
3
|
class HasCount < SingularAssociation
|
17
|
-
# Not preloaded behaviour of
|
4
|
+
# Not preloaded behaviour of has_count association
|
18
5
|
# When this method is called, it will be N+1 query
|
19
6
|
def load_target
|
20
7
|
count_target = name_without_count.to_sym
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module HasCount
|
3
|
+
module Model
|
4
|
+
def self.included(model)
|
5
|
+
model.singleton_class.class_eval do
|
6
|
+
include ClassMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
private
|
12
|
+
|
13
|
+
def has_count(name, scope = nil, options = {}, &extension)
|
14
|
+
name_with_count = :"#{name}_count"
|
15
|
+
|
16
|
+
reflection = ActiveRecord::Associations::Builder::HasCount.
|
17
|
+
build(self, name_with_count, scope, options, &extension)
|
18
|
+
ActiveRecord::Reflection.add_reflection(self, name_with_count, reflection)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
|
+
require "active_record"
|
2
|
+
require "active_support/lazy_load_hooks"
|
3
|
+
|
1
4
|
require "active_record/associations/has_count"
|
2
5
|
require "active_record/associations/builder/has_count"
|
3
6
|
require "active_record/associations/preloader/has_count"
|
4
7
|
require "active_record/associations/join_dependency/has_count"
|
5
8
|
require "active_record/reflection/has_count"
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
include HasCount
|
11
|
-
end
|
12
|
-
end
|
10
|
+
ActiveSupport.on_load(:active_record) do
|
11
|
+
require "activerecord-has_count/model"
|
12
|
+
ActiveRecord::Base.send(:include, ActiveRecord::HasCount::Model)
|
13
13
|
end
|
14
14
|
|
15
15
|
module ActiveRecord
|
data/spec/database.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "#eager_load" do
|
4
|
+
describe "has_count association" do
|
5
|
+
let(:tweets_count) { 3 }
|
6
|
+
let(:tweets) do
|
7
|
+
tweets_count.times.map do
|
8
|
+
FactoryGirl.create(:tweet)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
tweets.each_with_index do |tweet, index|
|
14
|
+
index.times do
|
15
|
+
FactoryGirl.create(:reply, tweet: tweet)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises ActiveRecord::EagerLoadHasCountError" do
|
21
|
+
# Currently this is not supported because I can't come up with how to implement.
|
22
|
+
# I'm waiting for your contribution.
|
23
|
+
expect {
|
24
|
+
Tweet.all.eager_load(:replies_count).map(&:replies_count)
|
25
|
+
}.to raise_error(ActiveRecord::EagerLoadHasCountError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "#includes" do
|
4
|
+
describe "builtin associations" do
|
5
|
+
let(:replies_count) { 3 }
|
6
|
+
let!(:tweet) { FactoryGirl.create(:tweet) }
|
7
|
+
before do
|
8
|
+
replies_count.times do
|
9
|
+
FactoryGirl.create(:reply, tweet: tweet)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "given has_many association" do
|
14
|
+
it "works as usual" do
|
15
|
+
tweet = Tweet.includes(:replies).first
|
16
|
+
expect(tweet.replies.count).to eq(replies_count)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "given belongs_to association" do
|
21
|
+
let!(:reply) { FactoryGirl.create(:reply, tweet: tweet) }
|
22
|
+
|
23
|
+
it "works as usual" do
|
24
|
+
included_reply = Reply.includes(:tweet).find(reply.id)
|
25
|
+
expect(included_reply.tweet).to eq(tweet)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "has_count association" do
|
31
|
+
let(:tweets_count) { 3 }
|
32
|
+
let(:tweets) do
|
33
|
+
tweets_count.times.map do
|
34
|
+
FactoryGirl.create(:tweet)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
tweets.each_with_index do |tweet, index|
|
40
|
+
index.times do
|
41
|
+
FactoryGirl.create(:reply, tweet: tweet)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does not execute N+1 queries by preload" do
|
47
|
+
expect_query_counts(1 + tweets_count) { Tweet.all.map(&:replies_count) }
|
48
|
+
expect_query_counts(2) { Tweet.all.includes(:replies_count).map(&:replies_count) }
|
49
|
+
end
|
50
|
+
|
51
|
+
it "counts properly" do
|
52
|
+
expected = Tweet.all.map { |t| t.replies.count }
|
53
|
+
expect(Tweet.all.map(&:replies_count)).to eq(expected)
|
54
|
+
expect(Tweet.all.includes(:replies_count).map(&:replies_count)).to eq(expected)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "#preload" do
|
4
|
+
describe "builtin associations" do
|
5
|
+
let(:replies_count) { 3 }
|
6
|
+
let!(:tweet) { FactoryGirl.create(:tweet) }
|
7
|
+
before do
|
8
|
+
replies_count.times do
|
9
|
+
FactoryGirl.create(:reply, tweet: tweet)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "given has_many association" do
|
14
|
+
it "works as usual" do
|
15
|
+
tweet = Tweet.preload(:replies).first
|
16
|
+
expect(tweet.replies.count).to eq(replies_count)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "given belongs_to association" do
|
21
|
+
let!(:reply) { FactoryGirl.create(:reply, tweet: tweet) }
|
22
|
+
|
23
|
+
it "works as usual" do
|
24
|
+
preloaded_reply = Reply.preload(:tweet).find(reply.id)
|
25
|
+
expect(preloaded_reply.tweet).to eq(tweet)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "has_count association" do
|
31
|
+
let(:tweets_count) { 3 }
|
32
|
+
let(:tweets) do
|
33
|
+
tweets_count.times.map do
|
34
|
+
FactoryGirl.create(:tweet)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
tweets.each_with_index do |tweet, index|
|
40
|
+
index.times do
|
41
|
+
FactoryGirl.create(:reply, tweet: tweet)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does not execute N+1 queries by preload" do
|
47
|
+
expect_query_counts(1 + tweets_count) { Tweet.all.map(&:replies_count) }
|
48
|
+
expect_query_counts(2) { Tweet.all.preload(:replies_count).map(&:replies_count) }
|
49
|
+
end
|
50
|
+
|
51
|
+
it "counts properly" do
|
52
|
+
expected = Tweet.all.map { |t| t.replies.count }
|
53
|
+
expect(Tweet.all.map(&:replies_count)).to eq(expected)
|
54
|
+
expect(Tweet.all.preload(:replies_count).map(&:replies_count)).to eq(expected)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "pry"
|
3
|
+
require "active_record"
|
4
|
+
require "activerecord-has_count"
|
5
|
+
|
6
|
+
spec_dir = Pathname.new(File.dirname(__FILE__))
|
7
|
+
Dir[File.join(spec_dir, "models/*.rb")].each { |f| require f }
|
8
|
+
|
9
|
+
require "factory_girl"
|
10
|
+
Dir[File.join(spec_dir, "support/*.rb")].each { |f| require f }
|
11
|
+
|
12
|
+
database_yml = File.join(spec_dir, "database.yml")
|
13
|
+
ActiveRecord::Base.configurations["test"] = YAML.load_file(database_yml)["test"]
|
14
|
+
ActiveRecord::Base.establish_connection :test
|
15
|
+
|
16
|
+
ActiveRecord::Schema.define do
|
17
|
+
create_table :tweets, force: true do |t|
|
18
|
+
t.column :user_id, :integer
|
19
|
+
t.column :created_at, :datetime
|
20
|
+
t.column :updated_at, :datetime
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :replies, force: true do |t|
|
24
|
+
t.column :tweet_id, :integer
|
25
|
+
t.column :created_at, :datetime
|
26
|
+
t.column :updated_at, :datetime
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
RSpec.configure do |config|
|
31
|
+
ALL_MODELS = [
|
32
|
+
Tweet,
|
33
|
+
Reply,
|
34
|
+
].freeze
|
35
|
+
|
36
|
+
config.after do
|
37
|
+
ALL_MODELS.each(&:delete_all)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Copied from:
|
2
|
+
# https://github.com/rails/rails/blob/9bb76261d39b59e7e229c80d052ca91a65ff17be/activerecord/test/cases/test_case.rb#L40-L52
|
3
|
+
def expect_query_counts(num = 1, options = {})
|
4
|
+
ignore_none = options.fetch(:ignore_none) { num == :any }
|
5
|
+
SQLCounter.clear_log
|
6
|
+
x = yield
|
7
|
+
|
8
|
+
the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
|
9
|
+
expect(the_log.size).to eq(num)
|
10
|
+
|
11
|
+
x
|
12
|
+
end
|
13
|
+
|
14
|
+
class SQLCounter
|
15
|
+
class << self
|
16
|
+
attr_accessor :ignored_sql, :log, :log_all
|
17
|
+
def clear_log; self.log = []; self.log_all = []; end
|
18
|
+
end
|
19
|
+
|
20
|
+
self.clear_log
|
21
|
+
|
22
|
+
self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
|
23
|
+
|
24
|
+
# FIXME: this needs to be refactored so specific database can add their own
|
25
|
+
# ignored SQL, or better yet, use a different notification for the queries
|
26
|
+
# instead examining the SQL content.
|
27
|
+
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
|
28
|
+
mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i]
|
29
|
+
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
|
30
|
+
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
|
31
|
+
|
32
|
+
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
|
33
|
+
ignored_sql.concat db_ignored_sql
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :ignore
|
37
|
+
|
38
|
+
def initialize(ignore = Regexp.union(self.class.ignored_sql))
|
39
|
+
@ignore = ignore
|
40
|
+
end
|
41
|
+
|
42
|
+
def call(name, start, finish, message_id, values)
|
43
|
+
sql = values[:sql]
|
44
|
+
|
45
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
46
|
+
# the query was cached
|
47
|
+
return if 'CACHE' == values[:name]
|
48
|
+
|
49
|
+
self.class.log_all << sql
|
50
|
+
self.class.log << sql unless ignore =~ sql
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-has_count
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takashi Kokubun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.2.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,18 +116,33 @@ extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
117
117
|
files:
|
118
118
|
- .gitignore
|
119
|
+
- .rspec
|
120
|
+
- .travis.yml
|
119
121
|
- Gemfile
|
120
122
|
- LICENSE.txt
|
121
123
|
- README.md
|
122
124
|
- Rakefile
|
123
125
|
- activerecord-has_count.gemspec
|
126
|
+
- benchmarks/Gemfile
|
127
|
+
- benchmarks/README.md
|
128
|
+
- benchmarks/benchmark.rb
|
124
129
|
- lib/active_record/associations/builder/has_count.rb
|
125
130
|
- lib/active_record/associations/has_count.rb
|
126
131
|
- lib/active_record/associations/join_dependency/has_count.rb
|
127
132
|
- lib/active_record/associations/preloader/has_count.rb
|
128
|
-
- lib/active_record/has_count/version.rb
|
129
133
|
- lib/active_record/reflection/has_count.rb
|
130
134
|
- lib/activerecord-has_count.rb
|
135
|
+
- lib/activerecord-has_count/model.rb
|
136
|
+
- lib/activerecord-has_count/version.rb
|
137
|
+
- spec/database.yml
|
138
|
+
- spec/eager_load_spec.rb
|
139
|
+
- spec/includes_spec.rb
|
140
|
+
- spec/models/reply.rb
|
141
|
+
- spec/models/tweet.rb
|
142
|
+
- spec/preload_spec.rb
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/support/assert_queries.rb
|
145
|
+
- spec/support/factories.rb
|
131
146
|
homepage: https://github.com/k0kubun/activerecord-has_count
|
132
147
|
licenses:
|
133
148
|
- MIT
|
@@ -148,9 +163,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
163
|
version: '0'
|
149
164
|
requirements: []
|
150
165
|
rubyforge_project:
|
151
|
-
rubygems_version: 2.0.
|
166
|
+
rubygems_version: 2.0.14
|
152
167
|
signing_key:
|
153
168
|
specification_version: 4
|
154
169
|
summary: N+1 count query killer for ActiveRecord
|
155
|
-
test_files:
|
170
|
+
test_files:
|
171
|
+
- spec/database.yml
|
172
|
+
- spec/eager_load_spec.rb
|
173
|
+
- spec/includes_spec.rb
|
174
|
+
- spec/models/reply.rb
|
175
|
+
- spec/models/tweet.rb
|
176
|
+
- spec/preload_spec.rb
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
- spec/support/assert_queries.rb
|
179
|
+
- spec/support/factories.rb
|
156
180
|
has_rdoc:
|