squeel 1.1.1 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +3 -0
- data/.travis.yml +36 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/README.md +47 -6
- data/Rakefile +14 -2
- data/lib/squeel.rb +9 -1
- data/lib/squeel/adapters/active_record.rb +0 -1
- data/lib/squeel/adapters/active_record/3.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +12 -1
- data/lib/squeel/adapters/active_record/3.1/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +92 -0
- data/lib/squeel/adapters/active_record/4.1/compat.rb +15 -0
- data/lib/squeel/adapters/active_record/4.1/context.rb +88 -0
- data/lib/squeel/adapters/active_record/4.1/preloader_extensions.rb +31 -0
- data/lib/squeel/adapters/active_record/4.1/reflection_extensions.rb +37 -0
- data/lib/squeel/adapters/active_record/4.1/relation_extensions.rb +307 -0
- data/lib/squeel/adapters/active_record/4.2/compat.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/context.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/preloader_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/relation_extensions.rb +108 -0
- data/lib/squeel/adapters/active_record/context.rb +7 -7
- data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +9 -13
- data/lib/squeel/adapters/active_record/relation_extensions.rb +38 -8
- data/lib/squeel/core_ext/symbol.rb +3 -3
- data/lib/squeel/dsl.rb +1 -1
- data/lib/squeel/nodes.rb +1 -0
- data/lib/squeel/nodes/as.rb +12 -0
- data/lib/squeel/nodes/join.rb +8 -4
- data/lib/squeel/nodes/key_path.rb +10 -1
- data/lib/squeel/nodes/node.rb +21 -0
- data/lib/squeel/nodes/stub.rb +8 -4
- data/lib/squeel/nodes/subquery_join.rb +44 -0
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors.rb +2 -0
- data/lib/squeel/visitors/enumeration_visitor.rb +101 -0
- data/lib/squeel/visitors/order_visitor.rb +9 -2
- data/lib/squeel/visitors/predicate_visitor.rb +11 -0
- data/lib/squeel/visitors/preload_visitor.rb +12 -0
- data/lib/squeel/visitors/visitor.rb +89 -13
- data/spec/config.travis.yml +13 -0
- data/spec/config.yml +12 -0
- data/spec/console.rb +3 -12
- data/spec/core_ext/symbol_spec.rb +3 -3
- data/spec/helpers/squeel_helper.rb +8 -5
- data/spec/spec_helper.rb +4 -16
- data/spec/squeel/adapters/active_record/context_spec.rb +8 -4
- data/spec/squeel/adapters/active_record/join_dependency_extensions_spec.rb +123 -38
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +350 -124
- data/spec/squeel/core_ext/symbol_spec.rb +3 -3
- data/spec/squeel/nodes/join_spec.rb +4 -4
- data/spec/squeel/nodes/stub_spec.rb +3 -3
- data/spec/squeel/nodes/subquery_join_spec.rb +46 -0
- data/spec/squeel/visitors/order_visitor_spec.rb +3 -3
- data/spec/squeel/visitors/predicate_visitor_spec.rb +69 -36
- data/spec/squeel/visitors/select_visitor_spec.rb +1 -1
- data/spec/squeel/visitors/visitor_spec.rb +7 -6
- data/spec/support/models.rb +99 -15
- data/spec/support/schema.rb +109 -4
- data/squeel.gemspec +8 -6
- metadata +89 -107
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/spec/blueprints/articles.rb +0 -5
- data/spec/blueprints/comments.rb +0 -5
- data/spec/blueprints/notes.rb +0 -3
- data/spec/blueprints/people.rb +0 -4
- data/spec/blueprints/tags.rb +0 -3
@@ -3,66 +3,151 @@ require 'spec_helper'
|
|
3
3
|
module Squeel
|
4
4
|
module Adapters
|
5
5
|
module ActiveRecord
|
6
|
-
describe JoinDependencyExtensions do
|
7
|
-
before do
|
8
|
-
@jd = new_join_dependency(Person, {}, [])
|
9
|
-
end
|
10
|
-
|
6
|
+
describe "JoinDependencyExtensions" do
|
11
7
|
it 'joins with symbols' do
|
12
|
-
@jd
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
@jd = new_join_dependency(Person, { :articles => :comments }, [])
|
9
|
+
|
10
|
+
if activerecord_version_at_least('4.2.0')
|
11
|
+
@jd.join_constraints([]).should have(2).join_info
|
12
|
+
@jd.join_constraints([]).map(&:joins).flatten.each do |join|
|
13
|
+
join.class.should eq Squeel::InnerJoin
|
14
|
+
end
|
15
|
+
elsif activerecord_version_at_least('4.1.0')
|
16
|
+
@jd.join_constraints([]).should have(2).joins
|
17
|
+
@jd.join_constraints([]).each do |join|
|
18
|
+
join.class.should eq Squeel::InnerJoin
|
19
|
+
end
|
20
|
+
else
|
21
|
+
@jd.join_associations.should have(2).associations
|
22
|
+
@jd.join_associations.each do |association|
|
23
|
+
association.join_type.should eq Squeel::InnerJoin
|
24
|
+
end
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
19
28
|
it 'joins has_many :through associations' do
|
20
|
-
@jd
|
21
|
-
|
22
|
-
|
29
|
+
@jd = new_join_dependency(Person, :authored_article_comments, [])
|
30
|
+
|
31
|
+
if activerecord_version_at_least('4.2.0')
|
32
|
+
@jd.join_constraints([]).should have(1).join_info
|
33
|
+
@jd.join_root.children.first.table_name.should eq 'comments'
|
34
|
+
elsif activerecord_version_at_least('4.1.0')
|
35
|
+
@jd.join_constraints([]).should have(2).joins
|
36
|
+
@jd.join_root.children.first.table_name.should eq 'comments'
|
37
|
+
else
|
38
|
+
@jd.join_associations.should have(1).association
|
39
|
+
@jd.join_associations.first.table_name.should eq 'comments'
|
40
|
+
end
|
23
41
|
end
|
24
42
|
|
25
43
|
it 'joins with stubs' do
|
26
|
-
@jd
|
27
|
-
|
28
|
-
|
29
|
-
|
44
|
+
@jd = new_join_dependency(Person, { Squeel::Nodes::Stub.new(:articles) => Squeel::Nodes::Stub.new(:comments) }, [])
|
45
|
+
|
46
|
+
if activerecord_version_at_least('4.2.0')
|
47
|
+
@jd.join_constraints([]).should have(2).join_info
|
48
|
+
@jd.join_constraints([]).map(&:joins).flatten.each do |join|
|
49
|
+
join.class.should eq Squeel::InnerJoin
|
50
|
+
end
|
51
|
+
@jd.join_root.children.first.table_name.should eq 'articles'
|
52
|
+
@jd.join_root.children.first.children.first.table_name.should eq 'comments'
|
53
|
+
elsif activerecord_version_at_least('4.1.0')
|
54
|
+
@jd.join_constraints([]).should have(2).joins
|
55
|
+
@jd.join_constraints([]).each do |join|
|
56
|
+
join.class.should eq Squeel::InnerJoin
|
57
|
+
end
|
58
|
+
@jd.join_root.children.first.table_name.should eq 'articles'
|
59
|
+
@jd.join_root.children.first.children.first.table_name.should eq 'comments'
|
60
|
+
else
|
61
|
+
@jd.join_associations.should have(2).associations
|
62
|
+
@jd.join_associations.each do |association|
|
63
|
+
association.join_type.should eq Squeel::InnerJoin
|
64
|
+
end
|
65
|
+
@jd.join_associations[0].table_name.should eq 'articles'
|
66
|
+
@jd.join_associations[1].table_name.should eq 'comments'
|
30
67
|
end
|
31
|
-
@jd.join_associations[0].table_name.should eq 'articles'
|
32
|
-
@jd.join_associations[1].table_name.should eq 'comments'
|
33
68
|
end
|
34
69
|
|
35
70
|
it 'joins with key paths' do
|
36
|
-
@jd
|
37
|
-
|
38
|
-
|
39
|
-
|
71
|
+
@jd = new_join_dependency(Person, dsl{ children.children.parent }, [])
|
72
|
+
|
73
|
+
if activerecord_version_at_least('4.2.0')
|
74
|
+
@jd.join_constraints([]).should have(3).join_info
|
75
|
+
@jd.join_constraints([]).map(&:joins).flatten.each do |join|
|
76
|
+
join.class.should eq Squeel::InnerJoin
|
77
|
+
end
|
78
|
+
(children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
|
79
|
+
(children_people2 = children_people.children.first).aliased_table_name.should eq 'children_people_2'
|
80
|
+
children_people2.children.first.aliased_table_name.should eq 'parents_people'
|
81
|
+
elsif activerecord_version_at_least('4.1.0')
|
82
|
+
@jd.join_constraints([]).should have(3).joins
|
83
|
+
@jd.join_constraints([]).each do |join|
|
84
|
+
join.class.should eq Squeel::InnerJoin
|
85
|
+
end
|
86
|
+
(children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
|
87
|
+
(children_people2 = children_people.children.first).aliased_table_name.should eq 'children_people_2'
|
88
|
+
children_people2.children.first.aliased_table_name.should eq 'parents_people'
|
89
|
+
else
|
90
|
+
@jd.join_associations.should have(3).associations
|
91
|
+
@jd.join_associations.each do |association|
|
92
|
+
association.join_type.should eq Squeel::InnerJoin
|
93
|
+
end
|
94
|
+
@jd.join_associations[0].aliased_table_name.should eq 'children_people'
|
95
|
+
@jd.join_associations[1].aliased_table_name.should eq 'children_people_2'
|
96
|
+
@jd.join_associations[2].aliased_table_name.should eq 'parents_people'
|
40
97
|
end
|
41
|
-
@jd.join_associations[0].aliased_table_name.should eq 'children_people'
|
42
|
-
@jd.join_associations[1].aliased_table_name.should eq 'children_people_2'
|
43
|
-
@jd.join_associations[2].aliased_table_name.should eq 'parents_people'
|
44
98
|
end
|
45
99
|
|
46
100
|
it 'joins with key paths as keys' do
|
47
|
-
@jd
|
48
|
-
|
49
|
-
|
50
|
-
|
101
|
+
@jd = new_join_dependency(Person, dsl{ { children.parent => parent } }, [])
|
102
|
+
|
103
|
+
if activerecord_version_at_least('4.2.0')
|
104
|
+
@jd.join_constraints([]).should have(3).join_info
|
105
|
+
@jd.join_constraints([]).map(&:joins).flatten.each do |join|
|
106
|
+
join.class.should eq Squeel::InnerJoin
|
107
|
+
end
|
108
|
+
(children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
|
109
|
+
(parents_people = children_people.children.first).aliased_table_name.should eq 'parents_people'
|
110
|
+
parents_people.children.first.aliased_table_name.should eq 'parents_people_2'
|
111
|
+
elsif activerecord_version_at_least('4.1.0')
|
112
|
+
@jd.join_constraints([]).should have(3).joins
|
113
|
+
@jd.join_constraints([]).each do |join|
|
114
|
+
join.class.should eq Squeel::InnerJoin
|
115
|
+
end
|
116
|
+
(children_people = @jd.join_root.children.first).aliased_table_name.should eq 'children_people'
|
117
|
+
(parents_people = children_people.children.first).aliased_table_name.should eq 'parents_people'
|
118
|
+
parents_people.children.first.aliased_table_name.should eq 'parents_people_2'
|
119
|
+
else
|
120
|
+
@jd.join_associations.should have(3).associations
|
121
|
+
@jd.join_associations.each do |association|
|
122
|
+
association.join_type.should eq Squeel::InnerJoin
|
123
|
+
end
|
124
|
+
@jd.join_associations[0].aliased_table_name.should eq 'children_people'
|
125
|
+
@jd.join_associations[1].aliased_table_name.should eq 'parents_people'
|
126
|
+
@jd.join_associations[2].aliased_table_name.should eq 'parents_people_2'
|
51
127
|
end
|
52
|
-
@jd.join_associations[0].aliased_table_name.should eq 'children_people'
|
53
|
-
@jd.join_associations[1].aliased_table_name.should eq 'parents_people'
|
54
|
-
@jd.join_associations[2].aliased_table_name.should eq 'parents_people_2'
|
55
128
|
end
|
56
129
|
|
57
130
|
it 'joins using outer joins' do
|
58
|
-
@jd
|
59
|
-
|
60
|
-
|
61
|
-
|
131
|
+
@jd = new_join_dependency(Person, { :articles.outer => :comments.outer }, [])
|
132
|
+
|
133
|
+
if activerecord_version_at_least('4.2.0')
|
134
|
+
@jd.join_constraints([]).should have(2).join_info
|
135
|
+
@jd.join_constraints([]).map(&:joins).flatten.each do |join|
|
136
|
+
join.class.should eq Squeel::OuterJoin
|
137
|
+
end
|
138
|
+
elsif activerecord_version_at_least('4.1.0')
|
139
|
+
@jd.join_constraints([]).should have(2).joins
|
140
|
+
@jd.join_constraints([]).each do |join|
|
141
|
+
join.class.should eq Squeel::OuterJoin
|
142
|
+
end
|
143
|
+
else
|
144
|
+
@jd.join_associations.should have(2).associations
|
145
|
+
@jd.join_associations.each do |association|
|
146
|
+
association.join_type.should eq Squeel::OuterJoin
|
147
|
+
end
|
62
148
|
end
|
63
149
|
end
|
64
|
-
|
65
150
|
end
|
66
151
|
end
|
67
152
|
end
|
68
|
-
end
|
153
|
+
end
|
@@ -15,8 +15,21 @@ module Squeel
|
|
15
15
|
queries = queries_for do
|
16
16
|
Person.find_by_id('')
|
17
17
|
end
|
18
|
-
|
19
|
-
|
18
|
+
if activerecord_version_at_least('3.1.0')
|
19
|
+
queries.should have(1).query
|
20
|
+
else
|
21
|
+
puts 'skips count of queries expectation'
|
22
|
+
end
|
23
|
+
|
24
|
+
if activerecord_version_at_least('4.2.0')
|
25
|
+
if PG_ENV
|
26
|
+
queries.last.should match /#{Q}people#{Q}.#{Q}id#{Q} = \$1/
|
27
|
+
else
|
28
|
+
queries.last.should match /#{Q}people#{Q}.#{Q}id#{Q} = ?/
|
29
|
+
end
|
30
|
+
else
|
31
|
+
queries.last.should match /#{Q}people#{Q}.#{Q}id#{Q} = 0/
|
32
|
+
end
|
20
33
|
end
|
21
34
|
|
22
35
|
end
|
@@ -34,8 +47,12 @@ module Squeel
|
|
34
47
|
|
35
48
|
arel = relation.build_arel
|
36
49
|
|
37
|
-
|
38
|
-
|
50
|
+
if activerecord_version_at_least('4.1.0')
|
51
|
+
relation.join_dependency.join_constraints([]).should have(4).items
|
52
|
+
else
|
53
|
+
relation.join_dependency.join_associations.should have(4).items
|
54
|
+
end
|
55
|
+
arel.to_sql.should match /INNER JOIN #{Q}people#{Q} #{Q}parents_people_2#{Q} ON #{Q}parents_people_2#{Q}.#{Q}id#{Q} = #{Q}parents_people#{Q}.#{Q}parent_id#{Q}/
|
39
56
|
end
|
40
57
|
|
41
58
|
it 'joins associations with custom join types' do
|
@@ -49,9 +66,13 @@ module Squeel
|
|
49
66
|
|
50
67
|
arel = relation.build_arel
|
51
68
|
|
52
|
-
|
53
|
-
|
54
|
-
|
69
|
+
if activerecord_version_at_least('4.1.0')
|
70
|
+
relation.join_dependency.join_constraints([]).should have(4).items
|
71
|
+
else
|
72
|
+
relation.join_dependency.join_associations.should have(4).items
|
73
|
+
end
|
74
|
+
arel.to_sql.should match /LEFT OUTER JOIN #{Q}people#{Q} #{Q}children_people#{Q}/
|
75
|
+
arel.to_sql.should match /LEFT OUTER JOIN #{Q}people#{Q} #{Q}parents_people_2#{Q} ON #{Q}parents_people_2#{Q}.#{Q}id#{Q} = #{Q}parents_people#{Q}.#{Q}parent_id#{Q}/
|
55
76
|
end
|
56
77
|
|
57
78
|
it 'only joins an association once, even if two overlapping joins_values hashes are given' do
|
@@ -70,8 +91,12 @@ module Squeel
|
|
70
91
|
})
|
71
92
|
|
72
93
|
arel = relation.build_arel
|
73
|
-
|
74
|
-
|
94
|
+
if activerecord_version_at_least('4.1.0')
|
95
|
+
relation.join_dependency.join_constraints([]).should have(6).items
|
96
|
+
else
|
97
|
+
relation.join_dependency.join_associations.should have(6).items
|
98
|
+
end
|
99
|
+
arel.to_sql.should match /INNER JOIN #{Q}people#{Q} #{Q}parents_people_3#{Q} ON #{Q}parents_people_3#{Q}.#{Q}id#{Q} = #{Q}children_people_3#{Q}.#{Q}parent_id#{Q}/
|
75
100
|
end
|
76
101
|
|
77
102
|
it 'respects :uniq option on associations' do
|
@@ -81,15 +106,15 @@ module Squeel
|
|
81
106
|
it 'visits wheres with a PredicateVisitor, converting them to Arel nodes' do
|
82
107
|
relation = Person.where(:name.matches => '%bob%')
|
83
108
|
arel = relation.build_arel
|
84
|
-
arel.to_sql.should match
|
109
|
+
arel.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%bob%'/
|
85
110
|
end
|
86
111
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
112
|
+
it 'handles multiple wheres using a keypath' do
|
113
|
+
relation = Person.joins{articles}.where{articles.title == 'Hello'}.
|
114
|
+
where{articles.body == 'World'}
|
115
|
+
arel = relation.build_arel
|
116
|
+
arel.to_sql.should match /articles/
|
117
|
+
end
|
93
118
|
|
94
119
|
it 'maps wheres inside a hash to their appropriate association table' do
|
95
120
|
relation = Person.joins({
|
@@ -109,15 +134,14 @@ module Squeel
|
|
109
134
|
})
|
110
135
|
|
111
136
|
arel = relation.build_arel
|
112
|
-
|
113
|
-
arel.to_sql.should match /"parents_people_2"."name" = 'bob'/
|
137
|
+
arel.to_sql.should match /#{Q}parents_people_2#{Q}.#{Q}name#{Q} = 'bob'/
|
114
138
|
end
|
115
139
|
|
116
140
|
it 'combines multiple conditions of the same type against the same column with AND' do
|
117
141
|
relation = Person.where(:name.matches => '%bob%')
|
118
142
|
relation = relation.where(:name.matches => '%joe%')
|
119
143
|
arel = relation.build_arel
|
120
|
-
arel.to_sql.should match
|
144
|
+
arel.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%bob%' AND #{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%joe%'/
|
121
145
|
end
|
122
146
|
|
123
147
|
it 'handles ORs between predicates' do
|
@@ -129,7 +153,7 @@ module Squeel
|
|
129
153
|
it 'maintains groupings as given' do
|
130
154
|
relation = Person.where(dsl{(name == 'Ernie') | ((name =~ 'Bob%') & (name =~ '%by'))})
|
131
155
|
arel = relation.build_arel
|
132
|
-
arel.to_sql.should match
|
156
|
+
arel.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'Ernie' OR \(#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE 'Bob%' AND #{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%by'\)/
|
133
157
|
end
|
134
158
|
|
135
159
|
it 'maps havings inside a hash to their appropriate association table' do
|
@@ -150,8 +174,7 @@ module Squeel
|
|
150
174
|
})
|
151
175
|
|
152
176
|
arel = relation.build_arel
|
153
|
-
|
154
|
-
arel.to_sql.should match /HAVING "parents_people_2"."name" = 'joe'/
|
177
|
+
arel.to_sql.should match /HAVING #{Q}parents_people_2#{Q}.#{Q}name#{Q} = 'joe'/
|
155
178
|
end
|
156
179
|
|
157
180
|
it 'maps orders inside a hash to their appropriate association table' do
|
@@ -173,8 +196,7 @@ module Squeel
|
|
173
196
|
})
|
174
197
|
|
175
198
|
arel = relation.build_arel
|
176
|
-
|
177
|
-
arel.to_sql.should match /ORDER BY "parents_people_2"."id" ASC/
|
199
|
+
arel.to_sql.should match /ORDER BY #{Q}parents_people_2#{Q}.#{Q}id#{Q} ASC/
|
178
200
|
else
|
179
201
|
pending 'Unsupported in ActiveRecord 4.0.0+'
|
180
202
|
end
|
@@ -191,7 +213,6 @@ module Squeel
|
|
191
213
|
|
192
214
|
it 'reverses order of Arel::Attributes when #last is called' do
|
193
215
|
sorted_people = Person.all.to_a.sort {|a, b| a.name.downcase <=> b.name.downcase}
|
194
|
-
|
195
216
|
Person.order{name}.last.should eq sorted_people.last
|
196
217
|
end
|
197
218
|
|
@@ -245,46 +266,77 @@ module Squeel
|
|
245
266
|
|
246
267
|
it 'eager loads belongs_to associations' do
|
247
268
|
queries = queries_for do
|
248
|
-
|
249
|
-
|
269
|
+
if activerecord_version_at_least('4.1.0')
|
270
|
+
Article.includes(:person).references(:person).
|
271
|
+
where{person.name == 'Ernie'}.to_a
|
272
|
+
else
|
273
|
+
Article.includes(:person).
|
274
|
+
where{person.name == 'Ernie'}.to_a
|
275
|
+
end
|
250
276
|
end
|
251
|
-
|
252
|
-
|
253
|
-
|
277
|
+
|
278
|
+
if activerecord_version_at_least('4.1.0')
|
279
|
+
queries.should have(1).item
|
280
|
+
else
|
281
|
+
puts 'skips count of queries expectation.'
|
282
|
+
end
|
283
|
+
|
284
|
+
queries.last.should match /LEFT OUTER JOIN #{Q}people#{Q}/
|
285
|
+
queries.last.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'Ernie'/
|
254
286
|
end
|
255
287
|
|
256
288
|
it 'eager loads belongs_to associations on models with default_scopes' do
|
257
289
|
queries = queries_for do
|
258
|
-
|
259
|
-
|
290
|
+
if activerecord_version_at_least('4.1.0')
|
291
|
+
PersonNamedBill.includes(:parent).references(:parent).
|
292
|
+
where{parent.name == 'Ernie'}.to_a
|
293
|
+
else
|
294
|
+
PersonNamedBill.includes(:parent).
|
295
|
+
where{parent.name == 'Ernie'}.to_a
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
if activerecord_version_at_least('4.1.0')
|
301
|
+
queries.should have(1).item
|
302
|
+
else
|
303
|
+
puts 'skips count of queries expectation.'
|
260
304
|
end
|
261
|
-
|
262
|
-
queries.
|
263
|
-
queries.
|
264
|
-
queries.
|
305
|
+
|
306
|
+
queries.last.should match /LEFT OUTER JOIN #{Q}people#{Q}/
|
307
|
+
queries.last.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'Bill'/
|
308
|
+
queries.last.should match /#{Q}parents_people#{Q}.#{Q}name#{Q} = 'Ernie'/
|
265
309
|
end
|
266
310
|
|
267
311
|
it 'eager loads polymorphic belongs_to associations' do
|
268
312
|
relation = Note.includes{notable(Article)}.where{{notable(Article) => {title => 'hey'}}}
|
269
|
-
relation.debug_sql.should match
|
313
|
+
relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
|
270
314
|
end
|
271
315
|
|
272
316
|
it 'eager loads multiple polymorphic belongs_to associations' do
|
273
317
|
relation = Note.includes{[notable(Article), notable(Person)]}.
|
274
318
|
where{{notable(Article) => {title => 'hey'}}}.
|
275
319
|
where{{notable(Person) => {name => 'joe'}}}
|
276
|
-
relation.debug_sql.should match
|
277
|
-
relation.debug_sql.should match
|
320
|
+
relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
|
321
|
+
relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
|
278
322
|
end
|
279
323
|
|
280
|
-
it
|
324
|
+
it 'only includes once, even if two join types are used' do
|
281
325
|
relation = Person.includes(:articles.inner, :articles.outer).where(:articles => {:title => 'hey'})
|
282
|
-
|
326
|
+
if activerecord_version_at_least('4.1.0')
|
327
|
+
pending ":article != :article.inner != :article.outer, you shouldn't pass two same tables into includes again"
|
328
|
+
else
|
329
|
+
relation.debug_sql.scan("JOIN").size.should eq 1
|
330
|
+
end
|
283
331
|
end
|
284
332
|
|
285
333
|
it 'includes a keypath' do
|
286
|
-
|
287
|
-
|
334
|
+
if activerecord_version_at_least('4.1.0')
|
335
|
+
relation = Note.includes{notable(Article).person.children}.references(:all).where{notable(Article).person.children.name == 'Ernie'}
|
336
|
+
else
|
337
|
+
relation = Note.includes{notable(Article).person.children}.where{notable(Article).person.children.name == 'Ernie'}
|
338
|
+
end
|
339
|
+
relation.debug_sql.should match /SELECT #{Q}notes#{Q}.* FROM #{Q}notes#{Q} LEFT OUTER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}id#{Q} = #{Q}notes#{Q}.#{Q}notable_id#{Q} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article' LEFT OUTER JOIN #{Q}people#{Q} ON #{Q}people#{Q}.#{Q}id#{Q} = #{Q}articles#{Q}.#{Q}person_id#{Q} LEFT OUTER JOIN #{Q}people#{Q} #{Q}children_people#{Q} ON #{Q}children_people#{Q}.#{Q}parent_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q} WHERE #{Q}children_people#{Q}.#{Q}name#{Q} = 'Ernie'/
|
288
340
|
end
|
289
341
|
|
290
342
|
end
|
@@ -311,7 +363,6 @@ module Squeel
|
|
311
363
|
}}
|
312
364
|
|
313
365
|
queries_for {relation.to_a}.should have(4).items
|
314
|
-
|
315
366
|
queries_for {
|
316
367
|
relation.first.articles
|
317
368
|
relation.first.articles.first.comments
|
@@ -327,8 +378,12 @@ module Squeel
|
|
327
378
|
standard = Person.eager_load(:children => :children)
|
328
379
|
block = Person.eager_load{{children => children}}
|
329
380
|
block.debug_sql.should eq standard.debug_sql
|
330
|
-
|
331
|
-
|
381
|
+
if activerecord_version_at_least('3.2.0')
|
382
|
+
queries_for {block.to_a}.should have(1).item
|
383
|
+
queries_for {block.first.children}.should have(0).items
|
384
|
+
else
|
385
|
+
puts 'skips count of queries expectation.'
|
386
|
+
end
|
332
387
|
end
|
333
388
|
|
334
389
|
it 'eager loads multiple top-level associations with a block' do
|
@@ -339,23 +394,27 @@ module Squeel
|
|
339
394
|
|
340
395
|
it 'eager loads polymorphic belongs_to associations' do
|
341
396
|
relation = Note.eager_load{notable(Article)}
|
342
|
-
relation.debug_sql.should match
|
397
|
+
relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
|
343
398
|
end
|
344
399
|
|
345
400
|
it 'eager loads multiple polymorphic belongs_to associations' do
|
346
401
|
relation = Note.eager_load{[notable(Article), notable(Person)]}
|
347
|
-
relation.debug_sql.should match
|
348
|
-
relation.debug_sql.should match
|
402
|
+
relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
|
403
|
+
relation.debug_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
|
349
404
|
end
|
350
405
|
|
351
406
|
it "only eager_load once, even if two join types are used" do
|
352
407
|
relation = Person.eager_load(:articles.inner, :articles.outer)
|
353
|
-
|
408
|
+
if activerecord_version_at_least('4.1.0')
|
409
|
+
pending ":article != :article.inner != :article.outer, you shouldn't pass two same tables into eager_load again"
|
410
|
+
else
|
411
|
+
relation.debug_sql.scan("JOIN").size.should eq 1
|
412
|
+
end
|
354
413
|
end
|
355
414
|
|
356
415
|
it 'eager_load a keypath' do
|
357
416
|
relation = Note.eager_load{notable(Article).person.children}
|
358
|
-
relation.debug_sql.should match /SELECT
|
417
|
+
relation.debug_sql.should match /SELECT #{Q}notes#{Q}.* FROM #{Q}notes#{Q} LEFT OUTER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}id#{Q} = #{Q}notes#{Q}.#{Q}notable_id#{Q} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article' LEFT OUTER JOIN #{Q}people#{Q} ON #{Q}people#{Q}.#{Q}id#{Q} = #{Q}articles#{Q}.#{Q}person_id#{Q} LEFT OUTER JOIN #{Q}people#{Q} #{Q}children_people#{Q} ON #{Q}children_people#{Q}.#{Q}parent_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q}/
|
359
418
|
end
|
360
419
|
|
361
420
|
end
|
@@ -385,39 +444,52 @@ module Squeel
|
|
385
444
|
|
386
445
|
it 'behaves as normal with standard parameters' do
|
387
446
|
people = Person.select(:id)
|
388
|
-
people.should have(
|
389
|
-
|
447
|
+
people.should have(10).people
|
448
|
+
if ::ActiveRecord::VERSION::MAJOR == 3 && ::ActiveRecord::VERSION::MINOR == 0 && RUBY_VERSION >= '2.0.0'
|
449
|
+
people.first.name.should be_nil
|
450
|
+
else
|
451
|
+
expect { people.first.name }.to raise_error ActiveModel::MissingAttributeError
|
452
|
+
end
|
390
453
|
end
|
391
454
|
|
392
455
|
it 'works with multiple fields in select' do
|
393
|
-
Article.select("title, body").
|
456
|
+
Article.select("title, body").size.should eq 31
|
394
457
|
end
|
395
458
|
|
396
459
|
it 'allows a function in the select values via Symbol#func' do
|
397
|
-
relation = Person.select(:max.func(:id).as('max_id'))
|
398
|
-
relation.first.max_id.should eq
|
460
|
+
relation = Person.select(:max.func(:id).as('max_id')).order('max_id')
|
461
|
+
relation.first.max_id.to_i.should eq 10
|
399
462
|
end
|
400
463
|
|
401
464
|
it 'allows a function in the select values via block' do
|
402
|
-
relation = Person.select{max(id).as(max_id)}
|
403
|
-
relation.first.max_id.should eq
|
465
|
+
relation = Person.select{max(id).as(max_id)}.order('max_id')
|
466
|
+
relation.first.max_id.to_i.should eq 10
|
404
467
|
end
|
405
468
|
|
406
469
|
it 'allows an operation in the select values via block' do
|
407
|
-
relation =
|
408
|
-
|
470
|
+
relation =
|
471
|
+
if SQLITE_ENV
|
472
|
+
Person.select{[id, (id + 1).as('id_plus_one')]}.where('id_plus_one = 2')
|
473
|
+
else
|
474
|
+
Person.select{[id, (id + 1).as('id_plus_one')]}.where{(id + 1) == 2}
|
475
|
+
end
|
476
|
+
relation.first.id.should eq 1
|
409
477
|
end
|
410
478
|
|
411
479
|
it 'allows custom operators in the select values via block' do
|
412
|
-
|
413
|
-
|
480
|
+
if MYSQL_ENV
|
481
|
+
pending "MySQL doesn't support concating string with ||."
|
482
|
+
else
|
483
|
+
relation = Person.select{name.op('||', '-diddly').as(flanderized_name)}
|
484
|
+
relation.first.flanderized_name.should eq Person.first.name + '-diddly'
|
485
|
+
end
|
414
486
|
end
|
415
487
|
|
416
488
|
it 'allows a subquery in the select values' do
|
417
489
|
subquery = Article.where(:person_id => 1).select(:id).order{id.desc}.limit(1)
|
418
490
|
relation = Person.where(:id => 1).select{[id, name, subquery.as('last_article_id')]}
|
419
491
|
aric = relation.first
|
420
|
-
aric.last_article_id.should eq Article.where(:person_id => 1).last.id
|
492
|
+
aric.last_article_id.to_i.should eq Article.where(:person_id => 1).last.id
|
421
493
|
end
|
422
494
|
|
423
495
|
end
|
@@ -425,7 +497,7 @@ module Squeel
|
|
425
497
|
describe '#count' do
|
426
498
|
|
427
499
|
it 'works with non-strings in select' do
|
428
|
-
Article.select{distinct(title)}.count.should eq
|
500
|
+
Article.select{distinct(title)}.count.should eq 31
|
429
501
|
end
|
430
502
|
|
431
503
|
it 'works with non-strings in wheres' do
|
@@ -463,17 +535,17 @@ module Squeel
|
|
463
535
|
|
464
536
|
it 'builds compound conditions with a block' do
|
465
537
|
block = Person.where{(name == 'bob') & (salary == 100000)}
|
466
|
-
block.to_sql.should match
|
538
|
+
block.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
|
467
539
|
block.to_sql.should match /AND/
|
468
|
-
block.to_sql.should match
|
540
|
+
block.to_sql.should match /#{Q}people#{Q}.#{Q}salary#{Q} = 100000/
|
469
541
|
end
|
470
542
|
|
471
543
|
it 'allows mixing hash and operator syntax inside a block' do
|
472
544
|
block = Person.joins(:comments).
|
473
545
|
where{(name == 'bob') & {comments => (body == 'First post!')}}
|
474
|
-
block.to_sql.should match
|
546
|
+
block.to_sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
|
475
547
|
block.to_sql.should match /AND/
|
476
|
-
block.to_sql.should match
|
548
|
+
block.to_sql.should match /#{Q}comments#{Q}.#{Q}body#{Q} = 'First post!'/
|
477
549
|
end
|
478
550
|
|
479
551
|
it 'allows a condition on a function via block' do
|
@@ -496,6 +568,12 @@ module Squeel
|
|
496
568
|
people_and_article_notes.should have(40).items
|
497
569
|
end
|
498
570
|
|
571
|
+
it 'maps conditions onto their proper table with a polymorphic belongs_to join followed by a polymorphic has_many join' do
|
572
|
+
relation = Note.joins{notable(Article).notes}.
|
573
|
+
where{notable(Article).notes.note.eq('zomg')}
|
574
|
+
relation.to_sql.should match /#{Q}notes_articles#{Q}\.#{Q}note#{Q} = 'zomg'/
|
575
|
+
end
|
576
|
+
|
499
577
|
it 'allows a subquery on the value side of a predicate' do
|
500
578
|
names = [Person.first.name, Person.last.name]
|
501
579
|
old_and_busted = Person.where(:name => names)
|
@@ -504,6 +582,21 @@ module Squeel
|
|
504
582
|
old_and_busted.to_a.should eq new_hotness.to_a
|
505
583
|
end
|
506
584
|
|
585
|
+
it 'allows a subquery from an association in a hash' do
|
586
|
+
scope = Person.first.articles
|
587
|
+
articles = scope.where(:id => scope)
|
588
|
+
articles.should have(3).articles
|
589
|
+
|
590
|
+
articles = Tag.all.second.articles.where(:id => scope)
|
591
|
+
articles.should have(1).articles
|
592
|
+
end
|
593
|
+
|
594
|
+
it 'allows a subquery from an association in a Squeel node' do
|
595
|
+
scope = Person.first.articles
|
596
|
+
articles = scope.where{id.in scope}
|
597
|
+
articles.should have(3).articles
|
598
|
+
end
|
599
|
+
|
507
600
|
it 'is backwards-compatible with "where.not"' do
|
508
601
|
if activerecord_version_at_least '4.0.0'
|
509
602
|
name = Person.first.name
|
@@ -517,37 +610,61 @@ module Squeel
|
|
517
610
|
it 'allows equality conditions against a belongs_to with an AR::Base value' do
|
518
611
|
first_person = Person.first
|
519
612
|
relation = Article.where { person.eq first_person }
|
520
|
-
relation.to_sql.should match
|
613
|
+
relation.to_sql.should match /#{Q}articles#{Q}.#{Q}person_id#{Q} = #{first_person.id}/
|
521
614
|
end
|
522
615
|
|
523
616
|
it 'allows equality conditions against a polymorphic belongs_to with an AR::Base value' do
|
524
617
|
first_person = Person.first
|
525
618
|
relation = Note.where { notable.eq first_person }
|
526
|
-
relation.to_sql.should match
|
619
|
+
relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_id#{Q} = #{first_person.id} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
|
527
620
|
end
|
528
621
|
|
529
622
|
it 'allows inequality conditions against a belongs_to with an AR::Base value' do
|
530
623
|
first_person = Person.first
|
531
624
|
relation = Article.where { person.not_eq first_person }
|
532
|
-
relation.to_sql.should match
|
625
|
+
relation.to_sql.should match /#{Q}articles#{Q}.#{Q}person_id#{Q} != #{first_person.id}/
|
533
626
|
end
|
534
627
|
|
535
628
|
it 'allows inequality conditions against a polymorphic belongs_to with an AR::Base value' do
|
536
629
|
first_person = Person.first
|
537
630
|
relation = Note.where { notable.not_eq first_person }
|
538
|
-
relation.to_sql.should match /\(
|
631
|
+
relation.to_sql.should match /\(#{Q}notes#{Q}.#{Q}notable_id#{Q} != #{first_person.id} OR #{Q}notes#{Q}.#{Q}notable_type#{Q} != 'Person'\)/
|
539
632
|
end
|
540
633
|
|
541
634
|
it 'allows hash equality conditions against a belongs_to with an AR::Base value' do
|
542
635
|
first_person = Person.first
|
543
636
|
relation = Article.where(:person => first_person)
|
544
|
-
relation.to_sql.should match
|
637
|
+
relation.to_sql.should match /#{Q}articles#{Q}.#{Q}person_id#{Q} = #{first_person.id}/
|
545
638
|
end
|
546
639
|
|
547
640
|
it 'allows hash equality conditions against a polymorphic belongs_to with an AR::Base value' do
|
548
641
|
first_person = Person.first
|
549
642
|
relation = Note.where(:notable => first_person)
|
550
|
-
relation.to_sql.should match
|
643
|
+
relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
|
644
|
+
relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_id#{Q} = #{first_person.id}/
|
645
|
+
end
|
646
|
+
|
647
|
+
it 'keeps original AR hashes behavior' do
|
648
|
+
relation = Person.joins(:articles).where(articles: { person_id: Person.first })
|
649
|
+
relation.to_sql.should match /SELECT #{Q}people#{Q}.* FROM #{Q}people#{Q} INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}person_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q} WHERE #{Q}articles#{Q}.#{Q}person_id#{Q} = 1/
|
650
|
+
|
651
|
+
relation = Person.joins(:articles).where(articles: { person_id: Person.all.to_a })
|
652
|
+
relation.to_sql.should match /SELECT #{Q}people#{Q}.\* FROM #{Q}people#{Q} INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}person_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q} WHERE #{Q}articles#{Q}.#{Q}person_id#{Q} IN \(1, 2, 3, 4, 5, 6, 7, 8, 9, 10\)/
|
653
|
+
end
|
654
|
+
|
655
|
+
it 'returns ActiveRecord::Relation after complex associations, joins and wheres' do
|
656
|
+
relation = Note.first.notable.articles.joins(:comments).where{comments.article_id != nil}
|
657
|
+
|
658
|
+
relation.should be_kind_of(::ActiveRecord::Relation)
|
659
|
+
relation.first.should be_kind_of(Article)
|
660
|
+
end
|
661
|
+
|
662
|
+
it 'uses Squeel and Arel at the same time' do
|
663
|
+
relation = User.where{id.in([1,2,3]) & User.arel_table[:id].not_eq(nil) }
|
664
|
+
relation.to_sql.should match /SELECT #{Q}users#{Q}.\* FROM #{Q}users#{Q}\s+WHERE \(\(#{Q}users#{Q}.#{Q}id#{Q} IN \(1, 2, 3\) AND #{Q}users#{Q}.#{Q}id#{Q} IS NOT NULL\)\)/
|
665
|
+
relation = User.where{
|
666
|
+
(id.in([1,2,3]) | User.arel_table[:id].eq(1)) & ((id == 1) | User.arel_table[:id].not_eq(nil)) }
|
667
|
+
relation.to_sql.should match /SELECT #{Q}users#{Q}.\* FROM #{Q}users#{Q}\s+WHERE \(\(\(#{Q}users#{Q}.#{Q}id#{Q} IN \(1, 2, 3\) OR #{Q}users#{Q}.#{Q}id#{Q} = 1\) AND \(#{Q}users#{Q}.#{Q}id#{Q} = 1 OR #{Q}users#{Q}.#{Q}id#{Q} IS NOT NULL\)\)\)/
|
551
668
|
end
|
552
669
|
|
553
670
|
end
|
@@ -577,25 +694,66 @@ module Squeel
|
|
577
694
|
|
578
695
|
it 'joins polymorphic belongs_to associations' do
|
579
696
|
relation = Note.joins{notable(Article)}
|
580
|
-
relation.to_sql.should match
|
697
|
+
relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
|
581
698
|
end
|
582
699
|
|
583
700
|
it 'joins multiple polymorphic belongs_to associations' do
|
584
701
|
relation = Note.joins{[notable(Article), notable(Person)]}
|
585
|
-
relation.to_sql.should match
|
586
|
-
relation.to_sql.should match
|
702
|
+
relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article'/
|
703
|
+
relation.to_sql.should match /#{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Person'/
|
587
704
|
end
|
588
705
|
|
589
706
|
it "only joins once, even if two join types are used" do
|
590
|
-
|
591
|
-
|
707
|
+
if activerecord_version_at_least('4.1.0')
|
708
|
+
pending "It's unreasonable to join once only, in some cases, we need twice."
|
709
|
+
else
|
710
|
+
relation = Person.joins(:articles.inner, :articles.outer)
|
711
|
+
relation.to_sql.scan("JOIN").size.should eq 1
|
712
|
+
end
|
592
713
|
end
|
593
714
|
|
594
715
|
it 'joins a keypath' do
|
595
716
|
relation = Note.joins{notable(Article).person.children}
|
596
|
-
relation.to_sql.should match /SELECT
|
717
|
+
relation.to_sql.should match /SELECT #{Q}notes#{Q}.* FROM #{Q}notes#{Q} INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}id#{Q} = #{Q}notes#{Q}.#{Q}notable_id#{Q} AND #{Q}notes#{Q}.#{Q}notable_type#{Q} = 'Article' INNER JOIN #{Q}people#{Q} ON #{Q}people#{Q}.#{Q}id#{Q} = #{Q}articles#{Q}.#{Q}person_id#{Q} INNER JOIN #{Q}people#{Q} #{Q}children_people#{Q} ON #{Q}children_people#{Q}.#{Q}parent_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q}/
|
597
718
|
end
|
598
719
|
|
720
|
+
it 'validates polymorphic relationship with source type' do
|
721
|
+
if activerecord_version_at_least '3.2.7'
|
722
|
+
Group.first.users.to_sql.should match /#{Q}memberships#{Q}.#{Q}member_type#{Q} = 'User'/
|
723
|
+
else
|
724
|
+
Group.first.users.size.should eq 1
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
it 'joins an ActiveRecord::Relation subquery' do
|
729
|
+
subquery = OrderItem.
|
730
|
+
group(:orderable_id).
|
731
|
+
select { [orderable_id, sum(quantity * unit_price).as(amount)] }
|
732
|
+
|
733
|
+
relation = Seat.
|
734
|
+
joins { [payment.outer,
|
735
|
+
subquery.as('seat_order_items').on { id == seat_order_items.orderable_id}.outer] }.
|
736
|
+
select { [seat_order_items.amount, "seats.*"] }.
|
737
|
+
where { seat_order_items.amount > 0 }
|
738
|
+
|
739
|
+
relation.debug_sql.should match /SELECT #{Q}seat_order_items#{Q}.#{Q}amount#{Q}, seats.\* FROM #{Q}seats#{Q} LEFT OUTER JOIN #{Q}payments#{Q} ON #{Q}payments#{Q}.#{Q}id#{Q} = #{Q}seats#{Q}.#{Q}payment_id#{Q} LEFT OUTER JOIN \(SELECT #{Q}order_items#{Q}.#{Q}orderable_id#{Q}, sum\(#{Q}order_items#{Q}.#{Q}quantity#{Q} \* #{Q}order_items#{Q}.#{Q}unit_price#{Q}\) AS amount FROM #{Q}order_items#{Q}\s+GROUP BY #{Q}order_items#{Q}.#{Q}orderable_id#{Q}\) seat_order_items ON #{Q}seats#{Q}.#{Q}id#{Q} = #{Q}seat_order_items#{Q}.#{Q}orderable_id#{Q} WHERE #{Q}seat_order_items#{Q}.#{Q}amount#{Q} > 0/
|
740
|
+
relation.to_a.should have(10).seats
|
741
|
+
relation.to_a.second.amount.to_i.should eq(10)
|
742
|
+
end
|
743
|
+
|
744
|
+
it 'joins from an association with default scopes' do
|
745
|
+
if activerecord_version_at_least('3.1.0')
|
746
|
+
if MYSQL_ENV
|
747
|
+
User.first.groups.to_sql.should match /#{Q}memberships#{Q}.#{Q}active#{Q} = 1/
|
748
|
+
else
|
749
|
+
puts User.first.groups.to_sql
|
750
|
+
User.first.groups.to_sql.should match /#{Q}memberships#{Q}.#{Q}active#{Q} = 't'/
|
751
|
+
end
|
752
|
+
|
753
|
+
else
|
754
|
+
pending "Rails 3.0.x doesn't support to_sql in an association."
|
755
|
+
end
|
756
|
+
end
|
599
757
|
end
|
600
758
|
|
601
759
|
describe '#having' do
|
@@ -607,8 +765,12 @@ module Squeel
|
|
607
765
|
end
|
608
766
|
|
609
767
|
it 'allows complex conditions on aggregate columns' do
|
610
|
-
|
611
|
-
|
768
|
+
if SQLITE_ENV
|
769
|
+
relation = Person.group(:parent_id).having{salary == max(salary)}
|
770
|
+
relation.first.name.should eq Person.last.name
|
771
|
+
else
|
772
|
+
pending "MySQL & PG don't support this type of group & having clauses, don't use it."
|
773
|
+
end
|
612
774
|
end
|
613
775
|
|
614
776
|
it 'allows a condition on a function via block' do
|
@@ -627,13 +789,13 @@ module Squeel
|
|
627
789
|
|
628
790
|
it 'builds options with a block' do
|
629
791
|
block = Person.order{name}
|
630
|
-
block.to_sql.should match /ORDER BY
|
792
|
+
block.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}name#{Q}/
|
631
793
|
end
|
632
794
|
|
633
795
|
it 'allows AR 4.0-style hash options' do
|
634
796
|
if activerecord_version_at_least '4.0.0'
|
635
797
|
block = Person.order(:name => :desc)
|
636
|
-
block.to_sql.should match /ORDER BY
|
798
|
+
block.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}name#{Q} DESC/
|
637
799
|
else
|
638
800
|
pending 'Not required in AR versions < 4.0.0'
|
639
801
|
end
|
@@ -641,7 +803,7 @@ module Squeel
|
|
641
803
|
|
642
804
|
it 'allows ordering by an attributes of a joined table' do
|
643
805
|
relation = Article.joins(:person).order { person.id.asc }
|
644
|
-
relation.to_sql.should match /ORDER BY
|
806
|
+
relation.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}id#{Q} ASC/
|
645
807
|
end
|
646
808
|
|
647
809
|
end
|
@@ -654,7 +816,7 @@ module Squeel
|
|
654
816
|
it 'builds options with a block' do
|
655
817
|
block = @standard.reorder{id}
|
656
818
|
block.to_sql.should_not eq @standard.to_sql
|
657
|
-
block.to_sql.should match /ORDER BY
|
819
|
+
block.to_sql.should match /ORDER BY #{Q}people#{Q}.#{Q}id#{Q}/
|
658
820
|
end
|
659
821
|
|
660
822
|
it 'drops order by clause when passed nil' do
|
@@ -673,7 +835,7 @@ module Squeel
|
|
673
835
|
|
674
836
|
describe '#from' do
|
675
837
|
it 'creates froms with a block' do
|
676
|
-
expected = /SELECT
|
838
|
+
expected = /SELECT #{Q}sub#{Q}.#{Q}name#{Q} AS aliased_name FROM \(SELECT #{Q}people#{Q}.#{Q}name#{Q} FROM #{Q}people#{Q}\s*\) sub/
|
677
839
|
block = Person.from{Person.select{name}.as('sub')}.
|
678
840
|
select{sub.name.as('aliased_name')}
|
679
841
|
sql = block.to_sql
|
@@ -681,7 +843,7 @@ module Squeel
|
|
681
843
|
end
|
682
844
|
|
683
845
|
it 'creates froms from literals' do
|
684
|
-
expected = /SELECT
|
846
|
+
expected = /SELECT #{Q}people#{Q}.* FROM sub/
|
685
847
|
relation = Person.from('sub')
|
686
848
|
sql = relation.to_sql
|
687
849
|
sql.should match expected
|
@@ -689,7 +851,7 @@ module Squeel
|
|
689
851
|
|
690
852
|
it 'creates froms from relations' do
|
691
853
|
if activerecord_version_at_least '4.0.0'
|
692
|
-
expected = "SELECT
|
854
|
+
expected = "SELECT #{Q}people#{Q}.* FROM (SELECT #{Q}people#{Q}.* FROM #{Q}people#{Q}) alias"
|
693
855
|
relation = Person.from(Person.all, 'alias')
|
694
856
|
sql = relation.to_sql
|
695
857
|
sql.should == expected
|
@@ -697,6 +859,17 @@ module Squeel
|
|
697
859
|
pending 'Unsupported before ActiveRecord 4.0'
|
698
860
|
end
|
699
861
|
end
|
862
|
+
|
863
|
+
it 'binds params from CollectionProxy subquery' do
|
864
|
+
if activerecord_version_at_least('3.1.0')
|
865
|
+
first_article = Article.first
|
866
|
+
expected_tags = Tag.where(id: [1,2,3]).order{name}.to_a
|
867
|
+
|
868
|
+
expected_tags.should == Tag.from{first_article.tags.as(Tag.table_name)}.order{tags.name}.to_a
|
869
|
+
else
|
870
|
+
pending "ActiveRecord 3.0.x doesn't support CollectionProxy chain."
|
871
|
+
end
|
872
|
+
end
|
700
873
|
end
|
701
874
|
|
702
875
|
describe '#build_where' do
|
@@ -718,7 +891,12 @@ module Squeel
|
|
718
891
|
|
719
892
|
it 'adds hash where values without converting to Arel predicates' do
|
720
893
|
wheres = Person.where({:name => 'bob'}).where_values
|
721
|
-
|
894
|
+
if activerecord_version_at_least('4.0.0')
|
895
|
+
wheres.flatten.should have(1).equality
|
896
|
+
wheres.flatten.last.should be_kind_of(Arel::Nodes::Equality)
|
897
|
+
else
|
898
|
+
wheres.should eq [{:name => 'bob'}]
|
899
|
+
end
|
722
900
|
end
|
723
901
|
|
724
902
|
end
|
@@ -729,8 +907,12 @@ module Squeel
|
|
729
907
|
relation = Person.includes(:comments, :articles).
|
730
908
|
where(:comments => {:body => 'First post!'}).
|
731
909
|
where(:articles => {:title => 'Hello, world!'})
|
732
|
-
|
733
|
-
|
910
|
+
if activerecord_version_at_least('4.1.0')
|
911
|
+
relation.debug_sql.should_not eq relation.arel.to_sql
|
912
|
+
else
|
913
|
+
relation.debug_sql.should_not eq relation.to_sql
|
914
|
+
end
|
915
|
+
relation.debug_sql.should match /SELECT #{Q}people#{Q}.#{Q}id#{Q} AS t0_r0/
|
734
916
|
end
|
735
917
|
|
736
918
|
end
|
@@ -802,22 +984,27 @@ module Squeel
|
|
802
984
|
it 'merges relations with the same base' do
|
803
985
|
relation = Person.where{name == 'bob'}.merge(Person.where{salary == 100000})
|
804
986
|
sql = relation.to_sql
|
805
|
-
sql.should match
|
806
|
-
sql.should match
|
987
|
+
sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
|
988
|
+
sql.should match /#{Q}people#{Q}.#{Q}salary#{Q} = 100000/
|
807
989
|
end
|
808
990
|
|
809
991
|
it 'merges relations with a different base' do
|
810
992
|
relation = Person.where{name == 'bob'}.joins(:articles).merge(Article.where{title == 'Hello world!'})
|
811
993
|
sql = relation.to_sql
|
812
|
-
sql.should match /INNER JOIN
|
813
|
-
sql.should match
|
814
|
-
sql.should match
|
994
|
+
sql.should match /INNER JOIN #{Q}articles#{Q} ON #{Q}articles#{Q}.#{Q}person_id#{Q} = #{Q}people#{Q}.#{Q}id#{Q}/
|
995
|
+
sql.should match /#{Q}people#{Q}.#{Q}name#{Q} = 'bob'/
|
996
|
+
sql.should match /#{Q}articles#{Q}.#{Q}title#{Q} = 'Hello world!'/
|
815
997
|
end
|
816
998
|
|
817
999
|
it 'does not break hm:t with conditions' do
|
818
1000
|
relation = Person.first.condition_article_comments
|
819
|
-
sql =
|
820
|
-
|
1001
|
+
sql =
|
1002
|
+
if activerecord_version_at_least('4.1.0')
|
1003
|
+
relation.to_sql
|
1004
|
+
else
|
1005
|
+
relation.scoped.to_sql
|
1006
|
+
end
|
1007
|
+
sql.should match /#{Q}articles#{Q}.#{Q}title#{Q} = 'Condition'/
|
821
1008
|
end
|
822
1009
|
|
823
1010
|
it 'uses the last condition in the case of a conflicting where' do
|
@@ -831,7 +1018,13 @@ module Squeel
|
|
831
1018
|
|
832
1019
|
it 'uses the given equality condition in the case of a conflicting where from a default scope' do
|
833
1020
|
if activerecord_version_at_least '3.1'
|
834
|
-
relation =
|
1021
|
+
relation =
|
1022
|
+
if activerecord_version_at_least('4.1.0')
|
1023
|
+
PersonNamedBill.rewhere(name: 'Ernie')
|
1024
|
+
# Or PersonNamedBill.unscope(where: :name).where { name == 'Ernie' }
|
1025
|
+
else
|
1026
|
+
PersonNamedBill.where{name == 'Ernie'}
|
1027
|
+
end
|
835
1028
|
sql = relation.to_sql
|
836
1029
|
sql.should_not match /Bill/
|
837
1030
|
sql.should match /Ernie/
|
@@ -844,53 +1037,82 @@ module Squeel
|
|
844
1037
|
relation = Person.with_article_title('hi').
|
845
1038
|
with_article_condition_title('yo')
|
846
1039
|
sql = relation.to_sql
|
847
|
-
sql.should match
|
848
|
-
sql.should match
|
1040
|
+
sql.should match /#{Q}articles#{Q}.#{Q}title#{Q} = 'hi'/
|
1041
|
+
sql.should match /#{Q}articles_with_conditions_people#{Q}.#{Q}title#{Q} = 'yo'/
|
849
1042
|
end
|
850
1043
|
|
851
1044
|
it "doesn't ruin everything when a scope returns nil" do
|
852
1045
|
relation = Person.nil_scope
|
853
|
-
|
1046
|
+
if activerecord_version_at_least('4.1.0')
|
1047
|
+
relation.should eq Person.all
|
1048
|
+
else
|
1049
|
+
relation.should eq Person.scoped
|
1050
|
+
end
|
854
1051
|
end
|
855
1052
|
|
856
1053
|
it "doesn't ruin everything when a group exists" do
|
857
|
-
relation = Person.scoped.merge(Person.group{name})
|
858
1054
|
count_hash = {}
|
859
|
-
|
860
|
-
|
1055
|
+
if activerecord_version_at_least('4.1.0')
|
1056
|
+
relation = Person.all.merge(Person.group{name})
|
1057
|
+
expect { count_hash = relation.count }.should_not raise_error
|
1058
|
+
count_hash.size.should eq Person.all.size
|
1059
|
+
else
|
1060
|
+
relation = Person.scoped.merge(Person.group{name})
|
1061
|
+
expect { count_hash = relation.count }.should_not raise_error
|
1062
|
+
count_hash.size.should eq Person.scoped.size
|
1063
|
+
end
|
1064
|
+
|
861
1065
|
count_hash.values.all? {|v| v == 1}.should be_true
|
862
1066
|
count_hash.keys.should =~ Person.select{name}.map(&:name)
|
863
1067
|
end
|
864
1068
|
|
865
1069
|
it "doesn't merge the default scope more than once" do
|
866
|
-
relation =
|
1070
|
+
relation =
|
1071
|
+
if activerecord_version_at_least('4.1.0')
|
1072
|
+
PersonNamedBill.all.highly_compensated.ending_with_ill
|
1073
|
+
else
|
1074
|
+
PersonNamedBill.scoped.highly_compensated.ending_with_ill
|
1075
|
+
end
|
867
1076
|
sql = relation.to_sql
|
868
|
-
sql.scan(
|
869
|
-
sql.scan(
|
870
|
-
sql.scan(
|
871
|
-
sql.scan(
|
1077
|
+
sql.scan(/#{Q}people#{Q}.#{Q}name#{Q} = 'Bill'/).should have(1).item
|
1078
|
+
sql.scan(/#{Q}people#{Q}.#{Q}name#{Q} [I]*LIKE '%ill'/).should have(1).item
|
1079
|
+
sql.scan(/#{Q}people#{Q}.#{Q}salary#{Q} > 200000/).should have(1).item
|
1080
|
+
sql.scan(/#{Q}people#{Q}.#{Q}id#{Q}/).should have(1).item
|
872
1081
|
end
|
873
1082
|
|
874
1083
|
it "doesn't hijack the table name when merging a relation with different base and default_scope" do
|
875
|
-
relation =
|
1084
|
+
relation =
|
1085
|
+
if activerecord_version_at_least('4.1.0')
|
1086
|
+
Article.joins(:person).merge(PersonNamedBill.all)
|
1087
|
+
else
|
1088
|
+
Article.joins(:person).merge(PersonNamedBill.scoped)
|
1089
|
+
end
|
876
1090
|
sql = relation.to_sql
|
877
|
-
sql.scan(
|
1091
|
+
sql.scan(/#{Q}people#{Q}.#{Q}name#{Q} = 'Bill'/).should have(1).item
|
878
1092
|
end
|
879
1093
|
|
880
1094
|
it 'merges scopes that contain functions' do
|
881
|
-
relation =
|
1095
|
+
relation =
|
1096
|
+
if activerecord_version_at_least('4.1.0')
|
1097
|
+
PersonNamedBill.all.with_salary_equal_to(100)
|
1098
|
+
else
|
1099
|
+
PersonNamedBill.scoped.with_salary_equal_to(100)
|
1100
|
+
end
|
882
1101
|
sql = relation.to_sql
|
883
|
-
sql.should match /abs\(
|
1102
|
+
sql.should match /abs\(#{Q}people#{Q}.#{Q}salary#{Q}\) = 100/
|
884
1103
|
end
|
885
1104
|
|
886
1105
|
it 'uses last equality when merging two scopes with identical function equalities' do
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
1106
|
+
if activerecord_version_at_least('4.1.0')
|
1107
|
+
pending "Named Functions can't be unscoped"
|
1108
|
+
else
|
1109
|
+
relation = PersonNamedBill.scoped.with_salary_equal_to(100).
|
1110
|
+
with_salary_equal_to(200)
|
1111
|
+
sql = relation.to_sql
|
1112
|
+
sql.should_not match /abs\(#{Q}people#{Q}.#{Q}salary#{Q}\) = 100/
|
1113
|
+
sql.should match /abs\(#{Q}people#{Q}.#{Q}salary#{Q}\) = 200/
|
1114
|
+
end
|
892
1115
|
end
|
893
|
-
|
894
1116
|
end
|
895
1117
|
|
896
1118
|
describe '#to_a' do
|
@@ -908,14 +1130,18 @@ module Squeel
|
|
908
1130
|
relation = UnidentifiedObject.where{person_id < 120}.includes(:person)
|
909
1131
|
queries = queries_for do
|
910
1132
|
vals = relation.to_a
|
911
|
-
vals.should have(
|
1133
|
+
vals.should have(20).items
|
912
1134
|
end
|
913
1135
|
|
914
|
-
|
1136
|
+
if activerecord_version_at_least('3.1.0')
|
1137
|
+
queries.should have(2).queries
|
1138
|
+
else
|
1139
|
+
puts 'skips count of queries expectation'
|
1140
|
+
end
|
915
1141
|
|
916
1142
|
matched_ids = queries.last.match(/IN \(([^)]*)/).captures.first
|
917
1143
|
matched_ids = matched_ids.split(/,\s*/).map(&:to_i)
|
918
|
-
matched_ids.should =~ [1,
|
1144
|
+
matched_ids.should =~ [1, 2, 3, 4, 5 ,6 ,7 ,8 ,9 ,10]
|
919
1145
|
end
|
920
1146
|
|
921
1147
|
end
|