aggregate_columns 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.hgignore +1 -0
- data/Gemfile +4 -0
- data/README +93 -18
- data/Rakefile +8 -0
- data/aggregate_columns.gemspec +27 -0
- data/lib/aggregate_columns.rb +3 -3
- data/lib/aggregate_columns/version.rb +3 -0
- data/test/rails_2_test.rb +86 -0
- data/test/rails_3_test.rb +80 -0
- data/test/test_helper.rb +3 -1
- metadata +67 -6
- data/test/aggregate_columns_test.rb +0 -82
data/.hgignore
CHANGED
data/Gemfile
ADDED
data/README
CHANGED
@@ -11,7 +11,6 @@ Take a classic example:
|
|
11
11
|
|
12
12
|
class Post
|
13
13
|
has_many :comments
|
14
|
-
has_many :tags
|
15
14
|
end
|
16
15
|
|
17
16
|
Now let's say you want to display a table with some posts' attributes including
|
@@ -20,25 +19,109 @@ generates separate query for each post. Alternatively you could use counter
|
|
20
19
|
cache, but it can get out of sync plus it only works on this simple example,
|
21
20
|
while you could also need the time of latest comment for each post.
|
22
21
|
|
22
|
+
Rails 3
|
23
|
+
=======
|
23
24
|
|
24
25
|
AggregateColumns solution would look like this:
|
25
26
|
|
26
|
-
|
27
|
+
posts = Post.aggregate_columns( :association => :comments )
|
28
|
+
|
29
|
+
SELECT posts.*, (SELECT count(*) AS comment_count FROM comments
|
30
|
+
WHERE (posts.id = post_id)) AS comment_count FROM "posts"
|
31
|
+
ORDER BY comment_count DESC
|
32
|
+
|
33
|
+
posts.first.comment_count # => "1"
|
34
|
+
|
35
|
+
This returns a full blown ActiveRecord::Relation object encapsulating a query
|
36
|
+
for posts with additional column called "comment_count" containing (big surprise
|
37
|
+
here) number of comments for each post. I'm taking advantage of the fact columns
|
38
|
+
specified in :select option are kept inside model objects (but not
|
39
|
+
type-casted, so comment_count would be a String).
|
27
40
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
41
|
+
Options:
|
42
|
+
* :association - association to aggregate
|
43
|
+
* :function - SQL aggregate function to use, defaults to count
|
44
|
+
* :column - associated table column to aggregate, defaults to '*' (useful for
|
45
|
+
count)
|
46
|
+
* :result_column - name of resulting column, defaults to
|
47
|
+
association_field_function with exception of '*' column, which results in
|
48
|
+
association.singularize_function
|
49
|
+
* :join_type - JOIN type for in-join mode, explained later
|
32
50
|
|
51
|
+
Ordering defaults to aggregate column descending, you may always change it using
|
52
|
+
reorder. The reason for using this default is that in my experience this is the
|
53
|
+
only order I needed :)
|
33
54
|
|
34
55
|
What if you did not want such a simple aggregation, but the one I already
|
35
56
|
mentioned: time of latest comment for each post:
|
36
57
|
|
37
|
-
Post.
|
58
|
+
Post.aggregate_columns( :association => :comments, :function => :max, :column => :created_at )
|
59
|
+
|
60
|
+
SELECT posts.*, (SELECT max(created_at) AS comments_created_at_max FROM
|
61
|
+
comments WHERE (posts.id = post_id)) AS comments_created_at_max FROM "posts"
|
62
|
+
ORDER BY comments_created_at_max DESC
|
38
63
|
|
39
64
|
The name of aggregated column would be comments_created_at_max in this case, but
|
40
65
|
it can be changed using :result_column option.
|
41
66
|
|
67
|
+
You may add some additional relations to the subquery using a block, eg.
|
68
|
+
|
69
|
+
Post.aggregate_columns( :association => :comments ) { |rel| rel.where( :user_id => 1 ) }
|
70
|
+
|
71
|
+
SELECT posts.*, (SELECT count(*) AS comment_count FROM comments WHERE
|
72
|
+
"comments"."user_id" = 1 AND (posts.id = post_id)) AS comment_count FROM
|
73
|
+
"posts" ORDER BY comment_count DESC
|
74
|
+
|
75
|
+
This may you can limit which comments are considered for aggregation.
|
76
|
+
|
77
|
+
The only option not mentioned yet is :join_type, which can only be explained
|
78
|
+
after digging deeper into how the gem works. Aggregate column is added in one of
|
79
|
+
two ways:
|
80
|
+
|
81
|
+
* if :join_type option is not present - it's appended as subquery to SELECT
|
82
|
+
clause, eg:
|
83
|
+
|
84
|
+
SELECT posts.*, (SELECT count(*) AS comment_count FROM comments WHERE
|
85
|
+
(posts.id = post_id)) AS comment_count FROM "posts" ORDER BY comment_count
|
86
|
+
DESC
|
87
|
+
|
88
|
+
* if :join_type option is present - it's added as a subquery connected using
|
89
|
+
JOIN clause with join type specified, eg (:join_type => :right):
|
90
|
+
|
91
|
+
SELECT posts.*, comment_count FROM "posts" RIGHT JOIN (SELECT post_id,
|
92
|
+
count(*) AS comment_count FROM comments GROUP BY post_id) comment_count_join
|
93
|
+
ON posts.id = comment_count_join.post_id ORDER BY comment_count DESC
|
94
|
+
|
95
|
+
Now, each way has it's place.
|
96
|
+
|
97
|
+
In-select subquery:
|
98
|
+
* foreign key index is used (PostgreSQL at least)
|
99
|
+
* subquery executed only for rows fetched from main table
|
100
|
+
|
101
|
+
In-join subquery:
|
102
|
+
* with RIGHT join only fetches records from main table having results in
|
103
|
+
aggregate column (eg. only posts with any comments)
|
104
|
+
|
105
|
+
You can only decide between those two on a case by case basis. But generally: if
|
106
|
+
you only care about records having some meaningful values in aggregate column,
|
107
|
+
in-join might be better; if main table has some large record count, but you only
|
108
|
+
want to select some subset of those, in-select might save you some computation
|
109
|
+
time.
|
110
|
+
|
111
|
+
Rails 2
|
112
|
+
=======
|
113
|
+
|
114
|
+
NOTE: this is deprecated and I'm not sure if it even still works. But the code
|
115
|
+
is there, so it might be useful for someone.
|
116
|
+
|
117
|
+
|
118
|
+
Instead of aggregate_columns you have aggregate_column_options:
|
119
|
+
|
120
|
+
Post.aggregate_columns_options( :association => :comments )
|
121
|
+
|
122
|
+
which returns a Hash of options suitable to pass to .find call. You have
|
123
|
+
additional options: :joins and :conditions to be passed to the subquery, because
|
124
|
+
you don't have a Relation object to work on in a block.
|
42
125
|
|
43
126
|
Now something really complicated - sum of votes for comments whose authors are
|
44
127
|
active. Here's where :conditions and :joins options come in handy:
|
@@ -53,6 +136,9 @@ active. Here's where :conditions and :joins options come in handy:
|
|
53
136
|
)
|
54
137
|
)
|
55
138
|
|
139
|
+
:joins and :conditions are passed to the subquery, so they will determine which
|
140
|
+
comments are considered when calculating vote sum
|
141
|
+
|
56
142
|
Important things to note here:
|
57
143
|
* aggregate_column_options return :select, :joins and :order options, so those
|
58
144
|
cannot be used for other purposes
|
@@ -60,7 +146,6 @@ Important things to note here:
|
|
60
146
|
with normal find options (other than aforementioned ones). This means you can
|
61
147
|
merge resulting options with eg. custom :conditions
|
62
148
|
|
63
|
-
|
64
149
|
You may also define multiple aggregate columns in one call:
|
65
150
|
|
66
151
|
Post.aggregate_columns_options(
|
@@ -68,20 +153,10 @@ You may also define multiple aggregate columns in one call:
|
|
68
153
|
{ :association => :tags, :result_column => :number_of_tags }
|
69
154
|
)
|
70
155
|
|
71
|
-
|
72
156
|
Yet another method to combine aggregate columns with other find options is to use scopes:
|
73
157
|
|
74
158
|
Post.aggregate_columns_scope( :association => :comments ).scoped( ...
|
75
159
|
|
76
|
-
Rails 3
|
77
|
-
=======
|
78
|
-
|
79
|
-
In Rails 3 you should use:
|
80
|
-
|
81
|
-
Post.aggregate_colums( :association => :comments )
|
82
|
-
|
83
|
-
which returns a full blown ActiveRecord::Relation object
|
84
|
-
|
85
160
|
Thanks
|
86
161
|
======
|
87
162
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "aggregate_columns/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "aggregate_columns"
|
7
|
+
s.version = AggregateColumns::VERSION
|
8
|
+
s.authors = ["Marek Janukowicz/Starware"]
|
9
|
+
s.email = ["marek@janukowicz.net"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = "Create and use aggregate columns in Rails applications"
|
12
|
+
s.description = ""
|
13
|
+
|
14
|
+
s.files = `hg manifest`.split("\n")
|
15
|
+
s.test_files = `hg manifest | grep ^test`.split("\n")
|
16
|
+
s.executables = `hg manifest | grep ^bin`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
# s.add_development_dependency "rspec"
|
21
|
+
s.add_runtime_dependency "activerecord", ">= 2"
|
22
|
+
s.add_development_dependency "rake"
|
23
|
+
s.add_development_dependency "activerecord", ">= 2"
|
24
|
+
s.add_development_dependency "activesupport", ">= 2"
|
25
|
+
s.add_development_dependency "activerecord-nulldb-adapter"
|
26
|
+
|
27
|
+
end
|
data/lib/aggregate_columns.rb
CHANGED
@@ -93,10 +93,10 @@ module AggregateColumns
|
|
93
93
|
|
94
94
|
end
|
95
95
|
|
96
|
-
#
|
97
|
-
ActiveRecord::Base.send( :extend, AggregateColumns::MethodsRails2 )
|
98
|
-
if defined?( ActiveRecord::Relation )
|
96
|
+
if defined?( ActiveRecord::Relation ) # Rails 3
|
99
97
|
ActiveRecord::Base.send( :extend, AggregateColumns::MethodsRails3 )
|
98
|
+
else # Rails 2
|
99
|
+
ActiveRecord::Base.send( :extend, AggregateColumns::MethodsRails2 )
|
100
100
|
end
|
101
101
|
|
102
102
|
## Rails 2
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# TODO: this test doesn't really work with Rails 2
|
2
|
+
require 'active_record'
|
3
|
+
require 'rails'
|
4
|
+
|
5
|
+
if Rails::VERSION::MAJOR == 2
|
6
|
+
|
7
|
+
require 'test_helper'
|
8
|
+
|
9
|
+
class Rails2Test < ActiveSupport::TestCase
|
10
|
+
|
11
|
+
class Post < FakeModel
|
12
|
+
end
|
13
|
+
|
14
|
+
class User < FakeModel
|
15
|
+
has_many :posts
|
16
|
+
end
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@user = User.new
|
20
|
+
end
|
21
|
+
|
22
|
+
test "empty options" do
|
23
|
+
options = User.aggregate_columns_options
|
24
|
+
assert_equal "users.*", options[:select]
|
25
|
+
assert options[:order].blank?
|
26
|
+
assert options[:joins].blank?
|
27
|
+
end
|
28
|
+
|
29
|
+
test "empty hash as options should fail" do
|
30
|
+
assert_raise( ArgumentError ) do
|
31
|
+
options = User.aggregate_columns_options( {} )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
test "only association" do
|
36
|
+
options = User.aggregate_columns_options( :association => :posts )
|
37
|
+
assert_equal "users.*, post_count", options[:select]
|
38
|
+
assert_equal "post_count DESC", options[:order]
|
39
|
+
assert_equal "LEFT JOIN (SELECT user_id, count(*) AS post_count FROM posts GROUP BY user_id) post_count_join ON posts.id = post_count_join.user_id", options[:joins]
|
40
|
+
end
|
41
|
+
|
42
|
+
test "column function" do
|
43
|
+
options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max )
|
44
|
+
assert_equal "users.*, posts_created_at_max", options[:select]
|
45
|
+
assert_equal "posts_created_at_max DESC", options[:order]
|
46
|
+
assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
47
|
+
end
|
48
|
+
|
49
|
+
test "result_column" do
|
50
|
+
options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :result_column => "last_post_time" )
|
51
|
+
assert_equal "users.*, last_post_time", options[:select]
|
52
|
+
assert_equal "last_post_time DESC", options[:order]
|
53
|
+
assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS last_post_time FROM posts GROUP BY user_id) last_post_time_join ON posts.id = last_post_time_join.user_id", options[:joins]
|
54
|
+
end
|
55
|
+
|
56
|
+
test "join_type" do
|
57
|
+
options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :join_type => "RIGHT" )
|
58
|
+
assert_equal "users.*, posts_created_at_max", options[:select]
|
59
|
+
assert_equal "posts_created_at_max DESC", options[:order]
|
60
|
+
assert_equal "RIGHT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
61
|
+
end
|
62
|
+
|
63
|
+
test "conditions" do
|
64
|
+
options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :conditions => { :active => true } )
|
65
|
+
assert_equal "users.*, posts_created_at_max", options[:select]
|
66
|
+
assert_equal "posts_created_at_max DESC", options[:order]
|
67
|
+
assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts WHERE posts.active = true GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
68
|
+
end
|
69
|
+
|
70
|
+
test "joins" do
|
71
|
+
time = 2.days.ago
|
72
|
+
options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :joins => "INNER JOIN comments ON comments.post_id = posts.id", :conditions => ["comments.updated_at > ?", time] )
|
73
|
+
assert_equal "users.*, posts_created_at_max", options[:select]
|
74
|
+
assert_equal "posts_created_at_max DESC", options[:order]
|
75
|
+
assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts INNER JOIN comments ON comments.post_id = posts.id WHERE comments.updated_at > #{time} GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
76
|
+
end
|
77
|
+
|
78
|
+
test "order" do
|
79
|
+
options = User.aggregate_columns_options( :association => :posts, :order => :asc )
|
80
|
+
assert_equal "users.*, post_count", options[:select]
|
81
|
+
assert_equal "post_count ASC", options[:order]
|
82
|
+
assert_equal "LEFT JOIN (SELECT user_id, count(*) AS post_count FROM posts GROUP BY user_id) post_count_join ON posts.id = post_count_join.user_id", options[:joins]
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
if Rails::VERSION::MAJOR == 3
|
5
|
+
require 'test/unit'
|
6
|
+
require 'aggregate_columns'
|
7
|
+
|
8
|
+
# Workaround for nulldb Rails 3.1 incompatibility
|
9
|
+
class Object
|
10
|
+
alias_method :returning, :tap
|
11
|
+
end
|
12
|
+
|
13
|
+
class Rails3Test < Test::Unit::TestCase
|
14
|
+
|
15
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
16
|
+
|
17
|
+
class Post < ActiveRecord::Base
|
18
|
+
has_many :comments
|
19
|
+
end
|
20
|
+
|
21
|
+
class Comment < ActiveRecord::Base
|
22
|
+
belongs_to :post
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_only_association
|
26
|
+
relation = Post.aggregate_columns( :association => :comments )
|
27
|
+
assert_equal 2, relation.select_values.count
|
28
|
+
assert relation.select_values.include?( "(SELECT count(*) AS comment_count FROM comments WHERE (posts.id = post_id)) AS comment_count" )
|
29
|
+
assert_equal relation.order_values, ["comment_count DESC"]
|
30
|
+
assert_empty relation.where_values
|
31
|
+
assert_empty relation.joins_values
|
32
|
+
end
|
33
|
+
|
34
|
+
# Reorder doesn't work, probably I don't understand how it is supposed to work
|
35
|
+
#def test_reorder
|
36
|
+
# relation = Post.aggregate_columns( :association => :comments ).reorder( "posts.id ASC" )
|
37
|
+
#end
|
38
|
+
|
39
|
+
def test_function_and_column
|
40
|
+
relation = Post.aggregate_columns( :association => :comments, :function => :max, :column => :created_at )
|
41
|
+
assert_equal 2, relation.select_values.count
|
42
|
+
assert relation.select_values.include?( "(SELECT max(created_at) AS comments_created_at_max FROM comments WHERE (posts.id = post_id)) AS comments_created_at_max" )
|
43
|
+
assert_equal relation.order_values, ["comments_created_at_max DESC"]
|
44
|
+
assert_empty relation.where_values
|
45
|
+
assert_empty relation.joins_values
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_result_column
|
49
|
+
relation = Post.aggregate_columns( :association => :comments, :function => :max, :column => :created_at, :result_column => :max_created_at )
|
50
|
+
assert_equal 2, relation.select_values.count
|
51
|
+
assert relation.select_values.include?( "(SELECT max(created_at) AS max_created_at FROM comments WHERE (posts.id = post_id)) AS max_created_at" )
|
52
|
+
assert_equal relation.order_values, ["max_created_at DESC"]
|
53
|
+
assert_empty relation.where_values
|
54
|
+
assert_empty relation.joins_values
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_right_join
|
58
|
+
relation = Post.aggregate_columns( :association => :comments, :join_type => :right )
|
59
|
+
assert_equal 2, relation.select_values.count
|
60
|
+
assert relation.select_values.include?( "comment_count" )
|
61
|
+
assert_equal relation.joins_values, ["RIGHT JOIN (SELECT post_id, count(*) AS comment_count FROM comments GROUP BY post_id) comment_count_join ON posts.id = comment_count_join.post_id"]
|
62
|
+
assert_equal ["comment_count DESC"], relation.order_values
|
63
|
+
assert_empty relation.where_values
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_where_clauses
|
67
|
+
relation = Post.where( :id => 2 ).aggregate_columns( :association => :comments ) { |rel| rel.where( :id => 1 ) }
|
68
|
+
assert_equal "posts.id = 2", relation.where_values.first.to_sql # Additional "where" means where_values must be processed with to_sql instead of querying directly
|
69
|
+
assert relation.select_values.include?( "(SELECT count(*) AS comment_count FROM comments WHERE comments.id = 1 AND (posts.id = post_id)) AS comment_count" )
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_multiple_aggregates
|
73
|
+
relation = Post.aggregate_columns( :association => :comments, :join_type => :right ).aggregate_columns( :association => :comments ).aggregate_columns( :association => :comments, :function => :sum, :column => :vote, :result_column => :number_of_votes )
|
74
|
+
assert_empty relation.where_values
|
75
|
+
assert relation.select_values.include?( "comment_count" )
|
76
|
+
assert relation.joins_values.include?( "RIGHT JOIN (SELECT post_id, count(*) AS comment_count FROM comments GROUP BY post_id) comment_count_join ON posts.id = comment_count_join.post_id" )
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# Rails 2 helper - doesn't really work, as do Rails 2 tests themselves
|
2
|
+
|
1
3
|
require 'rubygems'
|
2
4
|
require 'test/unit'
|
3
|
-
gem 'activesupport'
|
5
|
+
#gem 'activesupport'
|
4
6
|
require 'active_support/test_case'
|
5
7
|
require 'active_record'
|
6
8
|
require 'active_record/base'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aggregate_columns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 49
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
version: 0.9.
|
9
|
+
- 5
|
10
|
+
version: 0.9.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Marek Janukowicz/Starware
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-01-03 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -32,6 +32,62 @@ dependencies:
|
|
32
32
|
version: "2"
|
33
33
|
type: :runtime
|
34
34
|
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rake
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: activerecord
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 7
|
58
|
+
segments:
|
59
|
+
- 2
|
60
|
+
version: "2"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: activesupport
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 7
|
72
|
+
segments:
|
73
|
+
- 2
|
74
|
+
version: "2"
|
75
|
+
type: :development
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: activerecord-nulldb-adapter
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
version_requirements: *id005
|
35
91
|
description: ""
|
36
92
|
email:
|
37
93
|
- marek@janukowicz.net
|
@@ -43,10 +99,14 @@ extra_rdoc_files: []
|
|
43
99
|
|
44
100
|
files:
|
45
101
|
- .hgignore
|
102
|
+
- Gemfile
|
46
103
|
- README
|
47
104
|
- Rakefile
|
105
|
+
- aggregate_columns.gemspec
|
48
106
|
- lib/aggregate_columns.rb
|
49
|
-
-
|
107
|
+
- lib/aggregate_columns/version.rb
|
108
|
+
- test/rails_2_test.rb
|
109
|
+
- test/rails_3_test.rb
|
50
110
|
- test/test_helper.rb
|
51
111
|
has_rdoc: true
|
52
112
|
homepage: ""
|
@@ -83,5 +143,6 @@ signing_key:
|
|
83
143
|
specification_version: 3
|
84
144
|
summary: Create and use aggregate columns in Rails applications
|
85
145
|
test_files:
|
86
|
-
- test/
|
146
|
+
- test/rails_2_test.rb
|
147
|
+
- test/rails_3_test.rb
|
87
148
|
- test/test_helper.rb
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class AggregateColumnsTest < ActiveSupport::TestCase
|
4
|
-
|
5
|
-
class Post < FakeModel
|
6
|
-
end
|
7
|
-
|
8
|
-
class User < FakeModel
|
9
|
-
has_many :posts
|
10
|
-
end
|
11
|
-
|
12
|
-
def setup
|
13
|
-
@user = User.new
|
14
|
-
end
|
15
|
-
|
16
|
-
#test "empty options" do
|
17
|
-
# options = User.aggregate_columns_options
|
18
|
-
# assert_equal "users.*", options[:select]
|
19
|
-
# assert options[:order].blank?
|
20
|
-
# assert options[:joins].blank?
|
21
|
-
#end
|
22
|
-
|
23
|
-
#test "empty hash as options should fail" do
|
24
|
-
# assert_raise( ArgumentError ) do
|
25
|
-
# options = User.aggregate_columns_options( {} )
|
26
|
-
# end
|
27
|
-
#end
|
28
|
-
|
29
|
-
#test "only association" do
|
30
|
-
# options = User.aggregate_columns_options( :association => :posts )
|
31
|
-
# assert_equal "users.*, post_count", options[:select]
|
32
|
-
# assert_equal "post_count DESC", options[:order]
|
33
|
-
# assert_equal "LEFT JOIN (SELECT user_id, count(*) AS post_count FROM posts GROUP BY user_id) post_count_join ON posts.id = post_count_join.user_id", options[:joins]
|
34
|
-
#end
|
35
|
-
|
36
|
-
#test "column function" do
|
37
|
-
# options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max )
|
38
|
-
# assert_equal "users.*, posts_created_at_max", options[:select]
|
39
|
-
# assert_equal "posts_created_at_max DESC", options[:order]
|
40
|
-
# assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
41
|
-
#end
|
42
|
-
|
43
|
-
#test "result_column" do
|
44
|
-
# options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :result_column => "last_post_time" )
|
45
|
-
# assert_equal "users.*, last_post_time", options[:select]
|
46
|
-
# assert_equal "last_post_time DESC", options[:order]
|
47
|
-
# assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS last_post_time FROM posts GROUP BY user_id) last_post_time_join ON posts.id = last_post_time_join.user_id", options[:joins]
|
48
|
-
#end
|
49
|
-
|
50
|
-
#test "join_type" do
|
51
|
-
# options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :join_type => "RIGHT" )
|
52
|
-
# assert_equal "users.*, posts_created_at_max", options[:select]
|
53
|
-
# assert_equal "posts_created_at_max DESC", options[:order]
|
54
|
-
# assert_equal "RIGHT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
55
|
-
#end
|
56
|
-
|
57
|
-
#test "conditions" do
|
58
|
-
# options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :conditions => { :active => true } )
|
59
|
-
# assert_equal "users.*, posts_created_at_max", options[:select]
|
60
|
-
# assert_equal "posts_created_at_max DESC", options[:order]
|
61
|
-
# assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts WHERE posts.active = true GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
62
|
-
#end
|
63
|
-
|
64
|
-
#test "joins" do
|
65
|
-
# time = 2.days.ago
|
66
|
-
# options = User.aggregate_columns_options( :association => :posts, :column => :created_at, :function => :max, :joins => "INNER JOIN comments ON comments.post_id = posts.id", :conditions => ["comments.updated_at > ?", time] )
|
67
|
-
# assert_equal "users.*, posts_created_at_max", options[:select]
|
68
|
-
# assert_equal "posts_created_at_max DESC", options[:order]
|
69
|
-
# assert_equal "LEFT JOIN (SELECT user_id, max(created_at) AS posts_created_at_max FROM posts INNER JOIN comments ON comments.post_id = posts.id WHERE comments.updated_at > #{time} GROUP BY user_id) posts_created_at_max_join ON posts.id = posts_created_at_max_join.user_id", options[:joins]
|
70
|
-
#end
|
71
|
-
|
72
|
-
#test "order" do
|
73
|
-
# options = User.aggregate_columns_options( :association => :posts, :order => :asc )
|
74
|
-
# assert_equal "users.*, post_count", options[:select]
|
75
|
-
# assert_equal "post_count ASC", options[:order]
|
76
|
-
# assert_equal "LEFT JOIN (SELECT user_id, count(*) AS post_count FROM posts GROUP BY user_id) post_count_join ON posts.id = post_count_join.user_id", options[:joins]
|
77
|
-
#end
|
78
|
-
|
79
|
-
# TODO:
|
80
|
-
# test multiple aggregates
|
81
|
-
# test with real database
|
82
|
-
end
|