activerecord-precounter 0.2.3 → 0.4.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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +42 -0
- data/activerecord-precounter.gemspec +2 -2
- data/ci/Gemfile.activerecord-5.2.x +4 -0
- data/lib/active_record/precountable.rb +20 -0
- data/lib/active_record/precounter.rb +26 -13
- data/lib/active_record/precounter/version.rb +1 -1
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a80cb8c2919851956774b0c573d3a3271a9b4f017e33edca6fae411f1c4c189b
|
4
|
+
data.tar.gz: 9d479d623ceacf1a93ad4cc64b6ad58ce32f586e07dc8b6335d9e380f82e7d9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae135551e4619a121e488e62517c072b3692dd8b717867b45114ffe904ee50ad6cec6f3924755f511a48f640510d902986be83e0205b5b7178a054eae6d9b47d
|
7
|
+
data.tar.gz: 295507cafb1c6de872f92163d74ebfd37bae295cb2fe4478b3c313b967c0faf657090596fb37153a5a32ac5201be6c48b2384d697ca8901c98f4b0f1fe79da42
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -5,11 +5,18 @@ cache: bundler
|
|
5
5
|
branches:
|
6
6
|
only:
|
7
7
|
- master
|
8
|
+
dist: trusty
|
8
9
|
matrix:
|
9
10
|
include:
|
11
|
+
- rvm: 2.4.1
|
12
|
+
env: TASK=spec ARCONN=postgresql
|
13
|
+
gemfile: ci/Gemfile.activerecord-5.0.x
|
10
14
|
- rvm: 2.4.1
|
11
15
|
env: TASK=spec ARCONN=sqlite3
|
12
16
|
gemfile: ci/Gemfile.activerecord-5.1.x
|
13
17
|
- rvm: 2.4.1
|
14
18
|
env: TASK=spec ARCONN=postgresql
|
15
19
|
gemfile: ci/Gemfile.activerecord-5.1.x
|
20
|
+
- rvm: 2.4.1
|
21
|
+
env: TASK=spec ARCONN=postgresql
|
22
|
+
gemfile: ci/Gemfile.activerecord-5.2.x
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
## v0.4.0
|
2
|
+
|
3
|
+
* [breaking] Raise `NotPrecountedError` instead of returning nil if `*_count` is called without precount
|
4
|
+
* Add `ActiveRecord::Precountable` module to actively define `*_count` readers and writers by `precounts` DSL on ActiveRecord::Base.
|
5
|
+
|
6
|
+
## v0.3.3
|
7
|
+
|
8
|
+
* Support passing a single record to `ActiveRecord::Precounter.new` [#8](https://github.com/k0kubun/activerecord-precounter/pull/8)
|
9
|
+
|
10
|
+
## v0.3.2
|
11
|
+
|
12
|
+
* Support primary\_key option of belongs\_to association [#7](https://github.com/k0kubun/activerecord-precounter/pull/7)
|
13
|
+
|
14
|
+
## v0.3.1
|
15
|
+
|
16
|
+
* Fix [#5](https://github.com/k0kubun/activerecord-precounter/pull/5) for ActiveRecord 5.0 (take 3)
|
17
|
+
|
18
|
+
## v0.3.0
|
19
|
+
|
20
|
+
* Fix [#5](https://github.com/k0kubun/activerecord-precounter/pull/5) for ActiveRecord 5.0 (take 2)
|
21
|
+
* [breaking] Drop ActiveRecord 4.2 support
|
22
|
+
|
23
|
+
## v0.2.3
|
24
|
+
|
25
|
+
* Fix count of scoped association [#5](https://github.com/k0kubun/activerecord-precounter/pull/5)
|
26
|
+
|
27
|
+
## v0.2.2
|
28
|
+
|
29
|
+
* Fix unexpected subquery [#4](https://github.com/k0kubun/activerecord-precounter/pull/5)
|
30
|
+
|
31
|
+
## v0.2.1
|
32
|
+
|
33
|
+
* Add support for counting scoped association
|
34
|
+
* Return empty array when target records don't exist
|
35
|
+
|
36
|
+
## v0.2.0
|
37
|
+
|
38
|
+
* [breaking] Completely change interface
|
39
|
+
|
40
|
+
## v0.1.0
|
41
|
+
|
42
|
+
* Initial release
|
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ['lib']
|
23
23
|
|
24
|
-
spec.add_dependency 'activerecord'
|
24
|
+
spec.add_dependency 'activerecord', '>= 5'
|
25
25
|
spec.add_development_dependency 'bundler'
|
26
26
|
spec.add_development_dependency 'mysql2'
|
27
|
-
spec.add_development_dependency '
|
27
|
+
spec.add_development_dependency 'pg'
|
28
28
|
spec.add_development_dependency 'rake'
|
29
29
|
spec.add_development_dependency 'rspec'
|
30
30
|
spec.add_development_dependency 'sqlite3'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Precountable
|
3
|
+
class NotPrecountedError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
def precounts(*association_names)
|
7
|
+
association_names.each do |association_name|
|
8
|
+
var_name = "#{association_name}_count"
|
9
|
+
instance_var_name = "@#{var_name}"
|
10
|
+
|
11
|
+
attr_writer(var_name)
|
12
|
+
define_method(var_name) do
|
13
|
+
count = instance_variable_get(instance_var_name)
|
14
|
+
raise NotPrecountedError.new("`#{association_name}' not precounted") unless count
|
15
|
+
count
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_record/precounter/version'
|
2
|
+
require 'active_record/precountable'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
class Precounter
|
@@ -12,33 +13,44 @@ module ActiveRecord
|
|
12
13
|
# @param [Array<String,Symbol>] association_names - Eager loaded association names. e.g. `[:users, :likes]`
|
13
14
|
# @return [Array<ActiveRecord::Base>]
|
14
15
|
def precount(*association_names)
|
15
|
-
|
16
|
-
return
|
16
|
+
# Allow single record instances as well as relations to be passed.
|
17
|
+
# The splat here will return an array of the single record if it's
|
18
|
+
# not a relation or otherwise return the records themselves via #to_a.
|
19
|
+
records = *@relation
|
20
|
+
return records if records.empty?
|
21
|
+
|
22
|
+
# We need to get the relation's active class, which is the class itself
|
23
|
+
# in the case of a single record.
|
24
|
+
klass = @relation.respond_to?(:klass) ? @relation.klass : @relation.class
|
17
25
|
|
18
26
|
association_names.each do |association_name|
|
19
27
|
association_name = association_name.to_s
|
20
|
-
reflection =
|
28
|
+
reflection = klass.reflections.fetch(association_name)
|
21
29
|
|
22
30
|
if reflection.inverse_of.nil?
|
23
31
|
raise MissingInverseOf.new(
|
24
|
-
"`#{reflection.klass}` does not have inverse of `#{
|
25
|
-
"Probably missing to call `#{reflection.klass}.belongs_to #{
|
32
|
+
"`#{reflection.klass}` does not have inverse of `#{klass}##{reflection.name}`. "\
|
33
|
+
"Probably missing to call `#{reflection.klass}.belongs_to #{klass.name.underscore.to_sym.inspect}`?"
|
26
34
|
)
|
27
35
|
end
|
28
36
|
|
37
|
+
primary_key = reflection.inverse_of.association_primary_key.to_sym
|
38
|
+
|
29
39
|
count_by_id = if reflection.has_scope?
|
30
|
-
|
40
|
+
# ActiveRecord 5.0 unscopes #scope_for argument, so adding #where outside that:
|
41
|
+
# https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/reflection.rb#L314-L316
|
42
|
+
reflection.scope_for(reflection.klass.unscoped).where(reflection.inverse_of.name => records.map(&primary_key)).group(
|
31
43
|
reflection.inverse_of.foreign_key
|
32
44
|
).count
|
33
45
|
else
|
34
|
-
reflection.klass.where(reflection.inverse_of.name => records.map(
|
46
|
+
reflection.klass.where(reflection.inverse_of.name => records.map(&primary_key)).group(
|
35
47
|
reflection.inverse_of.foreign_key
|
36
48
|
).count
|
37
49
|
end
|
38
50
|
|
39
|
-
writer = define_count_accessor(
|
51
|
+
writer = define_count_accessor(klass, association_name)
|
40
52
|
records.each do |record|
|
41
|
-
record.public_send(writer, count_by_id.fetch(record.
|
53
|
+
record.public_send(writer, count_by_id.fetch(record.public_send(primary_key), 0))
|
42
54
|
end
|
43
55
|
end
|
44
56
|
records
|
@@ -46,15 +58,16 @@ module ActiveRecord
|
|
46
58
|
|
47
59
|
private
|
48
60
|
|
49
|
-
# @param [
|
61
|
+
# @param [Class] record class
|
50
62
|
# @param [String] association_name
|
51
63
|
# @return [String] writer method name
|
52
|
-
def define_count_accessor(
|
64
|
+
def define_count_accessor(klass, association_name)
|
53
65
|
reader_name = "#{association_name}_count"
|
54
66
|
writer_name = "#{reader_name}="
|
55
67
|
|
56
|
-
if !
|
57
|
-
|
68
|
+
if !klass.method_defined?(reader_name) && !klass.method_defined?(writer_name)
|
69
|
+
klass.extend(ActiveRecord::Precountable)
|
70
|
+
klass.public_send(:precounts, association_name)
|
58
71
|
end
|
59
72
|
|
60
73
|
writer_name
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-precounter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takashi Kokubun
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-26 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: '5'
|
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: '5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: pg
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- ".gitignore"
|
119
119
|
- ".rspec"
|
120
120
|
- ".travis.yml"
|
121
|
+
- CHANGELOG.md
|
121
122
|
- Gemfile
|
122
123
|
- LICENSE.txt
|
123
124
|
- README.md
|
@@ -128,7 +129,9 @@ files:
|
|
128
129
|
- ci/Gemfile.activerecord-4.2.x
|
129
130
|
- ci/Gemfile.activerecord-5.0.x
|
130
131
|
- ci/Gemfile.activerecord-5.1.x
|
132
|
+
- ci/Gemfile.activerecord-5.2.x
|
131
133
|
- ci/travis.rb
|
134
|
+
- lib/active_record/precountable.rb
|
132
135
|
- lib/active_record/precounter.rb
|
133
136
|
- lib/active_record/precounter/version.rb
|
134
137
|
- lib/activerecord-precounter.rb
|
@@ -151,8 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
154
|
- !ruby/object:Gem::Version
|
152
155
|
version: '0'
|
153
156
|
requirements: []
|
154
|
-
|
155
|
-
rubygems_version: 2.6.13
|
157
|
+
rubygems_version: 3.2.0.pre1
|
156
158
|
signing_key:
|
157
159
|
specification_version: 4
|
158
160
|
summary: Yet Another N+1 COUNT Query Killer for ActiveRecord
|