eager_group 0.1.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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +4 -0
- data/README.md +96 -0
- data/Rakefile +1 -0
- data/benchmark.rb +87 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/eager_group.gemspec +30 -0
- data/lib/eager_group.rb +29 -0
- data/lib/eager_group/active_record_base.rb +6 -0
- data/lib/eager_group/active_record_relation.rb +31 -0
- data/lib/eager_group/definition.rb +12 -0
- data/lib/eager_group/preloader.rb +30 -0
- data/lib/eager_group/version.rb +3 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e3e98c847d6d1283e9687c8b1b6a770dddd9c645
|
4
|
+
data.tar.gz: 7525252e8291a4b9ee3c270b6e9f318d5bb1b5da
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9ee51ef879b7359070dc7f45e9138dabc776e29a209ba2499bb7f799c373ea0c4f6d9c92ea0a04a313dc8b46dcd366bb478bcb44c817bf44a9cfe214231c4389
|
7
|
+
data.tar.gz: 3f201ec3c596ec40990e6b9d5a669b76388df8566a8b31cc15f4ae5a02039cab5276f1f89221e9cd0827806842c68d1f4dc46a4be8eddab449190d44a6a4a102
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# EagerGroup
|
2
|
+
|
3
|
+
[](http://travis-ci.org/xinminlabs/eager_group)
|
4
|
+
|
5
|
+
Fix n+1 aggregate sql functions for rails, like
|
6
|
+
|
7
|
+
SELECT "posts".* FROM "posts";
|
8
|
+
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."status" = 'approved'
|
9
|
+
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 2 AND "comments"."status" = 'approved'
|
10
|
+
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 3 AND "comments"."status" = 'approved'
|
11
|
+
|
12
|
+
=>
|
13
|
+
|
14
|
+
SELECT "posts".* FROM "posts";
|
15
|
+
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;
|
16
|
+
|
17
|
+
or
|
18
|
+
|
19
|
+
SELECT "posts".* FROM "posts";
|
20
|
+
SELECT AVG("comments"."rating") AS avg_id FROM "comments" WHERE "comments"."post_id" = 1;
|
21
|
+
SELECT AVG("comments"."rating") AS avg_id FROM "comments" WHERE "comments"."post_id" = 2;
|
22
|
+
SELECT AVG("comments"."rating") AS avg_id FROM "comments" WHERE "comments"."post_id" = 3;
|
23
|
+
|
24
|
+
=>
|
25
|
+
|
26
|
+
SELECT "posts".* FROM "posts";
|
27
|
+
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;
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Add this line to your application's Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'eager_group'
|
35
|
+
```
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
|
39
|
+
$ bundle
|
40
|
+
|
41
|
+
Or install it yourself as:
|
42
|
+
|
43
|
+
$ gem install eager_group
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
First you need to define what aggregate function you want to eager
|
48
|
+
load.
|
49
|
+
|
50
|
+
class Post
|
51
|
+
has_many :comments
|
52
|
+
|
53
|
+
define_eager_group :comments_average_rating, :comments, :average, :rating
|
54
|
+
define_eager_group :approved_comments_count, :comments, :count, :*, -> { approved }
|
55
|
+
end
|
56
|
+
|
57
|
+
class Comment
|
58
|
+
belongs_to :post
|
59
|
+
|
60
|
+
scope :approved, -> { where(status: 'approved') }
|
61
|
+
end
|
62
|
+
|
63
|
+
The parameters for `define_eager_group` are as follows
|
64
|
+
|
65
|
+
* `definition_name`, it's used to be a reference in `eager_group` query
|
66
|
+
method, it also generates a method with the same name to fetch the
|
67
|
+
result.
|
68
|
+
* `association`, association name you want to aggregate.
|
69
|
+
* `aggregate_function`, aggregate sql function, can be one of `average`,
|
70
|
+
`count`, `maximum`, `minimum`, `sum`.
|
71
|
+
* `column_name`, aggregate column name, it can be `:*` for `count`
|
72
|
+
* `scope`, scope is optional, it's used to filter data for aggregation.
|
73
|
+
|
74
|
+
Then you can use `eager_group` to fix n+1 aggregate sql functions
|
75
|
+
when querying
|
76
|
+
|
77
|
+
posts = Post.all.eager_group(:comments_average_rating, :approved_comments_count)
|
78
|
+
posts.each do |post|
|
79
|
+
post.comments_average_rating
|
80
|
+
post.approved_comments_count
|
81
|
+
end
|
82
|
+
|
83
|
+
EagerGroup will execute `GROUP BY` sqls for you then set the value of
|
84
|
+
attributes.
|
85
|
+
|
86
|
+
## Benchmark
|
87
|
+
|
88
|
+
I wrote a benchmark script [here][1], it queries approved comments count
|
89
|
+
and comments average rating for 20 posts, with eager group, it gets 10
|
90
|
+
times faster, WOW!
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/xinminlabs/eager_group.
|
95
|
+
|
96
|
+
[1]: https://github.com/xinminlabs/eager_group/blob/master/benchmark.rb
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/benchmark.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Calculating -------------------------------------
|
2
|
+
# Without EagerGroup 2.000 i/100ms
|
3
|
+
# With EagerGroup 28.000 i/100ms
|
4
|
+
# -------------------------------------------------
|
5
|
+
# Without EagerGroup 28.883 (± 6.9%) i/s - 144.000
|
6
|
+
# With EagerGroup 281.755 (± 5.0%) i/s - 1.428k
|
7
|
+
#
|
8
|
+
# Comparison:
|
9
|
+
# With EagerGroup: 281.8 i/s
|
10
|
+
# Without EagerGroup: 28.9 i/s - 9.76x slower
|
11
|
+
$: << 'lib'
|
12
|
+
require 'benchmark/ips'
|
13
|
+
require 'active_record'
|
14
|
+
require 'activerecord-import'
|
15
|
+
require 'eager_group'
|
16
|
+
|
17
|
+
class Post < ActiveRecord::Base
|
18
|
+
has_many :comments
|
19
|
+
|
20
|
+
define_eager_group :comments_average_rating, :comments, :average, :rating
|
21
|
+
define_eager_group :approved_comments_count, :comments, :count, :*, -> { approved }
|
22
|
+
end
|
23
|
+
|
24
|
+
class Comment < ActiveRecord::Base
|
25
|
+
belongs_to :post
|
26
|
+
|
27
|
+
scope :approved, -> { where(status: 'approved') }
|
28
|
+
end
|
29
|
+
|
30
|
+
# create database eager_group_benchmark;
|
31
|
+
ActiveRecord::Base.establish_connection(:adapter => 'mysql2', :database => 'eager_group_benchmark', :server => '/tmp/mysql.socket', :username => 'root')
|
32
|
+
|
33
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
34
|
+
ActiveRecord::Base.connection.drop_table(table)
|
35
|
+
end
|
36
|
+
|
37
|
+
ActiveRecord::Schema.define do
|
38
|
+
self.verbose = false
|
39
|
+
|
40
|
+
create_table :posts, :force => true do |t|
|
41
|
+
t.string :title
|
42
|
+
t.string :body
|
43
|
+
t.timestamps null: false
|
44
|
+
end
|
45
|
+
|
46
|
+
create_table :comments, :force => true do |t|
|
47
|
+
t.string :body
|
48
|
+
t.string :status
|
49
|
+
t.integer :rating
|
50
|
+
t.integer :post_id
|
51
|
+
t.timestamps null: false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
posts_size = 100
|
56
|
+
comments_size = 1000
|
57
|
+
|
58
|
+
posts = []
|
59
|
+
posts_size.times do |i|
|
60
|
+
posts << Post.new(:title => "Title #{i}", :body => "Body #{i}")
|
61
|
+
end
|
62
|
+
Post.import posts
|
63
|
+
post_ids = Post.all.pluck(:id)
|
64
|
+
|
65
|
+
comments = []
|
66
|
+
comments_size.times do |i|
|
67
|
+
comments << Comment.new(:body => "Comment #{i}", :post_id => post_ids[i%100], :status => ["approved", "deleted"][i%2], rating: i%5+1)
|
68
|
+
end
|
69
|
+
Comment.import comments
|
70
|
+
|
71
|
+
Benchmark.ips do |x|
|
72
|
+
x.report("Without EagerGroup") do
|
73
|
+
Post.limit(20).each do |post|
|
74
|
+
post.comments.approved.count
|
75
|
+
post.comments.approved.average('rating')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
x.report("With EagerGroup") do
|
80
|
+
Post.eager_group(:approved_comments_count, :comments_average_rating).limit(20).each do |post|
|
81
|
+
post.approved_comments_count
|
82
|
+
post.comments_average_rating
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
x.compare!
|
87
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "eager_group"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/eager_group.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eager_group/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "eager_group"
|
8
|
+
spec.version = EagerGroup::VERSION
|
9
|
+
spec.authors = ["Richard Huang"]
|
10
|
+
spec.email = ["flyerhzm@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Fix n+1 aggregate sql functions}
|
13
|
+
spec.description = %q{Fix n+1 aggregate sql functions for rails}
|
14
|
+
spec.homepage = "https://github.com/xinminlabs/eager_group"
|
15
|
+
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_dependency "activerecord"
|
24
|
+
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.3"
|
27
|
+
spec.add_development_dependency "sqlite3", "~> 1.3"
|
28
|
+
spec.add_development_dependency "activerecord-import"
|
29
|
+
spec.add_development_dependency "benchmark-ips"
|
30
|
+
end
|
data/lib/eager_group.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "eager_group/version"
|
2
|
+
require 'active_record'
|
3
|
+
require 'eager_group/active_record_base'
|
4
|
+
require 'eager_group/active_record_relation'
|
5
|
+
|
6
|
+
module EagerGroup
|
7
|
+
autoload :Preloader, 'eager_group/preloader'
|
8
|
+
autoload :Definition, 'eager_group/definition'
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
attr_reader :eager_group_definations
|
16
|
+
|
17
|
+
# class Post
|
18
|
+
# define_eager_group :comments_avergage_rating, :comments, :average, :rating
|
19
|
+
# define_eager_group :approved_comments_count, :comments, :count, :*, -> { approved }
|
20
|
+
# end
|
21
|
+
def define_eager_group(attr, association, aggregate_function, column_name, scope = nil)
|
22
|
+
self.send :attr_accessor, attr
|
23
|
+
@eager_group_definations ||= {}
|
24
|
+
@eager_group_definations[attr] = Definition.new association, aggregate_function, column_name, scope
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
ActiveRecord::Base.send :include, EagerGroup
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ActiveRecord::Relation
|
2
|
+
# Post.all.eager_group(:approved_comments_count, :comments_average_rating)
|
3
|
+
|
4
|
+
def exec_queries_with_eager_group
|
5
|
+
records = exec_queries_without_eager_group
|
6
|
+
if eager_group_values.present?
|
7
|
+
EagerGroup::Preloader.new(self.klass, records, eager_group_values).run
|
8
|
+
end
|
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
|
+
@values[:eager_group] = values
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module EagerGroup
|
2
|
+
class Definition
|
3
|
+
attr_reader :association, :aggregate_function, :column_name, :scope
|
4
|
+
|
5
|
+
def initialize(association, aggregate_function, column_name, scope)
|
6
|
+
@association = association
|
7
|
+
@aggregate_function = aggregate_function
|
8
|
+
@column_name = column_name
|
9
|
+
@scope = scope
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module EagerGroup
|
2
|
+
class Preloader
|
3
|
+
def initialize(klass, records, eager_group_values)
|
4
|
+
@klass = klass
|
5
|
+
@records = Array.wrap(records).compact.uniq
|
6
|
+
@eager_group_values = eager_group_values
|
7
|
+
end
|
8
|
+
|
9
|
+
# Preload aggregate functions
|
10
|
+
def run
|
11
|
+
primary_key = @klass.primary_key
|
12
|
+
record_ids = @records.map { |record| record.send primary_key }
|
13
|
+
@eager_group_values.each do |eager_group_value|
|
14
|
+
definition = @klass.eager_group_definations[eager_group_value]
|
15
|
+
if definition
|
16
|
+
reflect = @klass.reflect_on_association(definition.association)
|
17
|
+
association_class = reflect.class_name.constantize
|
18
|
+
association_class = association_class.instance_exec(&definition.scope) if definition.scope
|
19
|
+
aggregate_hash = association_class.where(reflect.foreign_key => record_ids)
|
20
|
+
.group(reflect.foreign_key)
|
21
|
+
.send(definition.aggregate_function, definition.column_name)
|
22
|
+
@records.each do |record|
|
23
|
+
id = record.send primary_key
|
24
|
+
record.send "#{eager_group_value}=", aggregate_hash[id]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eager_group
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Huang
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activerecord-import
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: benchmark-ips
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Fix n+1 aggregate sql functions for rails
|
112
|
+
email:
|
113
|
+
- flyerhzm@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
121
|
+
- CHANGELOG.md
|
122
|
+
- Gemfile
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- benchmark.rb
|
126
|
+
- bin/console
|
127
|
+
- bin/setup
|
128
|
+
- eager_group.gemspec
|
129
|
+
- lib/eager_group.rb
|
130
|
+
- lib/eager_group/active_record_base.rb
|
131
|
+
- lib/eager_group/active_record_relation.rb
|
132
|
+
- lib/eager_group/definition.rb
|
133
|
+
- lib/eager_group/preloader.rb
|
134
|
+
- lib/eager_group/version.rb
|
135
|
+
homepage: https://github.com/xinminlabs/eager_group
|
136
|
+
licenses:
|
137
|
+
- MIT
|
138
|
+
metadata: {}
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.4.7
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Fix n+1 aggregate sql functions
|
159
|
+
test_files: []
|