squeel 1.0.18 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +2 -7
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -2
- data/README.md +223 -147
- data/lib/generators/templates/squeel.rb +1 -1
- data/lib/squeel/adapters/active_record.rb +1 -1
- data/lib/squeel/adapters/active_record/4.0/compat.rb +17 -0
- data/lib/squeel/adapters/active_record/4.0/context.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/preloader_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +126 -0
- data/lib/squeel/adapters/active_record/base_extensions.rb +1 -1
- data/lib/squeel/adapters/active_record/context.rb +10 -10
- data/lib/squeel/adapters/active_record/relation_extensions.rb +24 -16
- data/lib/squeel/configuration.rb +1 -0
- data/lib/squeel/constants.rb +2 -2
- data/lib/squeel/context.rb +2 -2
- data/lib/squeel/dsl.rb +1 -1
- data/lib/squeel/nodes.rb +2 -0
- data/lib/squeel/nodes/binary.rb +1 -1
- data/lib/squeel/nodes/function.rb +5 -5
- data/lib/squeel/nodes/join.rb +2 -2
- data/lib/squeel/nodes/key_path.rb +10 -5
- data/lib/squeel/nodes/literal.rb +1 -1
- data/lib/squeel/nodes/nary.rb +5 -7
- data/lib/squeel/nodes/node.rb +6 -0
- data/lib/squeel/nodes/operation.rb +1 -1
- data/lib/squeel/nodes/order.rb +1 -1
- data/lib/squeel/nodes/predicate.rb +5 -5
- data/lib/squeel/nodes/predicate_methods.rb +11 -2
- data/lib/squeel/nodes/sifter.rb +2 -2
- data/lib/squeel/nodes/stub.rb +2 -2
- data/lib/squeel/nodes/unary.rb +1 -1
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors/predicate_visitation.rb +6 -6
- data/lib/squeel/visitors/predicate_visitor.rb +1 -1
- data/lib/squeel/visitors/visitor.rb +20 -20
- data/spec/spec_helper.rb +6 -4
- data/spec/squeel/adapters/active_record/base_extensions_spec.rb +6 -6
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +55 -24
- data/spec/squeel/core_ext/symbol_spec.rb +2 -2
- data/spec/squeel/nodes/key_path_spec.rb +3 -3
- data/spec/squeel/nodes/predicate_operators_spec.rb +4 -4
- data/spec/squeel/visitors/predicate_visitor_spec.rb +11 -11
- data/spec/squeel/visitors/visitor_spec.rb +9 -9
- data/spec/support/models.rb +25 -7
- data/spec/support/schema.rb +1 -1
- data/squeel.gemspec +4 -4
- metadata +19 -12
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
squeel
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.3
|
data/.travis.yml
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
rvm:
|
2
|
-
- 1.8.7
|
3
2
|
- 1.9.3
|
4
|
-
# TODO: Re-enable when updates to Rubinius on
|
5
|
-
# Travis stop randomly breaking specs that
|
6
|
-
# pass on MRI. :(
|
7
|
-
# - rbx-18mode
|
8
|
-
# - rbx-19mode
|
9
3
|
|
10
4
|
env:
|
11
|
-
- RAILS=
|
5
|
+
- RAILS=4-0-stable AREL=master
|
6
|
+
- RAILS=3-2-stable AREL=3-0-stable
|
12
7
|
- RAILS=3-1-stable AREL=2-2-stable
|
13
8
|
- RAILS=3-0-stable AREL=2-0-stable
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 1.1.0 (2013-07-14)
|
2
|
+
|
3
|
+
* Support for Active Record 4.0.0!
|
4
|
+
* Deprecated core extensions. In Squeel 2.0, the DSL will be the way to
|
5
|
+
construct queries, and Symbol/Hash extensions will go away.
|
6
|
+
* Prefix generated sifter methods with `sifter_` so as not to interfere with
|
7
|
+
similarly-named scopes.
|
8
|
+
* No longer mutate And nodes when using `&` and `-` on the node
|
9
|
+
|
1
10
|
## 1.0.18 (2013-03-07)
|
2
11
|
|
3
12
|
* Stop treating nils as quotable. Fixes issue #221.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
# Squeel [](http://travis-ci.org/ernie/squeel) [](http://coderwall.com/ernie)
|
2
2
|
|
3
3
|
Squeel lets you write your Active Record queries with fewer strings, and more Ruby,
|
4
|
-
by making the
|
4
|
+
by making the Arel awesomeness that lies beneath Active Record more accessible.
|
5
5
|
|
6
6
|
Squeel lets you rewrite...
|
7
7
|
|
8
|
-
|
8
|
+
```ruby
|
9
|
+
Article.where ['created_at >= ?', 2.weeks.ago]
|
10
|
+
```
|
9
11
|
|
10
12
|
...as...
|
11
13
|
|
12
|
-
|
14
|
+
```ruby
|
15
|
+
Article.where{created_at >= 2.weeks.ago}
|
16
|
+
```
|
13
17
|
|
14
18
|
This is a _good thing_. If you don't agree, Squeel might not be for you. The above is
|
15
19
|
just a simple example -- Squeel's capable of a whole lot more. Keep reading.
|
@@ -18,8 +22,10 @@ just a simple example -- Squeel's capable of a whole lot more. Keep reading.
|
|
18
22
|
|
19
23
|
In your Gemfile:
|
20
24
|
|
21
|
-
|
22
|
-
|
25
|
+
```ruby
|
26
|
+
gem "squeel" # Last officially released gem
|
27
|
+
# gem "squeel", :git => "git://github.com/ernie/squeel.git" # Track git repo
|
28
|
+
```
|
23
29
|
|
24
30
|
Then bundle as usual.
|
25
31
|
|
@@ -27,7 +33,9 @@ If you'd like to customize Squeel's functionality by enabling core
|
|
27
33
|
extensions for hashes or symbols, or aliasing some predicates, you can
|
28
34
|
create a sample initializer with:
|
29
35
|
|
30
|
-
|
36
|
+
```sh
|
37
|
+
$ rails g squeel:initializer
|
38
|
+
```
|
31
39
|
|
32
40
|
## The Squeel Query DSL
|
33
41
|
|
@@ -63,8 +71,10 @@ Stubs are, for most intents and purposes, just like Symbols in a normal call to
|
|
63
71
|
`Relation#where` (note the need for doubling up on the curly braces here, the first ones
|
64
72
|
start the block, the second are the hash braces):
|
65
73
|
|
66
|
-
|
67
|
-
|
74
|
+
```ruby
|
75
|
+
Person.where{{name => 'Ernie'}}
|
76
|
+
# => SELECT "people".* FROM "people" WHERE "people"."name" = 'Ernie'
|
77
|
+
```
|
68
78
|
|
69
79
|
You normally wouldn't bother using the DSL in this case, as a simple hash would
|
70
80
|
suffice. However, stubs serve as a building block for keypaths, and keypaths are
|
@@ -76,29 +86,35 @@ A Squeel keypath is essentially a more concise and readable alternative to a
|
|
76
86
|
deeply nested hash. For instance, in standard Active Record, you might join several
|
77
87
|
associations like this to perform a query:
|
78
88
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
89
|
+
```ruby
|
90
|
+
Person.joins(:articles => {:comments => :person})
|
91
|
+
# => SELECT "people".* FROM "people"
|
92
|
+
# INNER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
93
|
+
# INNER JOIN "comments" ON "comments"."article_id" = "articles"."id"
|
94
|
+
# INNER JOIN "people" "people_comments" ON "people_comments"."id" = "comments"."person_id"
|
95
|
+
```
|
84
96
|
|
85
97
|
With a keypath, this would look like:
|
86
98
|
|
87
|
-
|
99
|
+
```ruby
|
100
|
+
Person.joins{articles.comments.person}
|
101
|
+
```
|
88
102
|
|
89
103
|
A keypath can exist in the context of a hash, and is normally interpreted relative to
|
90
104
|
the current level of nesting. It can be forced into an "absolute" path by anchoring it with
|
91
105
|
a ~, like:
|
92
106
|
|
93
|
-
|
107
|
+
```ruby
|
108
|
+
~articles.comments.person
|
109
|
+
```
|
94
110
|
|
95
111
|
This isn't quite so useful in the typical hash context, but can be very useful when it comes
|
96
112
|
to interpreting functions and the like. We'll cover those later.
|
97
113
|
|
98
114
|
### Predicates
|
99
115
|
|
100
|
-
All of the
|
101
|
-
their method name, an alias, or an an operator, to create
|
116
|
+
All of the Arel "predication" methods can be accessed inside the Squeel DSL, via
|
117
|
+
their method name, an alias, or an an operator, to create Arel predicates, which are
|
102
118
|
used in `WHERE` or `HAVING` clauses.
|
103
119
|
|
104
120
|
<table>
|
@@ -172,34 +188,44 @@ used in `WHERE` or `HAVING` clauses.
|
|
172
188
|
|
173
189
|
Let's say we want to generate this simple query:
|
174
190
|
|
175
|
-
|
191
|
+
```
|
192
|
+
SELECT "people".* FROM people WHERE "people"."name" = 'Joe Blow'
|
193
|
+
```
|
176
194
|
|
177
195
|
All of the following will generate the above SQL:
|
178
196
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
197
|
+
```ruby
|
198
|
+
Person.where(:name => 'Joe Blow')
|
199
|
+
Person.where{{name => 'Joe Blow'}}
|
200
|
+
Person.where{{name.eq => 'Joe Blow'}}
|
201
|
+
Person.where{name.eq 'Joe Blow'}
|
202
|
+
Person.where{name == 'Joe Blow'}
|
203
|
+
```
|
204
|
+
|
185
205
|
Not a very exciting example since equality is handled just fine via the
|
186
206
|
first example in standard Active Record. But consider the following query:
|
187
207
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
208
|
+
```
|
209
|
+
SELECT "people".* FROM people
|
210
|
+
WHERE ("people"."name" LIKE 'Ernie%' AND "people"."salary" < 50000)
|
211
|
+
OR ("people"."name" LIKE 'Joe%' AND "people"."salary" > 100000)
|
212
|
+
```
|
213
|
+
|
192
214
|
To do this with standard Active Record, we'd do something like:
|
193
215
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
216
|
+
```ruby
|
217
|
+
Person.where(
|
218
|
+
'(name LIKE ? AND salary < ?) OR (name LIKE ? AND salary > ?)',
|
219
|
+
'Ernie%', 50000, 'Joe%', 100000
|
220
|
+
)
|
221
|
+
```
|
222
|
+
|
199
223
|
With Squeel:
|
200
224
|
|
201
|
-
|
202
|
-
|
225
|
+
```ruby
|
226
|
+
Person.where{(name =~ 'Ernie%') & (salary < 50000) | (name =~ 'Joe%') & (salary > 100000)}
|
227
|
+
```
|
228
|
+
|
203
229
|
Here, we're using `&` and `|` to generate `AND` and `OR`, respectively.
|
204
230
|
|
205
231
|
There are two obvious but important differences between these two code samples, and
|
@@ -209,7 +235,7 @@ both of them have to do with *context*.
|
|
209
235
|
first be considered, then we must cross-reference the values to be substituted
|
210
236
|
with their placeholders. This carries with it a small but perceptible (and
|
211
237
|
annoying!) context shift during which we stop thinking about the comparison being
|
212
|
-
performed, and instead play "count the arguments", or, in the case of
|
238
|
+
performed, and instead play "count the arguments", or, in the case of
|
213
239
|
named/hash interpolations, "find the word". The Squeel syntax places
|
214
240
|
both sides of each comparison in proximity to one another, allowing us to
|
215
241
|
focus on what our code is doing.
|
@@ -228,12 +254,15 @@ our intentions more clear, on the first read-through. And if we don't like the
|
|
228
254
|
way that the existing predications read, we can create our own aliases in a Squeel
|
229
255
|
configure block:
|
230
256
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
257
|
+
```ruby
|
258
|
+
Squeel.configure do |config|
|
259
|
+
config.alias_predicate :is_less_than, :lt
|
260
|
+
end
|
261
|
+
```
|
262
|
+
```ruby
|
263
|
+
Person.where{salary.is_less_than 50000}.to_sql
|
264
|
+
# => SELECT "people".* FROM "people" WHERE "people"."salary" < 50000
|
265
|
+
```
|
237
266
|
|
238
267
|
And while we're on the topic of helping you make your code more expressive...
|
239
268
|
|
@@ -241,20 +270,26 @@ And while we're on the topic of helping you make your code more expressive...
|
|
241
270
|
|
242
271
|
Let's say you want to check if a Person has a name like one of several possibilities.
|
243
272
|
|
244
|
-
|
245
|
-
|
273
|
+
```ruby
|
274
|
+
names = ['Ernie%', 'Joe%', 'Mary%']
|
275
|
+
Person.where('name LIKE ? OR name LIKE ? OR name LIKE ?', *names)
|
276
|
+
```
|
246
277
|
|
247
278
|
But you're smart, and you know that you might want to check more or less than
|
248
279
|
3 names, so you make your query flexible:
|
249
280
|
|
250
|
-
|
281
|
+
```ruby
|
282
|
+
Person.where((['name LIKE ?'] * names.size).join(' OR '), *names)
|
283
|
+
```
|
251
284
|
|
252
285
|
Yeah... that's readable, all right. How about:
|
253
286
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
287
|
+
```ruby
|
288
|
+
Person.where{name.like_any names}
|
289
|
+
# => SELECT "people".* FROM "people"
|
290
|
+
# WHERE (("people"."name" LIKE 'Ernie%' OR "people"."name" LIKE 'Joe%' OR "people"."name" LIKE 'Mary%'))
|
291
|
+
```
|
292
|
+
|
258
293
|
I'm not sure about you, but I much prefer the latter. In short, you can add `_any` or
|
259
294
|
`_all` to any predicate method, and it would do what you expect, when given an array of
|
260
295
|
possibilities to compare against.
|
@@ -265,57 +300,69 @@ Sifters are like little snippets of conditions that take parameters. Let's say t
|
|
265
300
|
have a model called Article, and you often want to query for articles that contain a
|
266
301
|
string in the title or body. So you write a scope:
|
267
302
|
|
268
|
-
|
269
|
-
|
270
|
-
|
303
|
+
```ruby
|
304
|
+
def self.title_or_body_contains(string)
|
305
|
+
where{title.matches("%#{string}%") | body.matches("%#{string}%")}
|
306
|
+
end
|
307
|
+
```
|
271
308
|
|
272
309
|
But then you want to query for people who wrote an article that matches these conditions,
|
273
310
|
but the scope only works against the model where it was defined. So instead, you write a
|
274
311
|
sifter:
|
275
312
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
313
|
+
```ruby
|
314
|
+
class Article < ActiveRecord::Base
|
315
|
+
sifter :title_or_body_contains do |string|
|
316
|
+
title.matches("%#{string}%") | body.matches("%#{string}%")
|
317
|
+
end
|
318
|
+
end
|
319
|
+
```
|
281
320
|
|
282
321
|
Now you can write...
|
283
322
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
323
|
+
```ruby
|
324
|
+
Article.where{sift :title_or_body_contains, 'awesome'}
|
325
|
+
# => SELECT "articles".* FROM "articles"
|
326
|
+
# WHERE ((
|
327
|
+
# "articles"."title" LIKE '%awesome%'
|
328
|
+
# OR "articles"."body" LIKE '%awesome%'
|
329
|
+
# ))
|
330
|
+
```
|
290
331
|
|
291
332
|
... or ...
|
292
333
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
334
|
+
```ruby
|
335
|
+
Person.joins(:articles).
|
336
|
+
where{
|
337
|
+
{articles => sift(:title_or_body_contains, 'awesome')}
|
338
|
+
}
|
339
|
+
# => SELECT "people".* FROM "people"
|
340
|
+
# INNER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
341
|
+
# WHERE ((
|
342
|
+
# "articles"."title" LIKE '%awesome%'
|
343
|
+
# OR "articles"."body" LIKE '%awesome%'
|
344
|
+
# ))
|
345
|
+
```
|
303
346
|
|
304
347
|
Or, you can just modify your previous scope, changing `where` to `squeel`:
|
305
348
|
|
306
|
-
|
307
|
-
|
308
|
-
|
349
|
+
```ruby
|
350
|
+
def self.title_or_body_contains(string)
|
351
|
+
squeel{title.matches("%#{string}%") | body.matches("%#{string}%")}
|
352
|
+
end
|
353
|
+
```
|
309
354
|
|
310
355
|
### Subqueries
|
311
356
|
|
312
357
|
You can supply an `ActiveRecord::Relation` as a value for a predicate in order to use
|
313
358
|
a subquery. So, for example:
|
314
359
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
360
|
+
```ruby
|
361
|
+
awesome_people = Person.where{awesome == true}
|
362
|
+
Article.where{author_id.in(awesome_people.select{id})}
|
363
|
+
# => SELECT "articles".* FROM "articles"
|
364
|
+
# WHERE "articles"."author_id" IN (SELECT "people"."id" FROM "people" WHERE "people"."awesome" = 't')
|
365
|
+
```
|
319
366
|
|
320
367
|
### Joins
|
321
368
|
|
@@ -323,40 +370,46 @@ Squeel adds a couple of enhancements to joins. First, keypaths can be used as sh
|
|
323
370
|
nested association joins. Second, you can specify join types (inner and outer), and a class
|
324
371
|
in the case of a polymorphic belongs_to relationship.
|
325
372
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
373
|
+
```ruby
|
374
|
+
Person.joins{articles.outer}
|
375
|
+
# => SELECT "people".* FROM "people"
|
376
|
+
# LEFT OUTER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
377
|
+
Note.joins{notable(Person).outer}
|
378
|
+
# => SELECT "notes".* FROM "notes"
|
379
|
+
# LEFT OUTER JOIN "people"
|
380
|
+
# ON "people"."id" = "notes"."notable_id"
|
381
|
+
# AND "notes"."notable_type" = 'Person'
|
382
|
+
```
|
334
383
|
|
335
384
|
These can also be used inside keypaths:
|
336
385
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
386
|
+
```ruby
|
387
|
+
Note.joins{notable(Person).articles}
|
388
|
+
# => SELECT "notes".* FROM "notes"
|
389
|
+
# INNER JOIN "people" ON "people"."id" = "notes"."notable_id"
|
390
|
+
# AND "notes"."notable_type" = 'Person'
|
391
|
+
# INNER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
392
|
+
```
|
393
|
+
|
343
394
|
You can refer to these associations when constructing other parts of your query, and
|
344
395
|
they'll be automatically mapped to the proper table or table alias This is most noticeable
|
345
396
|
when using self-referential associations:
|
346
397
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
398
|
+
```ruby
|
399
|
+
Person.joins{children.parent.children}.
|
400
|
+
where{
|
401
|
+
(children.name.like 'Ernie%') |
|
402
|
+
(children.parent.name.like 'Ernie%') |
|
403
|
+
(children.parent.children.name.like 'Ernie%')
|
404
|
+
}
|
405
|
+
# => SELECT "people".* FROM "people"
|
406
|
+
# INNER JOIN "people" "children_people" ON "children_people"."parent_id" = "people"."id"
|
407
|
+
# INNER JOIN "people" "parents_people" ON "parents_people"."id" = "children_people"."parent_id"
|
408
|
+
# INNER JOIN "people" "children_people_2" ON "children_people_2"."parent_id" = "parents_people"."id"
|
409
|
+
# WHERE ((("children_people"."name" LIKE 'Ernie%'
|
410
|
+
# OR "parents_people"."name" LIKE 'Ernie%')
|
411
|
+
# OR "children_people_2"."name" LIKE 'Ernie%'))
|
412
|
+
```
|
360
413
|
|
361
414
|
Keypaths were used here for clarity, but nested hashes would work just as well.
|
362
415
|
|
@@ -364,33 +417,41 @@ Keypaths were used here for clarity, but nested hashes would work just as well.
|
|
364
417
|
|
365
418
|
You can call SQL functions just like you would call a method in Ruby...
|
366
419
|
|
367
|
-
|
368
|
-
|
420
|
+
```ruby
|
421
|
+
Person.select{coalesce(name, '<no name given>')}
|
422
|
+
# => SELECT coalesce("people"."name", '<no name given>') FROM "people"
|
423
|
+
```
|
369
424
|
|
370
425
|
...and you can easily give it an alias:
|
371
426
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
427
|
+
```ruby
|
428
|
+
person = Person.select{
|
429
|
+
coalesce(name, '<no name given>').as(name_with_default)
|
430
|
+
}.first
|
431
|
+
person.name_with_default # name or <no name given>, depending on data
|
432
|
+
```
|
376
433
|
|
377
434
|
When you use a stub, symbol, or keypath inside a function call, it'll be interpreted relative to
|
378
435
|
its place inside any nested associations:
|
379
436
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
437
|
+
```ruby
|
438
|
+
Person.joins{articles}.group{articles.title}.having{{articles => {max(id) => id}}}
|
439
|
+
# => SELECT "people".* FROM "people"
|
440
|
+
# INNER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
441
|
+
# GROUP BY "articles"."title"
|
442
|
+
# HAVING max("articles"."id") = "articles"."id"
|
443
|
+
```
|
444
|
+
|
386
445
|
If you want to use an attribute from a different branch of the hierarchy, use an absolute
|
387
446
|
keypath (~) as done here:
|
388
447
|
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
448
|
+
```ruby
|
449
|
+
Person.joins{articles}.group{articles.title}.having{{articles => {max(~id) => id}}}
|
450
|
+
# => SELECT "people".* FROM "people"
|
451
|
+
# INNER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
452
|
+
# GROUP BY "articles"."title"
|
453
|
+
# HAVING max("people"."id") = "articles"."id"
|
454
|
+
```
|
394
455
|
|
395
456
|
### SQL Operators
|
396
457
|
|
@@ -398,13 +459,15 @@ You can use the standard mathematical operators (`+`, `-`, `*`, `/`) inside the
|
|
398
459
|
specify operators in the resulting SQL, or the `op` method to specify another
|
399
460
|
custom operator, such as the standard SQL concatenation operator, `||`:
|
400
461
|
|
401
|
-
|
402
|
-
|
403
|
-
|
462
|
+
```ruby
|
463
|
+
p = Person.select{name.op('||', '-diddly').as(flanderized_name)}.first
|
464
|
+
p.flanderized_name
|
465
|
+
# => "Aric Smith-diddly"
|
466
|
+
```
|
404
467
|
|
405
468
|
As you can see, just like functions, these operations can be given aliases.
|
406
469
|
|
407
|
-
## Compatibility with Active Record
|
470
|
+
## Compatibility with Active Record
|
408
471
|
|
409
472
|
Most of the new functionality provided by Squeel is accessed with the new block-style `where{}`
|
410
473
|
syntax.
|
@@ -424,15 +487,21 @@ the name of a **column** instead of simply treating the symbol as a **string lit
|
|
424
487
|
|
425
488
|
For example, this query:
|
426
489
|
|
427
|
-
|
490
|
+
```ruby
|
491
|
+
Person.where(:first_name => :last_name)
|
492
|
+
```
|
428
493
|
|
429
494
|
produces this SQL query in plain Active Record:
|
430
495
|
|
431
|
-
|
496
|
+
```
|
497
|
+
SELECT people.* FROM people WHERE people.first_name = 'last_name'.
|
498
|
+
```
|
432
499
|
|
433
500
|
but produces this SQL query if you are using Squeel:
|
434
501
|
|
435
|
-
|
502
|
+
```
|
503
|
+
SELECT people.* FROM people WHERE people.first_name = people.last_name
|
504
|
+
```
|
436
505
|
|
437
506
|
Note that this new behavior applies to the plain `where()`-style expressions in addition to the new
|
438
507
|
`where{}` Squeel style.
|
@@ -440,13 +509,17 @@ Note that this new behavior applies to the plain `where()`-style expressions in
|
|
440
509
|
In order for your existing `where()` clauses with symbols to continue to behave the same, you
|
441
510
|
**must** change the symbols into strings. These scopes, for example:
|
442
511
|
|
443
|
-
|
444
|
-
|
512
|
+
```ruby
|
513
|
+
scope :active, where(:state => :active)
|
514
|
+
scope :in_state, lambda {|state| where(:state => state) }
|
515
|
+
```
|
445
516
|
|
446
517
|
should be changed to this:
|
447
518
|
|
448
|
-
|
449
|
-
|
519
|
+
```ruby
|
520
|
+
scope :active, where(:state => 'active')
|
521
|
+
scope :in_state, lambda {|state| where(:state => state.to_s) }
|
522
|
+
```
|
450
523
|
|
451
524
|
For further information, see
|
452
525
|
[this post to the Rails list](https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-core/NQJJzZ7R7S0),
|
@@ -459,18 +532,21 @@ the [Active Record guides](http://edgeguides.rubyonrails.org/active_record_query
|
|
459
532
|
## Compatibility with MetaWhere
|
460
533
|
|
461
534
|
While the Squeel DSL is the preferred way to access advanced query functionality, you can
|
462
|
-
still enable methods on symbols to access
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
535
|
+
still enable methods on symbols to access Arel predications in a similar manner to MetaWhere:
|
536
|
+
|
537
|
+
```ruby
|
538
|
+
Squeel.configure do |config|
|
539
|
+
config.load_core_extensions :symbol
|
540
|
+
end
|
541
|
+
```
|
542
|
+
```ruby
|
543
|
+
Person.joins(:articles => :comments).
|
544
|
+
where(:articles => {:comments => {:body.matches => 'Hello!'}})
|
545
|
+
# => SELECT "people".* FROM "people"
|
546
|
+
# INNER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
547
|
+
# INNER JOIN "comments" ON "comments"."article_id" = "articles"."id"
|
548
|
+
# WHERE "comments"."body" LIKE 'Hello!'
|
549
|
+
```
|
474
550
|
|
475
551
|
This should help to smooth over the transition to the new DSL.
|
476
552
|
|