sequel_core 2.1.0 → 2.2.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.
@@ -1,50 +0,0 @@
1
- unless defined?(SEQUEL_NO_PARSE_TREE)
2
- begin
3
- require 'parse_tree'
4
- require 'sequel_core/dataset/parse_tree_sequelizer'
5
- class Proc
6
- def to_sql(dataset, opts = {})
7
- Sequel::Deprecation.deprecate("ParseTree filters are deprecated and will be removed in Sequel 2.2")
8
- dataset.send(:pt_expr, to_sexp[2], self.binding, opts)
9
- end
10
- end
11
- begin
12
- require 'ruby2ruby'
13
- class Sequel::Dataset
14
- # Evaluates a method call. This method is used to evaluate Ruby expressions
15
- # referring to indirect values, e.g.:
16
- #
17
- # dataset.filter {:category => category.to_s}
18
- # dataset.filter {:x > y[0..3]}
19
- #
20
- # This method depends on the Ruby2Ruby gem. If you do not have Ruby2Ruby
21
- # installed, this method will raise an error.
22
- def ext_expr(e, b, opts)
23
- eval(::RubyToRuby.new.process(e), b)
24
- end
25
- end
26
- class Proc
27
- remove_method :to_sexp
28
- end
29
- rescue LoadError
30
- class Sequel::Dataset
31
- def ext_expr(*args)
32
- raise Sequel::Error, "You must have the Ruby2Ruby gem installed in order to use this block filter."
33
- end
34
- end
35
- ensure
36
- class Proc
37
- # replacement for Proc#to_sexp as defined in ruby2ruby.
38
- # see also: http://rubyforge.org/tracker/index.php?func=detail&aid=18095&group_id=1513&atid=5921
39
- # The ruby2ruby implementation leaks memory, so we fix it.
40
- def to_sexp
41
- block = self
42
- c = Class.new {define_method(:m, &block)}
43
- ParseTree.translate(c, :m)[2]
44
- end
45
- end
46
- end
47
- rescue LoadError
48
- SEQUEL_NO_PARSE_TREE = true
49
- end
50
- end
@@ -1,504 +0,0 @@
1
- require File.join(File.dirname(__FILE__), 'spec_helper')
2
-
3
- context "Sequelizer without ParseTree" do
4
- setup do
5
- Sequel.use_parse_tree = false
6
- @db = Sequel::Database.new
7
- @ds = @db[:items]
8
- end
9
-
10
- teardown do
11
- Sequel.use_parse_tree = true
12
- end
13
-
14
- specify "should raise error when using ParseTree specific syntax without ParseTree" do
15
- proc {@ds.filter{:x << 1}}.should raise_error
16
- end
17
-
18
- specify "should allow expression syntax inside block raise error when converting ParseTree proc to SQL" do
19
- proc {@ds.filter{:x > 1}}.should_not raise_error(Sequel::Error)
20
- end
21
- end
22
-
23
- context "Sequelizer without Ruby2Ruby" do
24
- setup do
25
- module Kernel
26
- alias_method :orig_sq_require, :require
27
- def require(name); raise LoadError if name == 'ruby2ruby'; end
28
- end
29
- old_verbose = $VERBOSE
30
- $VERBOSE = nil
31
- load(File.join(File.dirname(__FILE__), '../lib/sequel_core/dataset/sequelizer.rb'))
32
- $VERBOSE = old_verbose
33
- @db = Sequel::Database.new
34
- @ds = @db[:items]
35
- end
36
-
37
- teardown do
38
- module Kernel
39
- alias_method :require, :orig_sq_require
40
- end
41
- old_verbose = $VERBOSE
42
- $VERBOSE = nil
43
- load(File.join(File.dirname(__FILE__), '../lib/sequel_core/dataset/sequelizer.rb'))
44
- $VERBOSE = old_verbose
45
- end
46
-
47
- pt_specify "should raise error only when using external expressions" do
48
- proc {proc {:x > 1}.to_sql(@ds)}.should_not raise_error(Sequel::Error)
49
- proc {proc {1 + 1}.to_sql(@ds)}.should raise_error(Sequel::Error)
50
- end
51
- end
52
-
53
- context "Proc#to_sql" do
54
- DB = Sequel::Database.new
55
- DS = DB[:items]
56
-
57
- class ::Proc
58
- def sql
59
- to_sql(DS)
60
- end
61
-
62
- def sql_comma_separated
63
- to_sql(DS, :comma_separated => true)
64
- end
65
- end
66
-
67
- def DS.match_expr(l, r)
68
- case r
69
- when String
70
- "(#{literal(l)} LIKE #{literal(r)})"
71
- when Regexp
72
- "(#{literal(l)} ~ #{literal(r.source)})"
73
- else
74
- raise Sequel::Error, "Unsupported match pattern class (#{r.class})."
75
- end
76
- end
77
-
78
- pt_specify "should support <sym> <op> <lit>" do
79
- proc {:x > 100}.sql.should == '(x > 100)'
80
- proc {:x < 100}.sql.should == '(x < 100)'
81
- proc {:x >= 100}.sql.should == '(x >= 100)'
82
- proc {:x <= 100}.sql.should == '(x <= 100)'
83
- proc {:x == 100}.sql.should == '(x = 100)'
84
- end
85
-
86
- pt_specify "should support number literals" do
87
- proc {:x > 123.45}.sql.should == '(x > 123.45)'
88
- proc {:x > -30_000}.sql.should == '(x > -30000)'
89
- end
90
-
91
- pt_specify "should support string literals" do
92
- proc {:x == 'abc'}.sql.should == "(x = 'abc')"
93
- proc {:y == "ab'cd"}.sql.should == "(y = 'ab''cd')"
94
- end
95
-
96
- pt_specify "should support boolean literals" do
97
- proc {:x == false}.sql.should == "(x = 'f')"
98
- proc {:x == true}.sql.should == "(x = 't')"
99
- end
100
-
101
- pt_specify "should support nil literal and nil?" do
102
- proc {:x == nil}.sql.should == "(x IS NULL)"
103
- proc {:x.nil?}.sql.should == "(x IS NULL)"
104
- end
105
-
106
- pt_specify "should support local vars or method references" do
107
- proc {proc {:x == a}.sql}.should raise_error(NameError)
108
- b = 123
109
- proc {:x == b}.sql.should == "(x = 123)"
110
- def xyz; 321; end
111
- proc {:x == xyz}.sql.should == "(x = 321)"
112
- proc {:x == xyz.to_s}.sql.should == "(x = '321')"
113
-
114
- def y1(x); x; end
115
- def y2; 111; end
116
-
117
- proc {:x == y1(222)}.sql.should == "(x = 222)"
118
- proc {:x == y2}.sql.should == "(x = 111)"
119
- end
120
-
121
- pt_specify "sould support subscript access on symbols" do
122
- proc {:x|1 > 0}.sql.should == "(x[1] > 0)"
123
- proc {:x|2|3 > 0}.sql.should == "(x[2, 3] > 0)"
124
- proc {:x|[4, 5] > 0}.sql.should == "(x[4, 5] > 0)"
125
- end
126
-
127
- pt_specify "should support constants" do
128
- ZZZ = 444
129
- proc {:x == ZZZ}.sql.should == "(x = 444)"
130
-
131
- CCCD = Module.new
132
- CCCD::DDD = 'hi'
133
- proc {:x == CCCD::DDD}.sql.should == "(x = 'hi')"
134
- end
135
-
136
- pt_specify "should support instance attributes" do
137
- @abc = 123
138
- proc {:x == @abc}.sql.should == "(x = 123)"
139
- end
140
-
141
- pt_specify "should support class attributes" do
142
- @@abc = 321
143
- proc {:x == @@abc}.sql.should == "(x = 321)"
144
- end
145
-
146
- pt_specify "should support like? pattern" do
147
- proc {:x.like? '%abc'}.sql.should == "(x LIKE '%abc')"
148
- end
149
-
150
- pt_specify "should support like? pattern with multiple choices" do
151
- proc {:x.like? ['%abc', '%def', '%ghi']}.sql.should == \
152
- "((x LIKE '%abc') OR (x LIKE '%def') OR (x LIKE '%ghi'))"
153
- end
154
-
155
- pt_specify "should support =~ operator" do
156
- # stock SQL version does not know about regexps
157
- proc {:x =~ '123'}.sql.should == "(x LIKE '123')"
158
-
159
- proc {:x =~ /^123/}.sql.should == "(x ~ '^123')"
160
- end
161
-
162
- pt_specify "should support =~ operator with multiple choices" do
163
- # stock SQL version does not know about regexps
164
- proc {:x =~ ['123', '456', '789']}.sql.should == "((x LIKE '123') OR (x LIKE '456') OR (x LIKE '789'))"
165
-
166
- proc {:x =~ [/^123/, /^456/, /^789/]}.sql.should == "((x ~ '^123') OR (x ~ '^456') OR (x ~ '^789'))"
167
-
168
- proc {:x =~ [/^123/, '456%', /^789/]}.sql.should == "((x ~ '^123') OR (x LIKE '456%') OR (x ~ '^789'))"
169
- end
170
-
171
- pt_specify "should raise on =~ operator for unsupported types" do
172
- proc {proc {:x =~ 123}.sql}.should raise_error(Sequel::Error)
173
- end
174
-
175
- pt_specify "should support != operator" do
176
- proc {:x != 100}.sql.should == "(NOT (x = 100))"
177
- end
178
-
179
- pt_specify "should support != operator with multiple choices" do
180
- proc {:x != [100, 200, 300]}.sql.should == "(NOT (x IN (100, 200, 300)))"
181
- end
182
-
183
- pt_specify "should support !~ operator" do
184
- proc {:x !~ '123'}.sql.should == "(NOT (x LIKE '123'))"
185
- end
186
-
187
- pt_specify "should support !~ operator with multiple choices" do
188
- proc {:x !~ ['123', '456']}.sql.should == "(NOT ((x LIKE '123') OR (x LIKE '456')))"
189
- end
190
-
191
- pt_specify "should support ! operator" do
192
- proc {!:x}.sql.should == "(x = 'f')"
193
- proc {!(:x > 100)}.sql.should == "(NOT (x > 100))"
194
- end
195
-
196
- pt_specify "should support && operator" do
197
- proc {1 && 2}.sql.should == "(1 AND 2)"
198
- proc {:x > 100 && :y < 100}.sql.should == "((x > 100) AND (y < 100))"
199
- proc {:x && :y && :z}.sql.should == "(x AND (y AND z))"
200
- end
201
-
202
- pt_specify "should support << operator for assignment" do
203
- proc {:x << 1}.sql.should == "x = 1"
204
- end
205
-
206
- pt_specify "should concatenate separate statements using AND" do
207
- proc {:x == 20; :y == 30}.sql.should == "((x = 20) AND (y = 30))"
208
- proc {:x != 1; :y != 2; :z != 3}.sql.should == \
209
- "((NOT (x = 1)) AND (NOT (y = 2)) AND (NOT (z = 3)))"
210
- end
211
-
212
- pt_specify "should concatenate separate statements using custom join argument" do
213
- proc {:x << 20; :y << 30}.sql_comma_separated.should == "x = 20, y = 30"
214
- z = 333
215
- proc {:x << :x + 1; :y << z}.sql_comma_separated.should == "x = (x + 1), y = 333"
216
- end
217
-
218
- pt_specify "should support || operator" do
219
- proc {1 || 2}.sql.should == "(1 OR 2)"
220
- proc {:x > 100 || :y < 100}.sql.should == "((x > 100) OR (y < 100))"
221
- proc {:x || :y || :z}.sql.should == "(x OR (y OR z))"
222
- end
223
-
224
- pt_specify "should support operator combinations" do
225
- proc {(:x > 1 || :y > 2) && (:z > 3)}.sql.should == "(((x > 1) OR (y > 2)) AND (z > 3))"
226
- proc {(1 && 2) || (3 || 4)}.sql.should == "((1 AND 2) OR (3 OR 4))"
227
- proc {(:x != 2) || (:y == 3) || !(:z == 4)}.sql.should == \
228
- "((NOT (x = 2)) OR ((y = 3) OR (NOT (z = 4))))"
229
- end
230
-
231
- pt_specify "should support late bound column references" do
232
- def abc; :tttt; end
233
- proc {abc > 2}.sql.should == "(tttt > 2)"
234
- end
235
-
236
- pt_specify "should support qualified column references" do
237
- proc {:x__y > 3}.sql.should == "(x.y > 3)"
238
- end
239
-
240
- pt_specify "should support functions on columns" do
241
- proc {:max[:x] > 100}.sql.should == "(max(x) > 100)"
242
- proc {:count[:x] > 100}.sql.should == "(count(x) > 100)"
243
- end
244
-
245
- pt_specify "should support SQL functions" do
246
- proc {:MAX[:x] > 100}.sql.should == "(MAX(x) > 100)"
247
-
248
- proc {:MAX[:x__y] > 100}.sql.should == "(MAX(x.y) > 100)"
249
- end
250
-
251
- pt_specify "should support SQL functions with multiple arguments" do
252
- proc {:sum[1, 2, 3] > 100}.sql.should == "(sum(1, 2, 3) > 100)"
253
-
254
- proc {:x[1, DB[:y].select(:z), "a'b"] > 100}.sql.should == \
255
- "(x(1, (SELECT z FROM y), 'a''b') > 100)"
256
- end
257
-
258
- pt_specify "should support SQL functions without arguments" do
259
- proc {:abc[] > 100}.sql.should == "(abc() > 100)"
260
-
261
- proc {:now[] - :last_stamp > 100}.sql.should == \
262
- "((now() - last_stamp) > 100)"
263
- end
264
-
265
- pt_specify "should do stuff like..." do
266
- proc {:price < 100 || :category != 'ruby'}.sql.should == \
267
- "((price < 100) OR (NOT (category = 'ruby')))"
268
- t = Time.now
269
- proc {:node_id == 1 && :stamp < t}.sql.should == \
270
- "((node_id = 1) AND (stamp < #{DS.literal(t)}))"
271
-
272
- proc {1 < :x}.sql.should == "(1 < x)"
273
- end
274
-
275
- pt_specify "should complain if someone is crazy" do
276
- proc {proc {def x; 1; end}.sql}.should raise_error(Sequel::Error::InvalidExpression)
277
- a = 1
278
- proc {proc {a = 1}.sql}.should raise_error(Sequel::Error::InvalidExpression)
279
- end
280
-
281
- pt_specify "should support comparison to Range objects" do
282
- proc {:x == (1..10)}.sql.should == \
283
- "(x >= 1 AND x <= 10)"
284
-
285
- proc {:x == (1...10)}.sql.should == \
286
- "(x >= 1 AND x < 10)"
287
-
288
- a, b = 3, 5
289
- proc {:x == (a..b)}.sql.should == \
290
- "(x >= 3 AND x <= 5)"
291
-
292
- proc {:x == (a...b)}.sql.should == \
293
- "(x >= 3 AND x < 5)"
294
-
295
- t1 = Time.now - 4000
296
- t2 = Time.now - 2000
297
-
298
- proc {:stamp == (t1..t2)}.sql.should == \
299
- "(stamp >= #{DS.literal(t1)} AND stamp <= #{DS.literal(t2)})"
300
- end
301
-
302
- pt_specify "should support comparison to sub-queries" do
303
- @ds2 = DB[:test].select(:node_id)
304
-
305
- proc {:id == @ds2}.sql.should == \
306
- "(id IN (SELECT node_id FROM test))"
307
-
308
- proc {:id == DB[:test].select(:node_id)}.sql.should == \
309
- "(id IN (SELECT node_id FROM test))"
310
-
311
- proc {:id == DB[:test].select(:node_id).filter {:active == true}}.sql.should == \
312
- "(id IN (SELECT node_id FROM test WHERE (active = 't')))"
313
-
314
- proc {:price >= DB[:items].select(:price)}.sql.should == \
315
- "(price >= (SELECT price FROM items))"
316
- end
317
-
318
- pt_specify "should support comparison to arrays" do
319
- proc {:id == [1, 3, 7, 15]}.sql.should == \
320
- "(id IN (1, 3, 7, 15))"
321
- end
322
-
323
- pt_specify "should not literalize String#expr and String#lit" do
324
- proc {'x'.lit == 1}.sql.should == "(x = 1)"
325
- proc {'x.y'.expr == 1}.sql.should == "(x.y = 1)"
326
- end
327
-
328
- pt_specify "should support in/in? operator" do
329
- proc {:x.in [3, 4, 5]}.sql.should == "(x IN (3, 4, 5))"
330
- proc {:x.in?(3, 4, 5)}.sql.should == "(x IN (3, 4, 5))"
331
-
332
- proc {:x.in(1..10)}.sql.should == "(x >= 1 AND x <= 10)"
333
- proc {:x.in?(1..10)}.sql.should == "(x >= 1 AND x <= 10)"
334
-
335
- @ds2 = DB[:test].select(:node_id)
336
- proc {:x.in @ds2}.sql.should == "(x IN (SELECT node_id FROM test))"
337
- end
338
-
339
- pt_specify "should support nested procs" do
340
- proc {:x > 10 || proc{:y > 20}}.sql.should == \
341
- "((x > 10) OR (y > 20))"
342
-
343
- def pr(&block)
344
- proc {:x > 10 || block}
345
- end
346
-
347
- pr {:y > 20}.sql.should == \
348
- "((x > 10) OR (y > 20))"
349
- end
350
-
351
- pt_specify "should support unfolding of calls to #each" do
352
- # from http://groups.google.com/group/sequel-talk/browse_thread/thread/54a660568515fbb7
353
- periods = [:day, :week, :month, :year, :alltime]
354
- idx = 1
355
- v = 2
356
- pr = proc do
357
- periods.each do |p|
358
- (p|idx) << (p|idx) + v
359
- end
360
- end
361
- pr.sql_comma_separated.should == \
362
- "day[1] = (day[1] + 2), week[1] = (week[1] + 2), month[1] = (month[1] + 2), year[1] = (year[1] + 2), alltime[1] = (alltime[1] + 2)"
363
- end
364
-
365
- pt_specify "should support unfolding of calls to Hash#each" do
366
- periods = {:month => 3}
367
- idx = 1
368
- pr = proc do
369
- periods.each do |k, v|
370
- k << k + v
371
- end
372
- end
373
- pr.sql_comma_separated.should == "month = (month + 3)"
374
- end
375
-
376
- pt_specify "should support local arguments" do
377
- def t(x)
378
- proc {x > 10}.sql
379
- end
380
- t(:y).should == "(y > 10)"
381
- end
382
-
383
- pt_specify "should support binary operators on local context" do
384
- XXX = 1
385
- YYY = 2
386
- proc {XXX || YYY}.sql.should == "(1 OR 2)"
387
-
388
- xxx = 1
389
- yyy = 2
390
- proc {xxx && yyy}.sql.should == "(1 AND 2)"
391
- end
392
-
393
- pt_specify "should support arithmetics" do
394
- zzz = 300
395
- proc {(:x + 100) > zzz}.sql.should == "((x + 100) > 300)"
396
-
397
- proc {(:x + :y * 100) > zzz}.sql.should == "((x + (y * 100)) > 300)"
398
-
399
- proc {:units * :price}.sql.should == "(units * price)"
400
- end
401
-
402
- pt_specify "should support | operator" do
403
- proc {(:x | 1) > 0}.sql.should == "(x[1] > 0)"
404
- proc {10 | 1}.sql.should == 11
405
- end
406
-
407
- pt_specify "should support globals" do
408
- $aaaa_zzzz = 400
409
- proc {:x > $aaaa_zzzz}.sql.should == "(x > 400)"
410
- end
411
-
412
- pt_specify "should support Regexp macros" do
413
- "abc" =~ /(ab)/
414
- proc {:x == $1}.sql.should == "(x = 'ab')"
415
- end
416
-
417
- pt_specify "should evaluate expression not referring to symbols or literal strings." do
418
- proc {:x > 2 * 3}.sql.should == "(x > 6)"
419
- y = 3
420
- proc {:x > y * 4}.sql.should == "(x > 12)"
421
-
422
- proc {:AVG[:x] > 4}.sql.should == "(AVG(x) > 4)"
423
-
424
- proc {:AVG[:x] > 4}.sql.should == "(AVG(x) > 4)"
425
-
426
- proc {:y == (1 > 2)}.sql.should == "(y = 'f')"
427
- end
428
-
429
- pt_specify "should support ternary operator" do
430
- y = true
431
- proc {:x > (y ? 1 : 2)}.sql.should == "(x > 1)"
432
-
433
- proc {((1 > 2) ? :x : :y) > 3}.sql.should == "(y > 3)"
434
- end
435
-
436
- pt_specify "should support strings with embedded Ruby code in them and literalize them" do
437
- proc {:n == "#{1+2}"}.sql.should == "(n = '3')"
438
-
439
- y = "12'34"
440
-
441
- proc {:x > "#{y}"}.sql.should == "(x > '12''34')"
442
- end
443
-
444
- pt_specify "should support format strings and literalize the result" do
445
- prod = 1
446
- proc {:x == "abc%d" % prod}.sql.should == "(x = 'abc1')"
447
-
448
- proc {:x == ("%d" % prod).lit}.sql.should == "(x = 1)"
449
- end
450
-
451
- pt_specify "should support conditional filters" do
452
- @criteria = nil
453
- proc {if @criteria; :x.like @criteria; end}.sql.should == nil
454
-
455
- @criteria = 'blah'
456
- proc {if @criteria; :x.like @criteria; end}.sql.should == "(x LIKE 'blah')"
457
-
458
- @criteria = nil
459
- proc {if @criteria; :x.like @criteria; else; :x.like 'ddd'; end}.sql.should == "(x LIKE 'ddd')"
460
- end
461
-
462
- pt_specify "should support more complex conditional filters" do
463
- x, y = true, false
464
- proc {
465
- if x || y
466
- :a.like 'a'
467
- else
468
- :b.like 'b'
469
- end
470
- }.sql.should == "(a LIKE 'a')"
471
-
472
- proc {
473
- if y
474
- :a.like 'a'
475
- elsif x
476
- :b.like 'b'
477
- else
478
- :c.like 'c'
479
- end
480
- }.sql.should == "(b LIKE 'b')"
481
-
482
- @title = 'blah'
483
- use_title = true
484
-
485
- proc {
486
- if !@title.blank? && use_title
487
- :title.like "%#{@title}%"
488
- end
489
- }.sql.should == "(title LIKE '%blah%')"
490
-
491
-
492
- end
493
- end
494
-
495
- context "Proc#to_sql stock" do
496
- pt_specify "should not support regexps" do
497
- db = Sequel::Database.new
498
- ds = db[:items]
499
-
500
- p = proc {:x =~ /abc/}
501
- proc {p.to_sql(ds)}.should raise_error(Sequel::Error)
502
- end
503
- end
504
-