rufus-tokyo 1.0.3 → 1.0.4
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/CHANGELOG.txt +6 -0
- data/Rakefile +91 -0
- data/doc/decision_table.numbers +0 -0
- data/lib/rufus/edo/README.txt +101 -0
- data/lib/rufus/edo/tabcore.rb +1 -3
- data/lib/rufus/tokyo.rb +1 -2
- data/lib/rufus/tokyo/cabinet/lib.rb +4 -7
- data/lib/rufus/tokyo/cabinet/table.rb +10 -13
- data/lib/rufus/tokyo/cabinet/util.rb +4 -1
- data/lib/rufus/tokyo/hmethods.rb +4 -4
- data/lib/rufus/tokyo/outlen.rb +5 -1
- data/lib/rufus/tokyo/tyrant/abstract.rb +8 -0
- data/lib/rufus/tokyo/tyrant/lib.rb +6 -6
- data/lib/rufus/tokyo/tyrant/table.rb +9 -1
- data/lib/rufus/tokyo/version.rb +32 -0
- data/rufus-tokyo.gemspec +135 -0
- data/spec/cabinet_btree_spec.rb +92 -0
- data/spec/cabinet_fixed_spec.rb +33 -0
- data/spec/cabinet_spec.rb +291 -0
- data/spec/cabinetconfig_spec.rb +82 -0
- data/spec/dystopia_core_spec.rb +124 -0
- data/spec/edo_cabinet_btree_spec.rb +123 -0
- data/spec/edo_cabinet_fixed_spec.rb +42 -0
- data/spec/edo_cabinet_spec.rb +286 -0
- data/spec/edo_ntyrant_spec.rb +224 -0
- data/spec/edo_ntyrant_table_spec.rb +296 -0
- data/spec/edo_table_spec.rb +292 -0
- data/spec/hmethods_spec.rb +73 -0
- data/spec/incr.lua +23 -0
- data/spec/openable_spec.rb +51 -0
- data/spec/shared_abstract_spec.rb +426 -0
- data/spec/shared_table_spec.rb +675 -0
- data/spec/shared_tyrant_spec.rb +42 -0
- data/spec/spec_base.rb +23 -0
- data/spec/start_tyrants.sh +28 -0
- data/spec/stop_tyrants.sh +9 -0
- data/spec/table_spec.rb +267 -0
- data/spec/tyrant_spec.rb +218 -0
- data/spec/tyrant_table_spec.rb +298 -0
- data/spec/util_list_spec.rb +197 -0
- data/spec/util_map_spec.rb +130 -0
- data/tasks/dev.rb +70 -0
- data/test/bm0.rb +353 -0
- data/test/bm1_compression.rb +54 -0
- data/test/con0.rb +30 -0
- data/test/mem.rb +49 -0
- data/test/mem1.rb +44 -0
- data/test/readme0.rb +17 -0
- data/test/readme1.rb +21 -0
- data/test/readme2.rb +15 -0
- data/test/readme3.rb +24 -0
- data/test/readmes_test.sh +17 -0
- metadata +81 -21
- data/MIGRATED.txt +0 -1
@@ -0,0 +1,675 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Specifying rufus-tokyo
|
4
|
+
#
|
5
|
+
# Tue Jul 21 13:02:53 JST 2009
|
6
|
+
#
|
7
|
+
|
8
|
+
|
9
|
+
shared 'table' do
|
10
|
+
|
11
|
+
it 'should generate unique ids' do
|
12
|
+
|
13
|
+
@t.genuid.should.satisfy { |i| i.to_s > '0' }
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return nil for missing keys' do
|
17
|
+
|
18
|
+
@t['missing'].should.be.nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should accept Array and Hash input' do
|
22
|
+
|
23
|
+
@t.size.should.equal(0)
|
24
|
+
|
25
|
+
@t['pk0'] = [ 'name', 'toto', 'age', '30' ]
|
26
|
+
@t['pk1'] = { 'name' => 'fred', 'age' => '22' }
|
27
|
+
|
28
|
+
@t.size.should.equal(2)
|
29
|
+
@t['pk0'].should.equal({ 'name' => 'toto', 'age' => '30' })
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return nil when deleting inexistent entries' do
|
33
|
+
|
34
|
+
@t.delete('I_do_not_exist').should.equal(nil)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should delete the entry and return the value' do
|
38
|
+
|
39
|
+
@t['pk0'] = [ 'name', 'toto', 'age', '30' ]
|
40
|
+
@t.delete('pk0').should.equal({ 'name' => 'toto', 'age' => '30' })
|
41
|
+
@t.size.should.equal(0)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should raise an ArgumentError on non map or hash input' do
|
45
|
+
|
46
|
+
lambda {
|
47
|
+
@t['pk0'] = 'bad thing here'
|
48
|
+
}.should.raise(ArgumentError)
|
49
|
+
end
|
50
|
+
|
51
|
+
#it 'should raise an ArgumentError on non-string column name' do
|
52
|
+
# lambda {
|
53
|
+
# @t['pk0'] = [ 1, 2 ]
|
54
|
+
# }.should.raise(ArgumentError)
|
55
|
+
# lambda {
|
56
|
+
# @t['pk0'] = { 1 => 2 }
|
57
|
+
# }.should.raise(ArgumentError)
|
58
|
+
#end
|
59
|
+
#it 'should raise an ArgumentError on non-string column value' do
|
60
|
+
# lambda {
|
61
|
+
# @t['pk0'] = { 'a' => 2 }
|
62
|
+
# }.should.raise(ArgumentError)
|
63
|
+
#end
|
64
|
+
|
65
|
+
it 'should store binary data \0' do
|
66
|
+
s = "toto#{0.chr}nada"
|
67
|
+
@t[s] = { s => s }
|
68
|
+
@t[s].should.equal({ s => s })
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should stringify primary key, keys, and values on read and write' do
|
72
|
+
@t[123] = {:num => 456}
|
73
|
+
@t["123".to_sym].should.equal("num" => "456")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
shared 'table with transactions' do
|
78
|
+
|
79
|
+
it 'should correctly abort transactions' do
|
80
|
+
|
81
|
+
@t.transaction {
|
82
|
+
@t['pk0'] = { 'a' => 'A' }
|
83
|
+
@t.abort
|
84
|
+
}
|
85
|
+
@t.size.should.be.zero
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should rollback transactions with errors, and bubble exceptions' do
|
89
|
+
|
90
|
+
begin
|
91
|
+
@t.transaction {
|
92
|
+
@t['pk0'] = { 'a' => 'A' }
|
93
|
+
raise 'something goes wrong'
|
94
|
+
}
|
95
|
+
rescue RuntimeError
|
96
|
+
end
|
97
|
+
@t.size.should.be.zero
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should rollback transactions with Abort exceptions, and consume exceptions' do
|
101
|
+
|
102
|
+
@t.transaction {
|
103
|
+
@t['pk0'] = { 'a' => 'A' }
|
104
|
+
raise Rufus::Tokyo::Transactions::Abort
|
105
|
+
}
|
106
|
+
@t.size.should.be.zero
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should commit successful transactions' do
|
110
|
+
|
111
|
+
@t.transaction do
|
112
|
+
@t['pk0'] = { 'a' => 'A' }
|
113
|
+
end
|
114
|
+
@t['pk0'].should.equal({ 'a' => 'A' })
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should abort low level transactions' do
|
118
|
+
|
119
|
+
@t.tranbegin
|
120
|
+
@t['pk0'] = { 'a' => 'A' }
|
121
|
+
@t.tranabort
|
122
|
+
@t.size.should.be.zero
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should commit low level transactions' do
|
126
|
+
|
127
|
+
@t.tranbegin
|
128
|
+
@t['pk0'] = { 'a' => 'A' }
|
129
|
+
@t.trancommit
|
130
|
+
@t['pk0'].should.equal({ 'a' => 'A' })
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
shared 'table #keys' do
|
135
|
+
|
136
|
+
it 'should return a Ruby Array by default' do
|
137
|
+
|
138
|
+
@t.keys.class.should.equal(::Array)
|
139
|
+
end
|
140
|
+
|
141
|
+
if @t.class.name.match(/^Rufus::Tokyo/)
|
142
|
+
|
143
|
+
it 'should return a Cabinet List when :native => true' do
|
144
|
+
|
145
|
+
l = @t.keys(:native => true)
|
146
|
+
l.class.should.equal(Rufus::Tokyo::List)
|
147
|
+
l.size.should.equal(2 * @n + 1)
|
148
|
+
l.free
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should retrieve forward matching keys when :prefix => "prefix-"' do
|
153
|
+
|
154
|
+
@t.keys(:prefix => 'person').size.should.equal(@n)
|
155
|
+
|
156
|
+
#l = @t.keys(:prefix => 'animal', :native => true)
|
157
|
+
#l.size.should.equal(@n)
|
158
|
+
#l.free
|
159
|
+
l = @t.keys(:prefix => 'animal')
|
160
|
+
l.size.should.equal(@n)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should retrieve keys that contain \0' do
|
164
|
+
|
165
|
+
@t.keys.include?("toto#{0.chr}5").should.be.true
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should retrieve forward matching keys when key contains \0' do
|
169
|
+
|
170
|
+
@t.keys(:prefix => 'toto').should.equal([ "toto#{0.chr}5" ])
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should return a limited number of keys when :limit is set' do
|
174
|
+
|
175
|
+
@t.keys(:limit => 20).size.should.equal(20)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should delete_keys_with_prefix' do
|
179
|
+
|
180
|
+
@t.delete_keys_with_prefix('animal')
|
181
|
+
@t.size.should.equal(@n + 1)
|
182
|
+
@t.keys(:prefix => 'animal').size.should.equal(0)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
shared 'table indexes' do
|
187
|
+
|
188
|
+
it 'should accept lexical indexes' do
|
189
|
+
@t.set_index('name', :lexical).should.equal(true)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should accept decimal indexes' do
|
193
|
+
@t.set_index('age', :decimal).should.equal(true)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should accept removal of indexes' do
|
197
|
+
@t.set_index('age', :decimal)
|
198
|
+
@t.set_index('age', :remove).should.equal(true)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should accept indexes on the primary key (well...)' do
|
202
|
+
@t.set_index(:pk, :lexical).should.equal(true)
|
203
|
+
@t.set_index('', :lexical).should.equal(true)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
shared 'table lget' do
|
208
|
+
|
209
|
+
it 'should return an empty hash for missing keys' do
|
210
|
+
@t.lget(%w{ pk97 pk98 }).should.equal({})
|
211
|
+
@t.mget(%w{ pk97 pk98 }).should.equal({})
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should return multiple records' do
|
215
|
+
@t.lget(%w{ pk0 pk1 }).should.equal({
|
216
|
+
'pk0' => { 'name' => 'jim', 'age' => '25', 'lang' => 'ja,en' },
|
217
|
+
'pk1' => { 'name' => 'jeff', 'age' => '32', 'lang' => 'en,es' }
|
218
|
+
})
|
219
|
+
@t.lget(*%w{ pk0 pk1 }).should.equal({
|
220
|
+
'pk0' => { 'name' => 'jim', 'age' => '25', 'lang' => 'ja,en' },
|
221
|
+
'pk1' => { 'name' => 'jeff', 'age' => '32', 'lang' => 'en,es' }
|
222
|
+
})
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
shared 'table like a hash' do
|
227
|
+
|
228
|
+
it 'should respond to #keys' do
|
229
|
+
|
230
|
+
@t.keys.should.equal([ 'pk0', 'pk1', 'pk2', 'pk3' ])
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'should respond to #values' do
|
234
|
+
|
235
|
+
@t.values.should.equal([
|
236
|
+
{ 'name' => 'jim', 'age' => '25', 'lang' => 'ja,en' },
|
237
|
+
{ 'name' => 'jeff', 'age' => '32', 'lang' => 'en,es' },
|
238
|
+
{ 'name' => 'jack', 'age' => '44', 'lang' => 'en' },
|
239
|
+
{ 'name' => 'jake', 'age' => '45', 'lang' => 'en,li' }])
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'should benefit from Enumerable' do
|
243
|
+
|
244
|
+
@t.find { |k, v|
|
245
|
+
v['name'] == 'jeff'
|
246
|
+
}.should.equal([
|
247
|
+
'pk1', { 'name' => 'jeff', 'age' => '32', 'lang' => 'en,es' }])
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
shared 'table query' do
|
252
|
+
|
253
|
+
it 'can be executed' do
|
254
|
+
|
255
|
+
@t.query { |q|
|
256
|
+
q.add 'lang', :includes, 'en'
|
257
|
+
}.size.should.equal(4)
|
258
|
+
end
|
259
|
+
|
260
|
+
if @t.class.name.match(/^Rufus::Tokyo::/)
|
261
|
+
|
262
|
+
it 'can be prepared' do
|
263
|
+
|
264
|
+
@t.prepare_query { |q|
|
265
|
+
q.add 'lang', :includes, 'en'
|
266
|
+
}.should.satisfy { |q| q.class == Rufus::Tokyo::TableQuery }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'can be counted' do
|
271
|
+
|
272
|
+
q = @t.prepare_query { |qq| qq.add 'lang', :includes, 'en' }
|
273
|
+
q.run
|
274
|
+
q.count.should.equal(4)
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'can be counted without being explicitly run' do
|
278
|
+
|
279
|
+
@t.prepare_query { |qq|
|
280
|
+
qq.add 'lang', :includes, 'en'
|
281
|
+
}.count.should.equal(4)
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'can be counted immediately (qrycount table#query_count)' do
|
285
|
+
|
286
|
+
@t.query_count { |qq|
|
287
|
+
qq.add 'lang', :includes, 'en'
|
288
|
+
}.should.equal(4)
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'can be limited' do
|
292
|
+
|
293
|
+
@t.query { |q|
|
294
|
+
q.add 'lang', :includes, 'en'
|
295
|
+
q.limit 2
|
296
|
+
}.size.should.equal(2)
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'can leverage regex matches' do
|
300
|
+
|
301
|
+
@t.query { |q|
|
302
|
+
q.add 'name', :matches, '^j.+k'
|
303
|
+
}.to_a.should.equal([
|
304
|
+
{:pk => 'pk2', "name"=>"jack", "lang"=>"en", "age"=>"44"},
|
305
|
+
{:pk => 'pk3', "name"=>"jake", "lang"=>"en,li", "age"=>"45"}])
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'can leverage numerical comparison (gt)' do
|
309
|
+
|
310
|
+
@t.query { |q|
|
311
|
+
q.add 'age', :gt, '40'
|
312
|
+
q.pk_only
|
313
|
+
}.to_a.should.equal([ 'pk2', 'pk3' ])
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'can have negated conditions' do
|
317
|
+
|
318
|
+
@t.query { |q|
|
319
|
+
q.add 'age', :gt, '40', false
|
320
|
+
q.pk_only
|
321
|
+
}.to_a.should.equal([ 'pk0', 'pk1' ])
|
322
|
+
end
|
323
|
+
|
324
|
+
if (@t.respond_to?(:lib) && @t.lib.respond_to?(:qry_setlimit)) ||
|
325
|
+
(defined?(TokyoCabinet) && TokyoCabinet::TDBQRY.public_instance_methods.collect { |e| e.to_s }.include?('setlimit'))
|
326
|
+
|
327
|
+
it 'can be limited and have an offset' do
|
328
|
+
|
329
|
+
@t.query { |q|
|
330
|
+
q.add 'lang', :includes, 'en'
|
331
|
+
q.order_by 'name', :desc
|
332
|
+
q.limit 2, 0
|
333
|
+
}.collect { |e| e['name'] }.should.equal(%w{ jim jeff })
|
334
|
+
@t.query { |q|
|
335
|
+
q.add 'lang', :includes, 'en'
|
336
|
+
q.order_by 'name', :desc
|
337
|
+
q.limit 2, 2
|
338
|
+
}.collect { |e| e['name'] }.should.equal(%w{ jake jack })
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'can be deleted (searchout : query#delete)' do
|
343
|
+
|
344
|
+
@t.prepare_query { |q|
|
345
|
+
q.add 'lang', :includes, 'es'
|
346
|
+
}.delete
|
347
|
+
|
348
|
+
@t.size.should.equal(3)
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'can be deleted immediately (searchout table#query_delete)' do
|
352
|
+
|
353
|
+
@t.query_delete { |q|
|
354
|
+
q.add 'lang', :includes, 'es'
|
355
|
+
}
|
356
|
+
|
357
|
+
@t.size.should.equal(3)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
shared 'table query (fts)' do
|
362
|
+
|
363
|
+
it 'can do full-text search' do
|
364
|
+
|
365
|
+
@t.query { |q|
|
366
|
+
q.add 'words', :ftsphrase, 'consul'
|
367
|
+
q.pk_only
|
368
|
+
}.to_a.should.equal(%w[ pk0 pk3 pk5 ])
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
shared 'table query #process' do
|
373
|
+
|
374
|
+
it 'can iterate over the matching records' do
|
375
|
+
|
376
|
+
keys, values = [], []
|
377
|
+
|
378
|
+
@t.prepare_query { |q|
|
379
|
+
q.add 'lang', :includes, 'en'
|
380
|
+
}.process { |k, v|
|
381
|
+
keys << k
|
382
|
+
values << v
|
383
|
+
}.free
|
384
|
+
|
385
|
+
keys.should.equal(%w[ pk0 pk1 pk2 pk3 ])
|
386
|
+
values.first.keys.sort.should.equal(%w[ age lang name ])
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'can stop while iterating' do
|
390
|
+
|
391
|
+
seen = 0
|
392
|
+
|
393
|
+
@t.prepare_query { |q|
|
394
|
+
q.add 'lang', :includes, 'en'
|
395
|
+
}.process { |k, v|
|
396
|
+
seen = seen + 1
|
397
|
+
:stop
|
398
|
+
}.free
|
399
|
+
|
400
|
+
seen.should.equal(1)
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'can delete while iterating' do
|
404
|
+
|
405
|
+
@t.prepare_query { |q|
|
406
|
+
q.add 'lang', :includes, 'en'
|
407
|
+
}.process { |k, v|
|
408
|
+
v['name'].match(/^ja/) ? :delete : nil
|
409
|
+
}.free
|
410
|
+
|
411
|
+
@t.keys.sort.should.equal(%w[ pk0 pk1 ])
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'can update while iterating' do
|
415
|
+
|
416
|
+
@t.prepare_query { |q|
|
417
|
+
q.add 'lang', :includes, 'en'
|
418
|
+
}.process { |k, v|
|
419
|
+
v['name'].match(/^ja/) ? v.merge('special' => 'seen') : nil
|
420
|
+
}.free
|
421
|
+
|
422
|
+
@t.size.should.equal(4)
|
423
|
+
|
424
|
+
@t['pk2'].should.equal(
|
425
|
+
{'name'=>'jack', 'age'=>'44', 'lang'=>'en', 'special'=>'seen'})
|
426
|
+
@t['pk3'].should.equal(
|
427
|
+
{'name'=>'jake', 'age'=>'45', 'lang'=>'en,li', 'special'=>'seen'})
|
428
|
+
end
|
429
|
+
|
430
|
+
it 'can update, delete and stop' do
|
431
|
+
|
432
|
+
seen = []
|
433
|
+
|
434
|
+
@t.prepare_query { |q|
|
435
|
+
q.add 'lang', :includes, 'en'
|
436
|
+
q.order_by 'name', :desc
|
437
|
+
}.process { |k, v|
|
438
|
+
seen << v['name']
|
439
|
+
case v['name']
|
440
|
+
when 'jim' then nil
|
441
|
+
when 'jeff' then :delete
|
442
|
+
when 'jake' then [ :stop, v.merge('special' => 'nada') ]
|
443
|
+
end
|
444
|
+
}.free
|
445
|
+
|
446
|
+
seen.include?('jack').should.be.false
|
447
|
+
|
448
|
+
@t.size.should.equal(3)
|
449
|
+
|
450
|
+
@t['pk3'].should.equal(
|
451
|
+
{'name'=>'jake', 'age'=>'45', 'lang'=>'en,li', 'special'=>'nada'})
|
452
|
+
@t['pk1'].should.be.nil
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
shared 'table query results' do
|
457
|
+
|
458
|
+
it 'can come ordered (strdesc)' do
|
459
|
+
|
460
|
+
@t.query { |q|
|
461
|
+
q.add 'lang', :includes, 'en'
|
462
|
+
q.order_by 'name', :desc
|
463
|
+
q.limit 2
|
464
|
+
}.to_a.should.equal([
|
465
|
+
{:pk => 'pk0', "name"=>"jim", "lang"=>"ja,en", "age"=>"25"},
|
466
|
+
{:pk => 'pk1', "name"=>"jeff", "lang"=>"en,es", "age"=>"32"}])
|
467
|
+
end
|
468
|
+
|
469
|
+
it 'can come ordered (strasc)' do
|
470
|
+
|
471
|
+
@t.query { |q|
|
472
|
+
q.add 'lang', :includes, 'en'
|
473
|
+
q.order_by 'name', :asc
|
474
|
+
}.to_a.should.equal([
|
475
|
+
{:pk => 'pk2', "name"=>"jack", "lang"=>"en", "age"=>"44"},
|
476
|
+
{:pk => 'pk3', "name"=>"jake", "lang"=>"en,li", "age"=>"45"},
|
477
|
+
{:pk => 'pk1', "name"=>"jeff", "lang"=>"en,es", "age"=>"32"},
|
478
|
+
{:pk => 'pk0', "name"=>"jim", "lang"=>"ja,en", "age"=>"25"}])
|
479
|
+
end
|
480
|
+
|
481
|
+
it 'can come ordered (numasc)' do
|
482
|
+
|
483
|
+
@t.query { |q|
|
484
|
+
q.add 'lang', :includes, 'en'
|
485
|
+
q.order_by 'age', :numasc
|
486
|
+
}.to_a.should.equal([
|
487
|
+
{:pk => 'pk0', "name"=>"jim", "lang"=>"ja,en", "age"=>"25"},
|
488
|
+
{:pk => 'pk1', "name"=>"jeff", "lang"=>"en,es", "age"=>"32"},
|
489
|
+
{:pk => 'pk2', "name"=>"jack", "lang"=>"en", "age"=>"44"},
|
490
|
+
{:pk => 'pk3', "name"=>"jake", "lang"=>"en,li", "age"=>"45"}])
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'can come without the primary keys (no_pk)' do
|
494
|
+
|
495
|
+
@t.query { |q|
|
496
|
+
q.add 'name', :matches, '^j.+k'
|
497
|
+
q.no_pk
|
498
|
+
}.to_a.should.equal([
|
499
|
+
{"name"=>"jack", "lang"=>"en", "age"=>"44"},
|
500
|
+
{"name"=>"jake", "lang"=>"en,li", "age"=>"45"}])
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'can consist only of the primary keys (pk_only)' do
|
504
|
+
|
505
|
+
@t.query { |q|
|
506
|
+
q.add 'name', :matches, '^j.+k'
|
507
|
+
q.pk_only
|
508
|
+
}.to_a.should.equal(["pk2", "pk3"])
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
shared 'tyrant table with embedded lua' do
|
513
|
+
|
514
|
+
it 'should call Lua extensions' do
|
515
|
+
@t.ext(:hi).should.equal('Hi!')
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'should return nil when function is missing' do
|
519
|
+
@t.ext(:missing, 'nada', 'forever').should.equal(nil)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
shared 'a table structure flattening keys and values' do
|
524
|
+
|
525
|
+
it 'should to_s column names when #set_index' do
|
526
|
+
|
527
|
+
@t.set_index(:name, :lexical).should.equal(true)
|
528
|
+
end
|
529
|
+
|
530
|
+
it 'should to_s keys and values in the hash when #[]=' do
|
531
|
+
|
532
|
+
@t[:toto] = { :a => 1, :b => 2 }
|
533
|
+
@t['toto'].should.equal({ 'a' => '1', 'b' => '2' })
|
534
|
+
end
|
535
|
+
|
536
|
+
it 'should to_s keys when #delete' do
|
537
|
+
|
538
|
+
@t['toto'] = { 'a' => '1', 'b' => '2' }
|
539
|
+
@t.delete(:toto).should.equal({ 'a' => '1', 'b' => '2' })
|
540
|
+
end
|
541
|
+
|
542
|
+
it 'should to_s keys when #lget' do
|
543
|
+
|
544
|
+
(1..7).each { |i| @t["toto#{i}"] = { 'i' => i.to_s } }
|
545
|
+
|
546
|
+
@t.lget([ :toto1, :toto3, :toto4 ]).should.equal(
|
547
|
+
{"toto1"=>{"i"=>"1"}, "toto3"=>{"i"=>"3"}, "toto4"=>{"i"=>"4"}})
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
shared 'a table structure to_s-ing query stuff' do
|
552
|
+
|
553
|
+
it 'should accept symbols as column names' do
|
554
|
+
|
555
|
+
@t.query { |q|
|
556
|
+
q.add :lang, :includes, 'en'
|
557
|
+
}.size.should.equal(4)
|
558
|
+
end
|
559
|
+
|
560
|
+
it 'should accept non-strings as values' do
|
561
|
+
|
562
|
+
@t.query { |q|
|
563
|
+
q.add 'age', :equals, 44
|
564
|
+
}.to_a.should.equal(
|
565
|
+
[{"name"=>"jack", "lang"=>"en", :pk=>"pk2", "age"=>"44"}])
|
566
|
+
end
|
567
|
+
|
568
|
+
it 'should accept symbols as column names in #order_by' do
|
569
|
+
|
570
|
+
@t.query { |q|
|
571
|
+
q.add 'lang', :includes, 'en'
|
572
|
+
q.order_by :name, :desc
|
573
|
+
q.limit 2
|
574
|
+
}.to_a.should.equal([
|
575
|
+
{:pk => 'pk0', "name"=>"jim", "lang"=>"ja,en", "age"=>"25"},
|
576
|
+
{:pk => 'pk1', "name"=>"jeff", "lang"=>"en,es", "age"=>"32"}])
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
shared 'table query metasearch' do
|
581
|
+
|
582
|
+
it 'can do UNION on queries' do
|
583
|
+
|
584
|
+
@t.union(
|
585
|
+
@t.prepare_query { |q|
|
586
|
+
q.add 'lang', :includes, 'es'
|
587
|
+
},
|
588
|
+
@t.prepare_query { |q|
|
589
|
+
q.add 'lang', :includes, 'li'
|
590
|
+
},
|
591
|
+
false
|
592
|
+
).should.equal([
|
593
|
+
'pk1', 'pk3'
|
594
|
+
])
|
595
|
+
end
|
596
|
+
|
597
|
+
it 'can do UNION on queries (and fetch the results)' do
|
598
|
+
|
599
|
+
@t.union(
|
600
|
+
@t.prepare_query { |q|
|
601
|
+
q.add 'lang', :includes, 'es'
|
602
|
+
},
|
603
|
+
@t.prepare_query { |q|
|
604
|
+
q.add 'lang', :includes, 'li'
|
605
|
+
}
|
606
|
+
).should.equal(
|
607
|
+
{"pk1"=>{"name"=>"jeff", "lang"=>"en,es", "age"=>"32"}, "pk3"=>{"name"=>"jake", "lang"=>"en,li", "age"=>"45"}}
|
608
|
+
)
|
609
|
+
end
|
610
|
+
|
611
|
+
it 'can do INTERSECTION on queries' do
|
612
|
+
|
613
|
+
@t.intersection(
|
614
|
+
@t.prepare_query { |q|
|
615
|
+
q.add 'age', :gt, '30'
|
616
|
+
},
|
617
|
+
@t.prepare_query { |q|
|
618
|
+
q.add 'lang', :includes, 'li'
|
619
|
+
},
|
620
|
+
false
|
621
|
+
).should.equal([
|
622
|
+
'pk3'
|
623
|
+
])
|
624
|
+
end
|
625
|
+
|
626
|
+
it 'can do DIFFERENCE on queries' do
|
627
|
+
|
628
|
+
@t.difference(
|
629
|
+
@t.prepare_query { |q|
|
630
|
+
q.add 'age', :gt, '30'
|
631
|
+
},
|
632
|
+
@t.prepare_query { |q|
|
633
|
+
q.add 'lang', :includes, 'li'
|
634
|
+
},
|
635
|
+
false
|
636
|
+
).should.equal([
|
637
|
+
'pk1', 'pk2'
|
638
|
+
])
|
639
|
+
end
|
640
|
+
|
641
|
+
it 'can do meta with only one query' do
|
642
|
+
|
643
|
+
@t.difference(
|
644
|
+
@t.prepare_query { |q|
|
645
|
+
q.add 'age', :gt, '30'
|
646
|
+
},
|
647
|
+
false
|
648
|
+
).should.equal([
|
649
|
+
'pk1', 'pk2', 'pk3'
|
650
|
+
])
|
651
|
+
end
|
652
|
+
|
653
|
+
it 'should complain when there is no query' do
|
654
|
+
|
655
|
+
lambda {
|
656
|
+
@t.difference(false)
|
657
|
+
}.should.raise(ArgumentError)
|
658
|
+
end
|
659
|
+
|
660
|
+
it 'can do metasearch a la ruby-tokyotyrant' do
|
661
|
+
|
662
|
+
@t.search(
|
663
|
+
:difference,
|
664
|
+
@t.prepare_query { |q|
|
665
|
+
q.add 'age', :gt, '30'
|
666
|
+
},
|
667
|
+
@t.prepare_query { |q|
|
668
|
+
q.add 'lang', :includes, 'li'
|
669
|
+
}
|
670
|
+
).should.equal(
|
671
|
+
{"pk1"=>{"name"=>"jeff", "lang"=>"en,es", "age"=>"32"}, "pk2"=>{"name"=>"jack", "lang"=>"en", "age"=>"44"}}
|
672
|
+
)
|
673
|
+
end
|
674
|
+
end
|
675
|
+
|