squeel 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +8 -0
- data/LICENSE +20 -0
- data/README.rdoc +41 -0
- data/Rakefile +19 -0
- data/lib/core_ext/hash.rb +13 -0
- data/lib/core_ext/symbol.rb +36 -0
- data/lib/squeel.rb +26 -0
- data/lib/squeel/adapters/active_record.rb +6 -0
- data/lib/squeel/adapters/active_record/join_association.rb +90 -0
- data/lib/squeel/adapters/active_record/join_dependency.rb +68 -0
- data/lib/squeel/adapters/active_record/relation.rb +292 -0
- data/lib/squeel/configuration.rb +25 -0
- data/lib/squeel/constants.rb +23 -0
- data/lib/squeel/contexts/join_dependency_context.rb +74 -0
- data/lib/squeel/dsl.rb +31 -0
- data/lib/squeel/nodes.rb +10 -0
- data/lib/squeel/nodes/and.rb +8 -0
- data/lib/squeel/nodes/binary.rb +23 -0
- data/lib/squeel/nodes/function.rb +84 -0
- data/lib/squeel/nodes/join.rb +51 -0
- data/lib/squeel/nodes/key_path.rb +127 -0
- data/lib/squeel/nodes/nary.rb +35 -0
- data/lib/squeel/nodes/not.rb +8 -0
- data/lib/squeel/nodes/operation.rb +23 -0
- data/lib/squeel/nodes/operators.rb +27 -0
- data/lib/squeel/nodes/or.rb +8 -0
- data/lib/squeel/nodes/order.rb +35 -0
- data/lib/squeel/nodes/predicate.rb +49 -0
- data/lib/squeel/nodes/predicate_operators.rb +17 -0
- data/lib/squeel/nodes/stub.rb +113 -0
- data/lib/squeel/nodes/unary.rb +22 -0
- data/lib/squeel/predicate_methods.rb +22 -0
- data/lib/squeel/predicate_methods/function.rb +9 -0
- data/lib/squeel/predicate_methods/predicate.rb +11 -0
- data/lib/squeel/predicate_methods/stub.rb +9 -0
- data/lib/squeel/predicate_methods/symbol.rb +9 -0
- data/lib/squeel/version.rb +3 -0
- data/lib/squeel/visitors.rb +3 -0
- data/lib/squeel/visitors/base.rb +46 -0
- data/lib/squeel/visitors/order_visitor.rb +107 -0
- data/lib/squeel/visitors/predicate_visitor.rb +179 -0
- data/lib/squeel/visitors/select_visitor.rb +103 -0
- data/spec/blueprints/articles.rb +5 -0
- data/spec/blueprints/comments.rb +5 -0
- data/spec/blueprints/notes.rb +3 -0
- data/spec/blueprints/people.rb +4 -0
- data/spec/blueprints/tags.rb +3 -0
- data/spec/console.rb +22 -0
- data/spec/core_ext/symbol_spec.rb +68 -0
- data/spec/helpers/squeel_helper.rb +5 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/squeel/adapters/active_record/join_association_spec.rb +18 -0
- data/spec/squeel/adapters/active_record/join_depdendency_spec.rb +60 -0
- data/spec/squeel/adapters/active_record/relation_spec.rb +437 -0
- data/spec/squeel/contexts/join_dependency_context_spec.rb +43 -0
- data/spec/squeel/dsl_spec.rb +73 -0
- data/spec/squeel/nodes/function_spec.rb +149 -0
- data/spec/squeel/nodes/join_spec.rb +27 -0
- data/spec/squeel/nodes/key_path_spec.rb +92 -0
- data/spec/squeel/nodes/operation_spec.rb +149 -0
- data/spec/squeel/nodes/operators_spec.rb +87 -0
- data/spec/squeel/nodes/order_spec.rb +30 -0
- data/spec/squeel/nodes/predicate_operators_spec.rb +88 -0
- data/spec/squeel/nodes/predicate_spec.rb +92 -0
- data/spec/squeel/nodes/stub_spec.rb +178 -0
- data/spec/squeel/visitors/order_visitor_spec.rb +128 -0
- data/spec/squeel/visitors/predicate_visitor_spec.rb +267 -0
- data/spec/squeel/visitors/select_visitor_spec.rb +115 -0
- data/spec/support/schema.rb +101 -0
- data/squeel.gemspec +44 -0
- metadata +221 -0
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'machinist/active_record'
|
2
|
+
require 'sham'
|
3
|
+
require 'faker'
|
4
|
+
|
5
|
+
Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)].each do |f|
|
6
|
+
require f
|
7
|
+
end
|
8
|
+
|
9
|
+
Sham.define do
|
10
|
+
name { Faker::Name.name }
|
11
|
+
title { Faker::Lorem.sentence }
|
12
|
+
body { Faker::Lorem.paragraph }
|
13
|
+
salary {|index| 30000 + (index * 1000)}
|
14
|
+
tag_name { Faker::Lorem.words(3).join(' ') }
|
15
|
+
note { Faker::Lorem.words(7).join(' ') }
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.before(:suite) { Schema.create }
|
20
|
+
config.before(:all) { Sham.reset(:before_all) }
|
21
|
+
config.before(:each) { Sham.reset(:before_each) }
|
22
|
+
|
23
|
+
config.include SqueelHelper
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'squeel'
|
27
|
+
|
28
|
+
Squeel.configure do |config|
|
29
|
+
config.load_core_extensions :hash, :symbol
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Squeel
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
describe JoinAssociation do
|
5
|
+
before do
|
6
|
+
@jd = ::ActiveRecord::Associations::JoinDependency.new(Note, {}, [])
|
7
|
+
@notable = Note.reflect_on_association(:notable)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'accepts a 4th parameter to set a polymorphic class' do
|
11
|
+
join_association = JoinAssociation.new(@notable, @jd, @jd.join_base, Article)
|
12
|
+
join_association.reflection.klass.should eq Article
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Squeel
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
describe JoinDependency do
|
5
|
+
before do
|
6
|
+
@jd = ::ActiveRecord::Associations::JoinDependency.new(Person, {}, [])
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'joins with symbols' do
|
10
|
+
@jd.send(:build, :articles => :comments)
|
11
|
+
@jd.join_associations.should have(2).associations
|
12
|
+
@jd.join_associations.each do |association|
|
13
|
+
association.join_type.should eq Arel::InnerJoin
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'joins with stubs' do
|
18
|
+
@jd.send(:build, Nodes::Stub.new(:articles) => Nodes::Stub.new(:comments))
|
19
|
+
@jd.join_associations.should have(2).associations
|
20
|
+
@jd.join_associations.each do |association|
|
21
|
+
association.join_type.should eq Arel::InnerJoin
|
22
|
+
end
|
23
|
+
@jd.join_associations[0].table_name.should eq 'articles'
|
24
|
+
@jd.join_associations[1].table_name.should eq 'comments'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'joins with key paths' do
|
28
|
+
@jd.send(:build, dsl{children.children.parent})
|
29
|
+
@jd.join_associations.should have(3).associations
|
30
|
+
@jd.join_associations.each do |association|
|
31
|
+
association.join_type.should eq Arel::InnerJoin
|
32
|
+
end
|
33
|
+
@jd.join_associations[0].aliased_table_name.should eq 'children_people'
|
34
|
+
@jd.join_associations[1].aliased_table_name.should eq 'children_people_2'
|
35
|
+
@jd.join_associations[2].aliased_table_name.should eq 'parents_people'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'joins with key paths as keys' do
|
39
|
+
@jd.send(:build, dsl{{children.parent => parent}})
|
40
|
+
@jd.join_associations.should have(3).associations
|
41
|
+
@jd.join_associations.each do |association|
|
42
|
+
association.join_type.should eq Arel::InnerJoin
|
43
|
+
end
|
44
|
+
@jd.join_associations[0].aliased_table_name.should eq 'children_people'
|
45
|
+
@jd.join_associations[1].aliased_table_name.should eq 'parents_people'
|
46
|
+
@jd.join_associations[2].aliased_table_name.should eq 'parents_people_2'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'joins using outer joins' do
|
50
|
+
@jd.send(:build, :articles.outer => :comments.outer)
|
51
|
+
@jd.join_associations.should have(2).associations
|
52
|
+
@jd.join_associations.each do |association|
|
53
|
+
association.join_type.should eq Arel::OuterJoin
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,437 @@
|
|
1
|
+
module Squeel
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
describe Relation do
|
5
|
+
|
6
|
+
describe '#predicate_visitor' do
|
7
|
+
|
8
|
+
it 'creates a predicate visitor with a JoinDependencyContext for the relation' do
|
9
|
+
relation = Person.joins({
|
10
|
+
:children => {
|
11
|
+
:children => {
|
12
|
+
:parent => :parent
|
13
|
+
}
|
14
|
+
}
|
15
|
+
})
|
16
|
+
|
17
|
+
visitor = relation.predicate_visitor
|
18
|
+
|
19
|
+
visitor.should be_a Visitors::PredicateVisitor
|
20
|
+
table = visitor.contextualize(relation.join_dependency.join_parts.last)
|
21
|
+
table.table_alias.should eq 'parents_people_2'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#order_visitor' do
|
27
|
+
|
28
|
+
it 'creates an order visitor with a JoinDependencyContext for the relation' do
|
29
|
+
relation = Person.joins({
|
30
|
+
:children => {
|
31
|
+
:children => {
|
32
|
+
:parent => :parent
|
33
|
+
}
|
34
|
+
}
|
35
|
+
})
|
36
|
+
|
37
|
+
visitor = relation.order_visitor
|
38
|
+
|
39
|
+
visitor.should be_a Visitors::OrderVisitor
|
40
|
+
table = visitor.contextualize(relation.join_dependency.join_parts.last)
|
41
|
+
table.table_alias.should eq 'parents_people_2'
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#select_visitor' do
|
47
|
+
|
48
|
+
it 'creates a select visitor with a JoinDependencyContext for the relation' do
|
49
|
+
relation = Person.joins({
|
50
|
+
:children => {
|
51
|
+
:children => {
|
52
|
+
:parent => :parent
|
53
|
+
}
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
visitor = relation.select_visitor
|
58
|
+
|
59
|
+
visitor.should be_a Visitors::SelectVisitor
|
60
|
+
table = visitor.contextualize(relation.join_dependency.join_parts.last)
|
61
|
+
table.table_alias.should eq 'parents_people_2'
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#build_arel' do
|
67
|
+
|
68
|
+
it 'joins associations' do
|
69
|
+
relation = Person.joins({
|
70
|
+
:children => {
|
71
|
+
:children => {
|
72
|
+
:parent => :parent
|
73
|
+
}
|
74
|
+
}
|
75
|
+
})
|
76
|
+
|
77
|
+
arel = relation.build_arel
|
78
|
+
|
79
|
+
relation.join_dependency.join_associations.should have(4).items
|
80
|
+
arel.to_sql.should match /INNER JOIN "people" "parents_people_2" ON "parents_people_2"."id" = "parents_people"."parent_id"/
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'joins associations with custom join types' do
|
84
|
+
relation = Person.joins({
|
85
|
+
:children.outer => {
|
86
|
+
:children => {
|
87
|
+
:parent => :parent.outer
|
88
|
+
}
|
89
|
+
}
|
90
|
+
})
|
91
|
+
|
92
|
+
arel = relation.build_arel
|
93
|
+
|
94
|
+
relation.join_dependency.join_associations.should have(4).items
|
95
|
+
arel.to_sql.should match /LEFT OUTER JOIN "people" "children_people"/
|
96
|
+
arel.to_sql.should match /LEFT OUTER JOIN "people" "parents_people_2" ON "parents_people_2"."id" = "parents_people"."parent_id"/
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'only joins an association once, even if two overlapping joins_values hashes are given' do
|
100
|
+
relation = Person.joins({
|
101
|
+
:children => {
|
102
|
+
:children => {
|
103
|
+
:parent => :parent
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}).joins({
|
107
|
+
:children => {
|
108
|
+
:children => {
|
109
|
+
:children => :parent
|
110
|
+
}
|
111
|
+
}
|
112
|
+
})
|
113
|
+
|
114
|
+
arel = relation.build_arel
|
115
|
+
relation.join_dependency.join_associations.should have(6).items
|
116
|
+
arel.to_sql.should match /INNER JOIN "people" "parents_people_3" ON "parents_people_3"."id" = "children_people_3"."parent_id"/
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'visits wheres with a PredicateVisitor, converting them to ARel nodes' do
|
120
|
+
relation = Person.where(:name.matches => '%bob%')
|
121
|
+
arel = relation.build_arel
|
122
|
+
arel.to_sql.should match /"people"."name" LIKE '%bob%'/
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'maps wheres inside a hash to their appropriate association table' do
|
126
|
+
relation = Person.joins({
|
127
|
+
:children => {
|
128
|
+
:children => {
|
129
|
+
:parent => :parent
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}).where({
|
133
|
+
:children => {
|
134
|
+
:children => {
|
135
|
+
:parent => {
|
136
|
+
:parent => { :name => 'bob' }
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
})
|
141
|
+
|
142
|
+
arel = relation.build_arel
|
143
|
+
|
144
|
+
arel.to_sql.should match /"parents_people_2"."name" = 'bob'/
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'combines multiple conditions of the same type against the same column with AND' do
|
148
|
+
relation = Person.where(:name.matches => '%bob%')
|
149
|
+
relation = relation.where(:name.matches => '%joe%')
|
150
|
+
arel = relation.build_arel
|
151
|
+
arel.to_sql.should match /"people"."name" LIKE '%bob%' AND "people"."name" LIKE '%joe%'/
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'handles ORs between predicates' do
|
155
|
+
relation = Person.joins{articles}.where{(name =~ 'Joe%') | (articles.title =~ 'Hello%')}
|
156
|
+
arel = relation.build_arel
|
157
|
+
arel.to_sql.should match /OR/
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'maintains groupings as given' do
|
161
|
+
relation = Person.where(dsl{(name == 'Ernie') | ((name =~ 'Bob%') & (name =~ '%by'))})
|
162
|
+
arel = relation.build_arel
|
163
|
+
arel.to_sql.should match /"people"."name" = 'Ernie' OR \("people"."name" LIKE 'Bob%' AND "people"."name" LIKE '%by'\)/
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'maps havings inside a hash to their appropriate association table' do
|
167
|
+
relation = Person.joins({
|
168
|
+
:children => {
|
169
|
+
:children => {
|
170
|
+
:parent => :parent
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}).having({
|
174
|
+
:children => {
|
175
|
+
:children => {
|
176
|
+
:parent => {
|
177
|
+
:parent => {:name => 'joe'}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
})
|
182
|
+
|
183
|
+
arel = relation.build_arel
|
184
|
+
|
185
|
+
arel.to_sql.should match /HAVING "parents_people_2"."name" = 'joe'/
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'maps orders inside a hash to their appropriate association table' do
|
189
|
+
relation = Person.joins({
|
190
|
+
:children => {
|
191
|
+
:children => {
|
192
|
+
:parent => :parent
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}).order({
|
196
|
+
:children => {
|
197
|
+
:children => {
|
198
|
+
:parent => {
|
199
|
+
:parent => :id.asc
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
})
|
204
|
+
|
205
|
+
arel = relation.build_arel
|
206
|
+
|
207
|
+
arel.to_sql.should match /ORDER BY "parents_people_2"."id" ASC/
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
describe '#select' do
|
213
|
+
|
214
|
+
it 'accepts options from a block' do
|
215
|
+
standard = Person.select(:id)
|
216
|
+
block = Person.select {id}
|
217
|
+
block.to_sql.should eq standard.to_sql
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'falls back to Array#select behavior with a block that has an arity' do
|
221
|
+
people = Person.select{|p| p.name =~ /John/}
|
222
|
+
people.should have(1).person
|
223
|
+
people.first.name.should eq 'Miss Cameron Johnson'
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'behaves as normal with standard parameters' do
|
227
|
+
people = Person.select(:id)
|
228
|
+
people.should have(332).people
|
229
|
+
expect { people.first.name }.to raise_error ActiveModel::MissingAttributeError
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'allows a function in the select values via Symbol#func' do
|
233
|
+
relation = Person.select(:max.func(:id).as('max_id'))
|
234
|
+
relation.first.max_id.should eq 332
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'allows a function in the select values via block' do
|
238
|
+
relation = Person.select{max(id).as(max_id)}
|
239
|
+
relation.first.max_id.should eq 332
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'allows an operation in the select values via block' do
|
243
|
+
relation = Person.select{[id, (id + 1).as('id_plus_one')]}.where('id_plus_one = 2')
|
244
|
+
relation.first.id.should eq 1
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'allows custom operators in the select values via block' do
|
248
|
+
relation = Person.select{name.op('||', '-diddly').as(flanderized_name)}
|
249
|
+
relation.first.flanderized_name.should eq 'Aric Smith-diddly'
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
describe '#where' do
|
255
|
+
|
256
|
+
it 'builds options with a block' do
|
257
|
+
standard = Person.where(:name => 'bob')
|
258
|
+
block = Person.where{{name => 'bob'}}
|
259
|
+
block.to_sql.should eq standard.to_sql
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'builds compound conditions with a block' do
|
263
|
+
block = Person.where{(name == 'bob') & (salary == 100000)}
|
264
|
+
block.to_sql.should match /"people"."name" = 'bob'/
|
265
|
+
block.to_sql.should match /AND/
|
266
|
+
block.to_sql.should match /"people"."salary" = 100000/
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'allows mixing hash and operator syntax inside a block' do
|
270
|
+
block = Person.joins(:comments).
|
271
|
+
where{(name == 'bob') & {comments => (body == 'First post!')}}
|
272
|
+
block.to_sql.should match /"people"."name" = 'bob'/
|
273
|
+
block.to_sql.should match /AND/
|
274
|
+
block.to_sql.should match /"comments"."body" = 'First post!'/
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'allows a condition on a function via block' do
|
278
|
+
relation = Person.where{coalesce(nil,id) == 5}
|
279
|
+
relation.first.id.should eq 5
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'allows a condition on an operation via block' do
|
283
|
+
relation = Person.where{(id + 1) == 2}
|
284
|
+
relation.first.id.should eq 1
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
describe '#joins' do
|
290
|
+
|
291
|
+
it 'builds options with a block' do
|
292
|
+
standard = Person.joins(:children => :children)
|
293
|
+
block = Person.joins{{children => children}}
|
294
|
+
block.to_sql.should eq standard.to_sql
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'accepts multiple top-level associations with a block' do
|
298
|
+
standard = Person.joins(:children, :articles, :comments)
|
299
|
+
block = Person.joins{[children, articles, comments]}
|
300
|
+
block.to_sql.should eq standard.to_sql
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'joins polymorphic belongs_to associations' do
|
304
|
+
relation = Note.joins{notable(Article)}
|
305
|
+
relation.to_sql.should match /"notes"."notable_type" = 'Article'/
|
306
|
+
end
|
307
|
+
|
308
|
+
it "only joins once, even if two join types are used" do
|
309
|
+
relation = Person.joins(:articles.inner, :articles.outer)
|
310
|
+
relation.to_sql.scan("JOIN").size.should eq 1
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
314
|
+
|
315
|
+
describe '#having' do
|
316
|
+
|
317
|
+
it 'builds options with a block' do
|
318
|
+
standard = Person.having(:name => 'bob')
|
319
|
+
block = Person.having{{name => 'bob'}}
|
320
|
+
block.to_sql.should eq standard.to_sql
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'allows complex conditions on aggregate columns' do
|
324
|
+
relation = Person.group(:parent_id).having{salary == max(salary)}
|
325
|
+
relation.first.name.should eq 'Gladyce Kulas'
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'allows a condition on a function via block' do
|
329
|
+
relation = Person.group(:id).having{coalesce(nil,id) == 5}
|
330
|
+
relation.first.id.should eq 5
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'allows a condition on an operation via block' do
|
334
|
+
relation = Person.group(:id).having{(id + 1) == 2}
|
335
|
+
relation.first.id.should eq 1
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
describe '#order' do
|
341
|
+
|
342
|
+
it 'builds options with a block' do
|
343
|
+
standard = Person.order(:name)
|
344
|
+
block = Person.order{name}
|
345
|
+
block.to_sql.should eq standard.to_sql
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
describe '#build_where' do
|
351
|
+
|
352
|
+
it 'sanitizes SQL as usual with strings' do
|
353
|
+
wheres = Person.where('name like ?', '%bob%').where_values
|
354
|
+
wheres.should eq ["name like '%bob%'"]
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'sanitizes SQL as usual with strings and hash substitution' do
|
358
|
+
wheres = Person.where('name like :name', :name => '%bob%').where_values
|
359
|
+
wheres.should eq ["name like '%bob%'"]
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'sanitizes SQL as usual with arrays' do
|
363
|
+
wheres = Person.where(['name like ?', '%bob%']).where_values
|
364
|
+
wheres.should eq ["name like '%bob%'"]
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'adds hash where values without converting to ARel predicates' do
|
368
|
+
wheres = Person.where({:name => 'bob'}).where_values
|
369
|
+
wheres.should eq [{:name => 'bob'}]
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
describe '#debug_sql' do
|
375
|
+
|
376
|
+
it 'returns the query that would be run against the database, even if eager loading' do
|
377
|
+
relation = Person.includes(:comments, :articles).
|
378
|
+
where(:comments => {:body => 'First post!'}).
|
379
|
+
where(:articles => {:title => 'Hello, world!'})
|
380
|
+
relation.debug_sql.should_not eq relation.to_sql
|
381
|
+
relation.debug_sql.should match /SELECT "people"."id" AS t0_r0/
|
382
|
+
end
|
383
|
+
|
384
|
+
end
|
385
|
+
|
386
|
+
describe '#where_values_hash' do
|
387
|
+
|
388
|
+
it 'creates new records with equality predicates from wheres' do
|
389
|
+
@person = Person.where(:name => 'bob', :parent_id => 3).new
|
390
|
+
@person.parent_id.should eq 3
|
391
|
+
@person.name.should eq 'bob'
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'uses the last supplied equality predicate in where_values when creating new records' do
|
395
|
+
@person = Person.where(:name => 'bob', :parent_id => 3).where(:name => 'joe').new
|
396
|
+
@person.parent_id.should eq 3
|
397
|
+
@person.name.should eq 'joe'
|
398
|
+
end
|
399
|
+
|
400
|
+
end
|
401
|
+
|
402
|
+
describe '#merge' do
|
403
|
+
|
404
|
+
it 'merges relations with the same base' do
|
405
|
+
relation = Person.where{name == 'bob'}.merge(Person.where{salary == 100000})
|
406
|
+
sql = relation.to_sql
|
407
|
+
sql.should match /"people"."name" = 'bob'/
|
408
|
+
sql.should match /"people"."salary" = 100000/
|
409
|
+
end
|
410
|
+
|
411
|
+
it 'merges relations with a different base' do
|
412
|
+
relation = Person.where{name == 'bob'}.merge(Article.where{title == 'Hello world!'})
|
413
|
+
sql = relation.to_sql
|
414
|
+
sql.should match /INNER JOIN "articles" ON "articles"."person_id" = "people"."id"/
|
415
|
+
sql.should match /"people"."name" = 'bob'/
|
416
|
+
sql.should match /"articles"."title" = 'Hello world!'/
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
420
|
+
|
421
|
+
describe '#to_a' do
|
422
|
+
|
423
|
+
it 'eager-loads associations with dependent conditions' do
|
424
|
+
relation = Person.includes(:comments, :articles).
|
425
|
+
where{{comments => {body => 'First post!'}}}
|
426
|
+
relation.size.should be 1
|
427
|
+
person = relation.first
|
428
|
+
person.name.should eq 'Gladyce Kulas'
|
429
|
+
person.comments.loaded?.should be true
|
430
|
+
end
|
431
|
+
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|