eager_group 0.6.1 → 0.8.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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +86 -33
- data/benchmark.rb +10 -9
- data/eager_group.gemspec +2 -1
- data/lib/eager_group.rb +11 -10
- data/lib/eager_group/definition.rb +16 -1
- data/lib/eager_group/preloader.rb +23 -15
- data/lib/eager_group/version.rb +1 -1
- metadata +21 -9
- data/lib/eager_group/active_record_base.rb +0 -8
- data/lib/eager_group/active_record_relation.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88912c5696afe9332d53ac15b2365d2e2d7112391672a2cba5db62012876a138
|
4
|
+
data.tar.gz: a18a2d21cd4299a68774f3dbb62556c71f3096b3ebac28bebf7a767a875fc00f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af269ea9b3be9f9a613f56cf14e52a7b183b6d041583f02aa88945f65150f98147ed7271523b9f8ec0d36c622f42ae257f7137e9cf842b381f11cbd7cd6e50bf
|
7
|
+
data.tar.gz: 2878aaefb0b9420b13531927cd46ad47894954c2999fe81d9bf853d7f2255ae97b42df8e56c43a840b5812d71ed1f535c391ccf605fbed99f08ed0769959eab8
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Next Release
|
2
2
|
|
3
|
+
## 0.8.1 (10/25/2019)
|
4
|
+
|
5
|
+
* Fix for `has_many :through`
|
6
|
+
|
7
|
+
## 0.8.0 (10/21/2019)
|
8
|
+
|
9
|
+
* Support `has_and_belongs_to_many`
|
10
|
+
|
11
|
+
## 0.7.2 (10/10/2019)
|
12
|
+
|
13
|
+
* Simplify `association_klass` for `first_object` and `last_object`
|
14
|
+
|
15
|
+
## 0.7.1 (08/23/2019)
|
16
|
+
|
17
|
+
* Set `eager_group_definitions` by `mattr_accessor`
|
18
|
+
|
19
|
+
## 0.7.0 (08/22/2019)
|
20
|
+
|
21
|
+
* Add `first_object` and `last_object` aggregation
|
22
|
+
|
3
23
|
## 0.6.1 (03/05/2018)
|
4
24
|
|
5
25
|
* Skip preload when association is empty
|
data/README.md
CHANGED
@@ -8,29 +8,37 @@ flyerhzm/eager_group](https://awesomecode.io/projects/e5386790-9420-4003-831a-c9
|
|
8
8
|
|
9
9
|
Fix n+1 aggregate sql functions for rails, like
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
```
|
12
|
+
SELECT "posts".* FROM "posts";
|
13
|
+
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."status" = 'approved'
|
14
|
+
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 2 AND "comments"."status" = 'approved'
|
15
|
+
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 3 AND "comments"."status" = 'approved'
|
16
|
+
```
|
15
17
|
|
16
18
|
=>
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
```
|
21
|
+
SELECT "posts".* FROM "posts";
|
22
|
+
SELECT COUNT(*) AS count_all, post_id AS post_id FROM "comments" WHERE "comments"."post_id" IN (1, 2, 3) AND "comments"."status" = 'approved' GROUP BY post_id;
|
23
|
+
```
|
20
24
|
|
21
25
|
or
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
```
|
28
|
+
SELECT "posts".* FROM "posts";
|
29
|
+
SELECT AVG("comments"."rating") AS avg_id FROM "comments" WHERE "comments"."post_id" = 1;
|
30
|
+
SELECT AVG("comments"."rating") AS avg_id FROM "comments" WHERE "comments"."post_id" = 2;
|
31
|
+
SELECT AVG("comments"."rating") AS avg_id FROM "comments" WHERE "comments"."post_id" = 3;
|
32
|
+
```
|
27
33
|
|
28
34
|
=>
|
29
35
|
|
30
|
-
|
31
|
-
|
36
|
+
```
|
37
|
+
SELECT "posts".* FROM "posts";
|
38
|
+
SELECT AVG("comments"."rating") AS average_comments_rating, post_id AS post_id FROM "comments" WHERE "comments"."post_id" IN (1, 2, 3) GROUP BY post_id;
|
39
|
+
```
|
32
40
|
|
33
|
-
It supports Rails 4.x and Rails
|
41
|
+
It supports Rails 4.x, Rails 5.x and Rails 6.x
|
34
42
|
|
35
43
|
## Installation
|
36
44
|
|
@@ -42,29 +50,35 @@ gem 'eager_group'
|
|
42
50
|
|
43
51
|
And then execute:
|
44
52
|
|
45
|
-
|
53
|
+
```
|
54
|
+
$ bundle
|
55
|
+
```
|
46
56
|
|
47
57
|
Or install it yourself as:
|
48
58
|
|
49
|
-
|
59
|
+
```
|
60
|
+
$ gem install eager_group
|
61
|
+
```
|
50
62
|
|
51
63
|
## Usage
|
52
64
|
|
53
65
|
First you need to define what aggregate function you want to eager
|
54
66
|
load.
|
55
67
|
|
56
|
-
|
57
|
-
|
68
|
+
```ruby
|
69
|
+
class Post < ActiveRecord::Base
|
70
|
+
has_many :comments
|
58
71
|
|
59
|
-
|
60
|
-
|
61
|
-
|
72
|
+
define_eager_group :comments_average_rating, :comments, :average, :rating
|
73
|
+
define_eager_group :approved_comments_count, :comments, :count, :*, -> { approved }
|
74
|
+
end
|
62
75
|
|
63
|
-
|
64
|
-
|
76
|
+
class Comment < ActiveRecord::Base
|
77
|
+
belongs_to :post
|
65
78
|
|
66
|
-
|
67
|
-
|
79
|
+
scope :approved, -> { where(status: 'approved') }
|
80
|
+
end
|
81
|
+
```
|
68
82
|
|
69
83
|
The parameters for `define_eager_group` are as follows
|
70
84
|
|
@@ -73,18 +87,22 @@ method, it also generates a method with the same name to fetch the
|
|
73
87
|
result.
|
74
88
|
* `association`, association name you want to aggregate.
|
75
89
|
* `aggregate_function`, aggregate sql function, can be one of `average`,
|
76
|
-
`count`, `maximum`, `minimum`, `sum
|
90
|
+
`count`, `maximum`, `minimum`, `sum`, I define 2 additional aggregate
|
91
|
+
function `first_object` and `last_object` to eager load first and last
|
92
|
+
association objects.
|
77
93
|
* `column_name`, aggregate column name, it can be `:*` for `count`
|
78
94
|
* `scope`, scope is optional, it's used to filter data for aggregation.
|
79
95
|
|
80
96
|
Then you can use `eager_group` to fix n+1 aggregate sql functions
|
81
97
|
when querying
|
82
98
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
99
|
+
```ruby
|
100
|
+
posts = Post.all.eager_group(:comments_average_rating, :approved_comments_count)
|
101
|
+
posts.each do |post|
|
102
|
+
post.comments_average_rating
|
103
|
+
post.approved_comments_count
|
104
|
+
end
|
105
|
+
```
|
88
106
|
|
89
107
|
EagerGroup will execute `GROUP BY` sqls for you then set the value of
|
90
108
|
attributes.
|
@@ -93,16 +111,51 @@ attributes.
|
|
93
111
|
You can call the `definition_name` directly for convenience,
|
94
112
|
but it would not help you to fix n+1 aggregate sql issue.
|
95
113
|
|
96
|
-
|
97
|
-
|
98
|
-
|
114
|
+
```
|
115
|
+
post = Post.first
|
116
|
+
post.commets_average_rating
|
117
|
+
post.approved_comments_count
|
118
|
+
```
|
99
119
|
|
100
120
|
## Advanced
|
101
121
|
|
122
|
+
`eager_group` through association
|
123
|
+
|
102
124
|
```ruby
|
103
125
|
User.limit(10).includes(:posts).eager_group(posts: [:comments_average_rating, :approved_comments_count])
|
104
126
|
```
|
105
127
|
|
128
|
+
pass parameter to scope
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
class Post < ActiveRecord::Base
|
132
|
+
has_many :comments
|
133
|
+
|
134
|
+
define_eager_group :comments_average_rating_by_author, :comments, :average, :rating, ->(author, ignore) { by_author(author, ignore) }
|
135
|
+
end
|
136
|
+
|
137
|
+
posts = Post.all.eager_group([:comments_average_rating_by_author, author, true])
|
138
|
+
posts.each { |post| post.comments_average_rating_by_author }
|
139
|
+
```
|
140
|
+
|
141
|
+
`first_object` and `last_object` aggregation to eager load first and
|
142
|
+
last association objects.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
class Post < ActiveRecord::Base
|
146
|
+
has_many :comments
|
147
|
+
|
148
|
+
define_eager_group :first_comment, :comments, :first_object, :id
|
149
|
+
define_eager_group :last_comment, :comments, :last_object, :id
|
150
|
+
end
|
151
|
+
|
152
|
+
posts = Post.all.eager_group(:first_comment, :last_comment)
|
153
|
+
posts.each do |post|
|
154
|
+
post.first_comment
|
155
|
+
post.last_comment
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
106
159
|
|
107
160
|
## Benchmark
|
108
161
|
|
data/benchmark.rb
CHANGED
@@ -30,11 +30,11 @@ class Comment < ActiveRecord::Base
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# create database eager_group_benchmark;
|
33
|
-
ActiveRecord::Base.establish_connection(
|
33
|
+
ActiveRecord::Base.establish_connection(
|
34
|
+
adapter: 'mysql2', database: 'eager_group_benchmark', server: '/tmp/mysql.socket', username: 'root'
|
35
|
+
)
|
34
36
|
|
35
|
-
ActiveRecord::Base.connection.tables.each
|
36
|
-
ActiveRecord::Base.connection.drop_table(table)
|
37
|
-
end
|
37
|
+
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
38
38
|
|
39
39
|
ActiveRecord::Schema.define do
|
40
40
|
self.verbose = false
|
@@ -55,18 +55,19 @@ ActiveRecord::Schema.define do
|
|
55
55
|
end
|
56
56
|
|
57
57
|
posts_size = 100
|
58
|
-
comments_size =
|
58
|
+
comments_size = 1_000
|
59
59
|
|
60
60
|
posts = []
|
61
|
-
posts_size.times
|
62
|
-
posts << Post.new(title: "Title #{i}", body: "Body #{i}")
|
63
|
-
end
|
61
|
+
posts_size.times { |i| posts << Post.new(title: "Title #{i}", body: "Body #{i}") }
|
64
62
|
Post.import posts
|
65
63
|
post_ids = Post.all.pluck(:id)
|
66
64
|
|
67
65
|
comments = []
|
68
66
|
comments_size.times do |i|
|
69
|
-
comments <<
|
67
|
+
comments <<
|
68
|
+
Comment.new(
|
69
|
+
body: "Comment #{i}", post_id: post_ids[i % 100], status: %w[approved deleted][i % 2], rating: i % 5 + 1
|
70
|
+
)
|
70
71
|
end
|
71
72
|
Comment.import comments
|
72
73
|
|
data/eager_group.gemspec
CHANGED
@@ -23,9 +23,10 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.add_development_dependency 'activerecord'
|
25
25
|
spec.add_development_dependency 'activerecord-import'
|
26
|
+
spec.add_development_dependency 'activesupport'
|
26
27
|
spec.add_development_dependency 'benchmark-ips'
|
27
28
|
spec.add_development_dependency 'bundler'
|
28
29
|
spec.add_development_dependency 'rake', '~> 10.0'
|
29
30
|
spec.add_development_dependency 'rspec', '~> 3.3'
|
30
|
-
spec.add_development_dependency 'sqlite3'
|
31
|
+
spec.add_development_dependency 'sqlite3'
|
31
32
|
end
|
data/lib/eager_group.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
3
4
|
require 'eager_group/version'
|
4
5
|
|
5
6
|
module EagerGroup
|
@@ -11,7 +12,7 @@ module EagerGroup
|
|
11
12
|
end
|
12
13
|
|
13
14
|
module ClassMethods
|
14
|
-
|
15
|
+
mattr_accessor :eager_group_definitions, default: {}
|
15
16
|
|
16
17
|
# class Post
|
17
18
|
# define_eager_group :comments_avergage_rating, :comments, :average, :rating
|
@@ -19,16 +20,16 @@ module EagerGroup
|
|
19
20
|
# end
|
20
21
|
def define_eager_group(attr, association, aggregate_function, column_name, scope = nil)
|
21
22
|
send :attr_accessor, attr
|
22
|
-
|
23
|
-
@eager_group_definitions[attr] = Definition.new association, aggregate_function, column_name, scope
|
23
|
+
eager_group_definitions[attr] = Definition.new(association, aggregate_function, column_name, scope)
|
24
24
|
|
25
|
-
define_method attr,
|
26
|
-
|
27
|
-
|
25
|
+
define_method attr,
|
26
|
+
lambda { |*args|
|
27
|
+
query_result_cache = instance_variable_get("@#{attr}")
|
28
|
+
return query_result_cache if args.blank? && query_result_cache.present?
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
preload_eager_group(attr, *args)
|
31
|
+
instance_variable_get("@#{attr}")
|
32
|
+
}
|
32
33
|
|
33
34
|
define_method "#{attr}=" do |val|
|
34
35
|
instance_variable_set("@#{attr}", val)
|
@@ -51,4 +52,4 @@ ActiveRecord::Base.class_eval do
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
require 'active_record/with_eager_group'
|
54
|
-
ActiveRecord::Relation.
|
55
|
+
ActiveRecord::Relation.prepend ActiveRecord::WithEagerGroup
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module EagerGroup
|
4
4
|
class Definition
|
5
|
-
attr_reader :association, :
|
5
|
+
attr_reader :association, :column_name, :scope
|
6
6
|
|
7
7
|
def initialize(association, aggregate_function, column_name, scope)
|
8
8
|
@association = association
|
@@ -10,5 +10,20 @@ module EagerGroup
|
|
10
10
|
@column_name = column_name
|
11
11
|
@scope = scope
|
12
12
|
end
|
13
|
+
|
14
|
+
def aggregation_function
|
15
|
+
return :maximum if @aggregate_function.to_sym == :last_object
|
16
|
+
return :minimum if @aggregate_function.to_sym == :first_object
|
17
|
+
|
18
|
+
@aggregate_function
|
19
|
+
end
|
20
|
+
|
21
|
+
def need_load_object
|
22
|
+
%i[first_object last_object].include?(@aggregate_function.to_sym)
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_value
|
26
|
+
%i[first_object last_object].include?(@aggregate_function.to_sym) ? nil : 0
|
27
|
+
end
|
13
28
|
end
|
14
29
|
end
|
@@ -12,36 +12,44 @@ module EagerGroup
|
|
12
12
|
def run
|
13
13
|
primary_key = @klass.primary_key
|
14
14
|
@eager_group_values.each do |eager_group_value|
|
15
|
-
definition_key, arguments =
|
15
|
+
definition_key, arguments =
|
16
|
+
eager_group_value.is_a?(Array) ? [eager_group_value.shift, eager_group_value] : [eager_group_value, nil]
|
17
|
+
|
16
18
|
if definition_key.is_a?(Hash)
|
17
19
|
association_name, definition_key = *definition_key.first
|
18
20
|
@records = @records.flat_map { |record| record.send(association_name) }
|
19
21
|
next if @records.empty?
|
22
|
+
|
20
23
|
@klass = @records.first.class
|
21
24
|
end
|
22
25
|
record_ids = @records.map { |record| record.send(primary_key) }
|
23
|
-
|
26
|
+
unless definition = @klass.eager_group_definitions[definition_key]
|
27
|
+
next
|
28
|
+
end
|
24
29
|
|
25
30
|
reflection = @klass.reflect_on_association(definition.association)
|
26
31
|
association_class = reflection.klass
|
27
32
|
association_class = association_class.instance_exec(*arguments, &definition.scope) if definition.scope
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
foreign_key, aggregate_hash =
|
35
|
+
if reflection.is_a?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)
|
36
|
+
["#{reflection.join_table}.#{reflection.foreign_key}", @klass.joins(reflection.name)]
|
37
|
+
elsif reflection.through_reflection
|
38
|
+
["#{reflection.through_reflection.name}.#{reflection.through_reflection.foreign_key}", @klass.joins(reflection.name)]
|
39
|
+
else
|
40
|
+
[reflection.foreign_key, association_class]
|
41
|
+
end
|
42
|
+
aggregate_hash = aggregate_hash.where(foreign_key => record_ids)
|
43
|
+
.where(polymophic_as_condition(reflection))
|
44
|
+
.group(foreign_key)
|
45
|
+
.send(definition.aggregation_function, definition.column_name)
|
46
|
+
if definition.need_load_object
|
47
|
+
aggregate_objects = reflection.klass.find(aggregate_hash.values).each_with_object({}) { |o, h| h[o.id] = o }
|
48
|
+
aggregate_hash.keys.each { |key| aggregate_hash[key] = aggregate_objects[aggregate_hash[key]] }
|
41
49
|
end
|
42
50
|
@records.each do |record|
|
43
51
|
id = record.send(primary_key)
|
44
|
-
record.send("#{definition_key}=", aggregate_hash[id] ||
|
52
|
+
record.send("#{definition_key}=", aggregate_hash[id] || definition.default_value)
|
45
53
|
end
|
46
54
|
end
|
47
55
|
end
|
data/lib/eager_group/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eager_group
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: benchmark-ips
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,16 +112,16 @@ dependencies:
|
|
98
112
|
name: sqlite3
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
|
-
- - "
|
115
|
+
- - ">="
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
117
|
+
version: '0'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
|
-
- - "
|
122
|
+
- - ">="
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
124
|
+
version: '0'
|
111
125
|
description: Fix n+1 aggregate sql functions for rails
|
112
126
|
email:
|
113
127
|
- flyerhzm@gmail.com
|
@@ -128,8 +142,6 @@ files:
|
|
128
142
|
- eager_group.gemspec
|
129
143
|
- lib/active_record/with_eager_group.rb
|
130
144
|
- lib/eager_group.rb
|
131
|
-
- lib/eager_group/active_record_base.rb
|
132
|
-
- lib/eager_group/active_record_relation.rb
|
133
145
|
- lib/eager_group/definition.rb
|
134
146
|
- lib/eager_group/preloader.rb
|
135
147
|
- lib/eager_group/version.rb
|
@@ -152,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
164
|
- !ruby/object:Gem::Version
|
153
165
|
version: '0'
|
154
166
|
requirements: []
|
155
|
-
rubygems_version: 3.0.
|
167
|
+
rubygems_version: 3.0.3
|
156
168
|
signing_key:
|
157
169
|
specification_version: 4
|
158
170
|
summary: Fix n+1 aggregate sql functions
|
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class ActiveRecord::Relation
|
4
|
-
# Post.all.eager_group(:approved_comments_count, :comments_average_rating)
|
5
|
-
|
6
|
-
def exec_queries_with_eager_group
|
7
|
-
records = exec_queries_without_eager_group
|
8
|
-
EagerGroup::Preloader.new(klass, records, eager_group_values).run if eager_group_values.present?
|
9
|
-
records
|
10
|
-
end
|
11
|
-
alias_method_chain :exec_queries, :eager_group
|
12
|
-
|
13
|
-
def eager_group(*args)
|
14
|
-
check_if_method_has_arguments!('eager_group', args)
|
15
|
-
spawn.eager_group!(*args)
|
16
|
-
end
|
17
|
-
|
18
|
-
def eager_group!(*args)
|
19
|
-
self.eager_group_values += args
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
def eager_group_values
|
24
|
-
@values[:eager_group] || []
|
25
|
-
end
|
26
|
-
|
27
|
-
def eager_group_values=(values)
|
28
|
-
raise ImmutableRelation if @loaded
|
29
|
-
|
30
|
-
@values[:eager_group] = values
|
31
|
-
end
|
32
|
-
end
|