aggregate_columns 0.9.4 → 0.9.5
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.
- 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
|