lotus-rethinkdb 0.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +17 -0
- data/lib/lotus/model/adapters/rethinkdb/collection.rb +205 -0
- data/lib/lotus/model/adapters/rethinkdb/command.rb +66 -0
- data/lib/lotus/model/adapters/rethinkdb/query.rb +364 -0
- data/lib/lotus/model/adapters/rethinkdb_adapter.rb +234 -0
- data/lib/lotus/rethinkdb/version.rb +16 -0
- data/lib/lotus-rethinkdb.rb +2 -0
- data/lotus-rethinkdb.gemspec +29 -0
- data/test/model/adapters/rethinkdb/query_test.rb +0 -0
- data/test/model/adapters/rethinkdb_adapter_test.rb +562 -0
- data/test/test_helper.rb +29 -0
- data/test/version_test.rb +7 -0
- metadata +164 -0
@@ -0,0 +1,562 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::RethinkdbAdapter do
|
4
|
+
before do
|
5
|
+
# rubocop:disable Documentation
|
6
|
+
class TestUser
|
7
|
+
include Lotus::Entity
|
8
|
+
attributes :id, :name, :age
|
9
|
+
end
|
10
|
+
|
11
|
+
class TestUserRepository
|
12
|
+
include Lotus::Repository
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestDevice
|
16
|
+
include Lotus::Entity
|
17
|
+
attributes :id
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestDeviceRepository
|
21
|
+
include Lotus::Repository
|
22
|
+
end
|
23
|
+
# rubocop:enable Documentation
|
24
|
+
|
25
|
+
@mapper = Lotus::Model::Mapper.new do
|
26
|
+
collection :test_users do
|
27
|
+
entity TestUser
|
28
|
+
|
29
|
+
attribute :id, String
|
30
|
+
attribute :name, String
|
31
|
+
attribute :age, Integer
|
32
|
+
end
|
33
|
+
|
34
|
+
collection :test_devices do
|
35
|
+
entity TestDevice
|
36
|
+
|
37
|
+
attribute :id, String
|
38
|
+
end
|
39
|
+
end.load!
|
40
|
+
|
41
|
+
@adapter = Lotus::Model::Adapters::RethinkdbAdapter.new(
|
42
|
+
@mapper, RETHINKDB_TEST_CONNECTION
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
after do
|
47
|
+
Object.send(:remove_const, :TestUser)
|
48
|
+
Object.send(:remove_const, :TestUserRepository)
|
49
|
+
Object.send(:remove_const, :TestDevice)
|
50
|
+
Object.send(:remove_const, :TestDeviceRepository)
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:collection) { :test_users }
|
54
|
+
|
55
|
+
describe 'multiple collections' do
|
56
|
+
before do
|
57
|
+
@adapter.clear(:test_users)
|
58
|
+
@adapter.clear(:test_devices)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'create documents' do
|
62
|
+
user = TestUser.new
|
63
|
+
device = TestDevice.new
|
64
|
+
|
65
|
+
@adapter.create(:test_users, user)
|
66
|
+
@adapter.create(:test_devices, device)
|
67
|
+
|
68
|
+
@adapter.all(:test_users).must_equal [user]
|
69
|
+
@adapter.all(:test_devices).must_equal [device]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#persist' do
|
74
|
+
describe 'when the given entity is not persisted' do
|
75
|
+
let(:entity) { TestUser.new }
|
76
|
+
|
77
|
+
it 'stores the document and assigns an id' do
|
78
|
+
@adapter.persist(collection, entity)
|
79
|
+
|
80
|
+
entity.id.wont_be_nil
|
81
|
+
|
82
|
+
@adapter.find(collection, entity.id).must_equal entity
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'when the given entity is persisted' do
|
87
|
+
before do
|
88
|
+
@adapter.create(collection, entity)
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:entity) { TestUser.new }
|
92
|
+
|
93
|
+
it 'updates the document and leaves untouched the id' do
|
94
|
+
id = entity.id
|
95
|
+
id.wont_be_nil
|
96
|
+
|
97
|
+
entity.name = 'L'
|
98
|
+
@adapter.persist(collection, entity)
|
99
|
+
|
100
|
+
entity.id.must_equal id
|
101
|
+
@adapter.find(collection, entity.id).name.must_equal entity.name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#create' do
|
107
|
+
let(:entity) { TestUser.new }
|
108
|
+
|
109
|
+
it 'stores the document and assigns an id' do
|
110
|
+
@adapter.create(collection, entity)
|
111
|
+
|
112
|
+
entity.id.wont_be_nil
|
113
|
+
|
114
|
+
@adapter.find(collection, entity.id).must_equal entity
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#update' do
|
119
|
+
before do
|
120
|
+
@adapter.create(collection, entity)
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:entity) { TestUser.new(id: nil, name: 'L') }
|
124
|
+
|
125
|
+
it 'stores the changes and leave the id untouched' do
|
126
|
+
id = entity.id
|
127
|
+
|
128
|
+
entity.name = 'MG'
|
129
|
+
@adapter.update(collection, entity)
|
130
|
+
|
131
|
+
entity.id.must_equal id
|
132
|
+
@adapter.find(collection, entity.id).name.must_equal entity.name
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#delete' do
|
137
|
+
before do
|
138
|
+
@adapter.create(collection, entity)
|
139
|
+
end
|
140
|
+
|
141
|
+
let(:entity) { TestUser.new }
|
142
|
+
|
143
|
+
it 'removes the given identity' do
|
144
|
+
@adapter.delete(collection, entity)
|
145
|
+
@adapter.find(collection, entity.id).must_be_nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#all' do
|
150
|
+
describe 'when no documents are persisted' do
|
151
|
+
before do
|
152
|
+
@adapter.clear(collection)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'returns an empty collection' do
|
156
|
+
@adapter.all(collection).must_be_empty
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe 'when some documents are persisted' do
|
161
|
+
before do
|
162
|
+
@adapter.clear(collection)
|
163
|
+
@adapter.create(collection, entity)
|
164
|
+
end
|
165
|
+
|
166
|
+
let(:entity) { TestUser.new }
|
167
|
+
|
168
|
+
it 'returns all of them' do
|
169
|
+
@adapter.all(collection).must_equal [entity]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe '#find' do
|
175
|
+
before do
|
176
|
+
@adapter.create(collection, entity)
|
177
|
+
end
|
178
|
+
|
179
|
+
let(:entity) { TestUser.new }
|
180
|
+
|
181
|
+
it 'returns the document by id' do
|
182
|
+
@adapter.find(collection, entity.id).must_equal entity
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'returns nil when the document cannot be found' do
|
186
|
+
@adapter.find(collection, 1_000_000_000).must_be_nil
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'returns nil when the given id is nil' do
|
190
|
+
@adapter.find(collection, nil).must_be_nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#first' do
|
195
|
+
describe 'when no documents are persisted' do
|
196
|
+
before do
|
197
|
+
@adapter.clear(collection)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'raises an error' do
|
201
|
+
-> { @adapter.first(collection) }.must_raise NotImplementedError
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe 'when some documents are persisted' do
|
206
|
+
before do
|
207
|
+
@adapter.clear(:test_users)
|
208
|
+
@adapter.create(collection, entity1)
|
209
|
+
@adapter.create(collection, entity2)
|
210
|
+
end
|
211
|
+
|
212
|
+
let(:entity1) { TestUser.new }
|
213
|
+
let(:entity2) { TestUser.new }
|
214
|
+
|
215
|
+
it 'raises an error' do
|
216
|
+
-> { @adapter.first(collection) }.must_raise NotImplementedError
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe '#last' do
|
222
|
+
describe 'when no documents are persisted' do
|
223
|
+
before do
|
224
|
+
@adapter.clear(collection)
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'raises an error' do
|
228
|
+
-> { @adapter.last(collection) }.must_raise NotImplementedError
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe 'when some documents are persisted' do
|
233
|
+
before do
|
234
|
+
@adapter.clear(:test_users)
|
235
|
+
@adapter.create(collection, entity1)
|
236
|
+
@adapter.create(collection, entity2)
|
237
|
+
end
|
238
|
+
|
239
|
+
let(:entity1) { TestUser.new }
|
240
|
+
let(:entity2) { TestUser.new }
|
241
|
+
|
242
|
+
it 'raises an error' do
|
243
|
+
-> { @adapter.last(collection) }.must_raise NotImplementedError
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe '#clear' do
|
249
|
+
before do
|
250
|
+
@adapter.create(collection, entity)
|
251
|
+
end
|
252
|
+
|
253
|
+
let(:entity) { TestUser.new }
|
254
|
+
|
255
|
+
it 'removes all the documents' do
|
256
|
+
@adapter.clear(collection)
|
257
|
+
@adapter.all(collection).must_be_empty
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe '#query' do
|
262
|
+
before do
|
263
|
+
@adapter.clear(collection)
|
264
|
+
end
|
265
|
+
|
266
|
+
let(:user1) { TestUser.new(name: 'L', age: '32') }
|
267
|
+
let(:user2) { TestUser.new(name: 'MG', age: 31) }
|
268
|
+
|
269
|
+
describe 'where' do
|
270
|
+
describe 'with an empty collection' do
|
271
|
+
it 'returns an empty result set' do
|
272
|
+
result = @adapter.query(collection) do
|
273
|
+
where(id: 23)
|
274
|
+
end.all
|
275
|
+
|
276
|
+
result.must_be_empty
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe 'with a filled collection' do
|
281
|
+
before do
|
282
|
+
@adapter.create(collection, user1)
|
283
|
+
@adapter.create(collection, user2)
|
284
|
+
end
|
285
|
+
|
286
|
+
it 'returns selected records' do
|
287
|
+
id = user1.id
|
288
|
+
|
289
|
+
query = proc do
|
290
|
+
where(id: id)
|
291
|
+
end
|
292
|
+
|
293
|
+
result = @adapter.query(collection, &query).all
|
294
|
+
result.must_equal [user1]
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'can use multiple where conditions' do
|
298
|
+
id = user1.id
|
299
|
+
name = user1.name
|
300
|
+
|
301
|
+
query = proc do
|
302
|
+
where(id: id).where(name: name)
|
303
|
+
end
|
304
|
+
|
305
|
+
result = @adapter.query(collection, &query).all
|
306
|
+
result.must_equal [user1]
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'can use multiple where conditions with "and" alias' do
|
310
|
+
id = user1.id
|
311
|
+
name = user1.name
|
312
|
+
|
313
|
+
query = proc do
|
314
|
+
where(id: id).and(name: name)
|
315
|
+
end
|
316
|
+
|
317
|
+
result = @adapter.query(collection, &query).all
|
318
|
+
result.must_equal [user1]
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'can use a block' do
|
322
|
+
age = user1.age
|
323
|
+
|
324
|
+
query = proc do
|
325
|
+
where { |user| user['age'].eq(age) }
|
326
|
+
end
|
327
|
+
|
328
|
+
result = @adapter.query(collection, &query).all
|
329
|
+
result.must_equal [user1]
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'raises an error if you dont specify condition or block' do
|
333
|
+
lambda do
|
334
|
+
query = proc do
|
335
|
+
where
|
336
|
+
end
|
337
|
+
|
338
|
+
@adapter.query(collection, &query).all
|
339
|
+
end.must_raise(ArgumentError)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
describe 'pluck' do
|
345
|
+
describe 'with an empty collection' do
|
346
|
+
it 'returns an empty result' do
|
347
|
+
result = @adapter.query(collection) do
|
348
|
+
pluck(:age)
|
349
|
+
end.all
|
350
|
+
|
351
|
+
result.must_be_empty
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
describe 'with a filled collection' do
|
356
|
+
before do
|
357
|
+
@adapter.create(collection, user1)
|
358
|
+
@adapter.create(collection, user2)
|
359
|
+
@adapter.create(collection, user3)
|
360
|
+
end
|
361
|
+
|
362
|
+
let(:user1) { TestUser.new(name: 'L', age: 32) }
|
363
|
+
let(:user3) { TestUser.new(name: 'S') }
|
364
|
+
let(:users) { [user1, user2, user3] }
|
365
|
+
|
366
|
+
it 'returns the selected columns from all the records' do
|
367
|
+
query = proc do
|
368
|
+
pluck(:age)
|
369
|
+
end
|
370
|
+
|
371
|
+
result = @adapter.query(collection, &query).all
|
372
|
+
|
373
|
+
users.each do |user|
|
374
|
+
record = result.find { |r| r.age == user.age }
|
375
|
+
record.wont_be_nil
|
376
|
+
record.name.must_be_nil
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'returns only the select of requested records' do
|
381
|
+
name = user2.name
|
382
|
+
|
383
|
+
query = proc do
|
384
|
+
where(name: name).pluck(:age)
|
385
|
+
end
|
386
|
+
|
387
|
+
result = @adapter.query(collection, &query).all
|
388
|
+
|
389
|
+
record = result.first
|
390
|
+
record.age.must_equal(user2.age)
|
391
|
+
record.name.must_be_nil
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'returns only the multiple select of requested records' do
|
395
|
+
name = user2.name
|
396
|
+
|
397
|
+
query = proc do
|
398
|
+
where(name: name).pluck(:name, :age)
|
399
|
+
end
|
400
|
+
|
401
|
+
result = @adapter.query(collection, &query).all
|
402
|
+
|
403
|
+
record = result.first
|
404
|
+
record.name.must_equal(user2.name)
|
405
|
+
record.age.must_equal(user2.age)
|
406
|
+
record.id.must_be_nil
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe 'order' do
|
412
|
+
describe 'with an empty collection' do
|
413
|
+
it 'returns an empty result set' do
|
414
|
+
result = @adapter.query(collection) do
|
415
|
+
order(:id)
|
416
|
+
end.all
|
417
|
+
|
418
|
+
result.must_be_empty
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
describe 'with a filled collection' do
|
423
|
+
before do
|
424
|
+
@adapter.create(collection, user1)
|
425
|
+
@adapter.create(collection, user2)
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'returns sorted records' do
|
429
|
+
query = proc do
|
430
|
+
order(:age)
|
431
|
+
end
|
432
|
+
|
433
|
+
result = @adapter.query(collection, &query).all
|
434
|
+
result.must_equal [user2, user1]
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'returns sorted records, using multiple columns' do
|
438
|
+
query = proc do
|
439
|
+
order(:age, :name)
|
440
|
+
end
|
441
|
+
|
442
|
+
result = @adapter.query(collection, &query).all
|
443
|
+
result.must_equal [user2, user1]
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'returns sorted records, using multiple invokations' do
|
447
|
+
query = proc do
|
448
|
+
order(:age).order(:name)
|
449
|
+
end
|
450
|
+
|
451
|
+
result = @adapter.query(collection, &query).all
|
452
|
+
result.must_equal [user1, user2]
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
describe 'asc' do
|
458
|
+
describe 'with an empty collection' do
|
459
|
+
it 'returns an empty result set' do
|
460
|
+
result = @adapter.query(collection) do
|
461
|
+
asc(:id)
|
462
|
+
end.all
|
463
|
+
|
464
|
+
result.must_be_empty
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
describe 'with a filled collection' do
|
469
|
+
before do
|
470
|
+
@adapter.create(collection, user1)
|
471
|
+
@adapter.create(collection, user2)
|
472
|
+
end
|
473
|
+
|
474
|
+
it 'returns sorted records' do
|
475
|
+
query = proc do
|
476
|
+
asc(:age)
|
477
|
+
end
|
478
|
+
|
479
|
+
result = @adapter.query(collection, &query).all
|
480
|
+
result.must_equal [user2, user1]
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
describe 'desc' do
|
486
|
+
describe 'with an empty collection' do
|
487
|
+
it 'returns an empty result set' do
|
488
|
+
result = @adapter.query(collection) do
|
489
|
+
desc(:age)
|
490
|
+
end.all
|
491
|
+
|
492
|
+
result.must_be_empty
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
describe 'with a filled collection' do
|
497
|
+
before do
|
498
|
+
@adapter.create(collection, user1)
|
499
|
+
@adapter.create(collection, user2)
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'returns reverse sorted records' do
|
503
|
+
query = proc do
|
504
|
+
desc(:age)
|
505
|
+
end
|
506
|
+
|
507
|
+
result = @adapter.query(collection, &query).all
|
508
|
+
result.must_equal [user1, user2]
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'returns sorted records, using multiple columns' do
|
512
|
+
query = proc do
|
513
|
+
desc(:age, :name)
|
514
|
+
end
|
515
|
+
|
516
|
+
result = @adapter.query(collection, &query).all
|
517
|
+
result.must_equal [user1, user2]
|
518
|
+
end
|
519
|
+
|
520
|
+
it 'returns sorted records, using multiple invokations' do
|
521
|
+
query = proc do
|
522
|
+
desc(:age).desc(:name)
|
523
|
+
end
|
524
|
+
|
525
|
+
result = @adapter.query(collection, &query).all
|
526
|
+
result.must_equal [user2, user1]
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
describe 'limit' do
|
532
|
+
describe 'with an empty collection' do
|
533
|
+
it 'returns an empty result set' do
|
534
|
+
result = @adapter.query(collection) do
|
535
|
+
limit(1)
|
536
|
+
end.all
|
537
|
+
|
538
|
+
result.must_be_empty
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
describe 'with a filled collection' do
|
543
|
+
before do
|
544
|
+
@adapter.create(collection, user1)
|
545
|
+
@adapter.create(collection, user2)
|
546
|
+
@adapter.create(collection, TestUser.new(name: user2.name))
|
547
|
+
end
|
548
|
+
|
549
|
+
it 'returns only the number of requested records' do
|
550
|
+
name = user2.name
|
551
|
+
|
552
|
+
query = proc do
|
553
|
+
where(name: name).limit(1)
|
554
|
+
end
|
555
|
+
|
556
|
+
result = @adapter.query(collection, &query).all
|
557
|
+
result.length == 1
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
$LOAD_PATH.unshift 'lib'
|
6
|
+
require 'lotus-rethinkdb'
|
7
|
+
|
8
|
+
include RethinkDB::Shortcuts
|
9
|
+
|
10
|
+
RETHINKDB_TEST_CONNECTION = r.connect
|
11
|
+
|
12
|
+
def run
|
13
|
+
yield.run(RETHINKDB_TEST_CONNECTION)
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
run { r.table_create('test_users') }
|
18
|
+
rescue RethinkDB::RqlRuntimeError => _e
|
19
|
+
puts 'Table `test_users` already exists'
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
run { r.table_create('test_devices') }
|
24
|
+
rescue RethinkDB::RqlRuntimeError => _e
|
25
|
+
puts 'Table `test_devices` already exists'
|
26
|
+
end
|
27
|
+
|
28
|
+
run { r.table('test_users').delete }
|
29
|
+
run { r.table('test_devices').delete }
|