lotus-model 0.0.0 → 0.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/.yardopts +5 -0
- data/EXAMPLE.md +217 -0
- data/Gemfile +14 -2
- data/README.md +303 -3
- data/Rakefile +17 -1
- data/lib/lotus-model.rb +1 -0
- data/lib/lotus/entity.rb +157 -0
- data/lib/lotus/model.rb +23 -2
- data/lib/lotus/model/adapters/abstract.rb +167 -0
- data/lib/lotus/model/adapters/implementation.rb +111 -0
- data/lib/lotus/model/adapters/memory/collection.rb +132 -0
- data/lib/lotus/model/adapters/memory/command.rb +90 -0
- data/lib/lotus/model/adapters/memory/query.rb +457 -0
- data/lib/lotus/model/adapters/memory_adapter.rb +149 -0
- data/lib/lotus/model/adapters/sql/collection.rb +209 -0
- data/lib/lotus/model/adapters/sql/command.rb +67 -0
- data/lib/lotus/model/adapters/sql/query.rb +615 -0
- data/lib/lotus/model/adapters/sql_adapter.rb +154 -0
- data/lib/lotus/model/mapper.rb +101 -0
- data/lib/lotus/model/mapping.rb +23 -0
- data/lib/lotus/model/mapping/coercer.rb +80 -0
- data/lib/lotus/model/mapping/collection.rb +336 -0
- data/lib/lotus/model/version.rb +4 -1
- data/lib/lotus/repository.rb +620 -0
- data/lotus-model.gemspec +15 -11
- data/test/entity_test.rb +126 -0
- data/test/fixtures.rb +81 -0
- data/test/model/adapters/abstract_test.rb +75 -0
- data/test/model/adapters/implementation_test.rb +22 -0
- data/test/model/adapters/memory/query_test.rb +91 -0
- data/test/model/adapters/memory_adapter_test.rb +1044 -0
- data/test/model/adapters/sql/query_test.rb +121 -0
- data/test/model/adapters/sql_adapter_test.rb +1078 -0
- data/test/model/mapper_test.rb +94 -0
- data/test/model/mapping/coercer_test.rb +27 -0
- data/test/model/mapping/collection_test.rb +82 -0
- data/test/repository_test.rb +283 -0
- data/test/test_helper.rb +30 -0
- data/test/version_test.rb +7 -0
- metadata +109 -11
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::Sql::Query do
|
4
|
+
before do
|
5
|
+
@query = Lotus::Model::Adapters::Sql::Query.new(collection)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:collection) { [] }
|
9
|
+
|
10
|
+
describe '#negate!' do
|
11
|
+
describe 'where' do
|
12
|
+
before do
|
13
|
+
@query.where(id: 1)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'negates with exclude' do
|
17
|
+
@query.negate!
|
18
|
+
operator, condition = *@query.conditions.first
|
19
|
+
|
20
|
+
operator.must_equal :exclude
|
21
|
+
condition.must_equal({id: 1})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'multipe where conditions' do
|
26
|
+
before do
|
27
|
+
@query.where(id: 1).and(name: 'L')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'negates with exclude' do
|
31
|
+
@query.negate!
|
32
|
+
operator, condition = *@query.conditions.last
|
33
|
+
|
34
|
+
operator.must_equal :exclude
|
35
|
+
condition.must_equal({name: 'L'})
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'exclude' do
|
40
|
+
before do
|
41
|
+
@query.exclude(published: false)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'negates with where' do
|
45
|
+
@query.negate!
|
46
|
+
operator, condition = *@query.conditions.first
|
47
|
+
|
48
|
+
operator.must_equal :where
|
49
|
+
condition.must_equal({published: false})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'multiple exclude conditions' do
|
54
|
+
before do
|
55
|
+
@query.exclude(published: false).not(comments_count: 0)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'negates with where' do
|
59
|
+
@query.negate!
|
60
|
+
operator, condition = *@query.conditions.last
|
61
|
+
|
62
|
+
operator.must_equal :where
|
63
|
+
condition.must_equal({comments_count: 0})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#to_s' do
|
69
|
+
let(:collection) { [1, 2, 3] }
|
70
|
+
|
71
|
+
it 'delegates to the wrapped collection' do
|
72
|
+
@query.to_s.must_equal collection.to_s
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#empty?' do
|
77
|
+
describe "when it's empty" do
|
78
|
+
it 'returns true' do
|
79
|
+
@query.must_be_empty
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "when it's filled with elements" do
|
84
|
+
let(:collection) { [1, 2, 3] }
|
85
|
+
|
86
|
+
it 'returns false' do
|
87
|
+
@query.wont_be_empty
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#any?' do
|
93
|
+
describe "when it's empty" do
|
94
|
+
it 'returns false' do
|
95
|
+
assert !@query.any?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "when it's filled with elements" do
|
100
|
+
let(:collection) { [1, 2, 3] }
|
101
|
+
|
102
|
+
it 'returns true' do
|
103
|
+
assert @query.any?
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "when a block is passed" do
|
107
|
+
describe "and it doesn't match elements" do
|
108
|
+
it 'returns false' do
|
109
|
+
assert !@query.any? {|e| e > 100 }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "and it matches elements" do
|
114
|
+
it 'returns true' do
|
115
|
+
assert @query.any? {|e| e % 2 == 0 }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,1078 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Lotus::Model::Adapters::SqlAdapter do
|
4
|
+
before do
|
5
|
+
TestUser = Struct.new(:id, :name, :age) do
|
6
|
+
include Lotus::Entity
|
7
|
+
end
|
8
|
+
|
9
|
+
TestDevice = Struct.new(:id) do
|
10
|
+
include Lotus::Entity
|
11
|
+
end
|
12
|
+
|
13
|
+
@mapper = Lotus::Model::Mapper.new do
|
14
|
+
collection :users do
|
15
|
+
entity TestUser
|
16
|
+
|
17
|
+
attribute :id, Integer
|
18
|
+
attribute :name, String
|
19
|
+
attribute :age, Integer
|
20
|
+
end
|
21
|
+
|
22
|
+
collection :devices do
|
23
|
+
entity TestDevice
|
24
|
+
|
25
|
+
attribute :id, Integer
|
26
|
+
end
|
27
|
+
end.load!
|
28
|
+
|
29
|
+
@adapter = Lotus::Model::Adapters::SqlAdapter.new(@mapper, SQLITE_CONNECTION_STRING)
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
Object.send(:remove_const, :TestUser)
|
34
|
+
Object.send(:remove_const, :TestDevice)
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:collection) { :users }
|
38
|
+
|
39
|
+
describe 'multiple collections' do
|
40
|
+
it 'create records' do
|
41
|
+
user = TestUser.new
|
42
|
+
device = TestDevice.new
|
43
|
+
|
44
|
+
@adapter.create(:users, user)
|
45
|
+
@adapter.create(:devices, device)
|
46
|
+
|
47
|
+
@adapter.all(:users).must_equal [user]
|
48
|
+
@adapter.all(:devices).must_equal [device]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#initialize' do
|
53
|
+
it 'raises an error when the given URI refers to a non registered database adapter' do
|
54
|
+
-> {
|
55
|
+
Lotus::Model::Adapters::SqlAdapter.new(@mapper, 'postgres://host')
|
56
|
+
}.must_raise(Lotus::Model::Adapters::DatabaseAdapterNotFound)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises an error when the given URI refers to an unknown database adapter' do
|
60
|
+
-> {
|
61
|
+
Lotus::Model::Adapters::SqlAdapter.new(@mapper, 'unknown://host')
|
62
|
+
}.must_raise(Lotus::Model::Adapters::DatabaseAdapterNotFound)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'raises an error when the given URI is malformed' do
|
66
|
+
-> {
|
67
|
+
Lotus::Model::Adapters::SqlAdapter.new(@mapper, 'unknown_db:host')
|
68
|
+
}.must_raise(URI::InvalidURIError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#persist' do
|
73
|
+
describe 'when the given entity is not persisted' do
|
74
|
+
let(:entity) { TestUser.new }
|
75
|
+
|
76
|
+
it 'stores the record and assigns an id' do
|
77
|
+
@adapter.persist(collection, entity)
|
78
|
+
|
79
|
+
entity.id.wont_be_nil
|
80
|
+
@adapter.find(collection, entity.id).must_equal entity
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'when the given entity is persisted' do
|
85
|
+
before do
|
86
|
+
@adapter.create(collection, entity)
|
87
|
+
end
|
88
|
+
|
89
|
+
let(:entity) { TestUser.new }
|
90
|
+
|
91
|
+
it 'updates the record and leaves untouched the id' do
|
92
|
+
id = entity.id
|
93
|
+
id.wont_be_nil
|
94
|
+
|
95
|
+
entity.name = 'L'
|
96
|
+
@adapter.persist(collection, entity)
|
97
|
+
|
98
|
+
entity.id.must_equal(id)
|
99
|
+
@adapter.find(collection, entity.id).must_equal entity
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#create' do
|
105
|
+
let(:entity) { TestUser.new }
|
106
|
+
|
107
|
+
it 'stores the record and assigns an id' do
|
108
|
+
@adapter.create(collection, entity)
|
109
|
+
|
110
|
+
entity.id.wont_be_nil
|
111
|
+
@adapter.find(collection, entity.id).must_equal entity
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#update' do
|
116
|
+
before do
|
117
|
+
@adapter.create(collection, entity)
|
118
|
+
end
|
119
|
+
|
120
|
+
let(:entity) { TestUser.new(id: nil, name: 'L') }
|
121
|
+
|
122
|
+
it 'stores the changes and leave the id untouched' do
|
123
|
+
id = entity.id
|
124
|
+
|
125
|
+
entity.name = 'MG'
|
126
|
+
@adapter.update(collection, entity)
|
127
|
+
|
128
|
+
entity.id.must_equal id
|
129
|
+
@adapter.find(collection, entity.id).must_equal entity
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#delete' do
|
134
|
+
before do
|
135
|
+
@adapter.create(collection, entity)
|
136
|
+
end
|
137
|
+
|
138
|
+
let(:entity) { TestUser.new }
|
139
|
+
|
140
|
+
it 'removes the given identity' do
|
141
|
+
@adapter.delete(collection, entity)
|
142
|
+
@adapter.find(collection, entity.id).must_be_nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#all' do
|
147
|
+
describe 'when no records are persisted' do
|
148
|
+
before do
|
149
|
+
@adapter.clear(collection)
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'returns an empty collection' do
|
153
|
+
@adapter.all(collection).must_be_empty
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'when some records are persisted' do
|
158
|
+
before do
|
159
|
+
@adapter.create(collection, entity)
|
160
|
+
end
|
161
|
+
|
162
|
+
let(:entity) { TestUser.new }
|
163
|
+
|
164
|
+
it 'returns all of them' do
|
165
|
+
@adapter.all(collection).must_equal [entity]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe '#find' do
|
171
|
+
before do
|
172
|
+
@adapter.create(collection, entity)
|
173
|
+
end
|
174
|
+
|
175
|
+
let(:entity) { TestUser.new }
|
176
|
+
|
177
|
+
it 'returns the record by id' do
|
178
|
+
@adapter.find(collection, entity.id).must_equal entity
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'returns nil when the record cannot be found' do
|
182
|
+
@adapter.find(collection, 1_000_000).must_be_nil
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'returns nil when the given id is nil' do
|
186
|
+
@adapter.find(collection, nil).must_be_nil
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe '#first' do
|
191
|
+
describe 'when no records are peristed' do
|
192
|
+
before do
|
193
|
+
@adapter.clear(collection)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'returns nil' do
|
197
|
+
@adapter.first(collection).must_be_nil
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe 'when some records are persisted' do
|
202
|
+
before do
|
203
|
+
@adapter.create(collection, entity1)
|
204
|
+
@adapter.create(collection, entity2)
|
205
|
+
end
|
206
|
+
|
207
|
+
let(:entity1) { TestUser.new }
|
208
|
+
let(:entity2) { TestUser.new }
|
209
|
+
|
210
|
+
it 'returns the first record' do
|
211
|
+
@adapter.first(collection).must_equal entity1
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#last' do
|
217
|
+
describe 'when no records are peristed' do
|
218
|
+
before do
|
219
|
+
@adapter.clear(collection)
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'returns nil' do
|
223
|
+
@adapter.last(collection).must_be_nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe 'when some records are persisted' do
|
228
|
+
before do
|
229
|
+
@adapter.create(collection, entity1)
|
230
|
+
@adapter.create(collection, entity2)
|
231
|
+
end
|
232
|
+
|
233
|
+
let(:entity1) { TestUser.new }
|
234
|
+
let(:entity2) { TestUser.new }
|
235
|
+
|
236
|
+
it 'returns the last record' do
|
237
|
+
@adapter.last(collection).must_equal entity2
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe '#clear' do
|
243
|
+
before do
|
244
|
+
@adapter.create(collection, entity)
|
245
|
+
end
|
246
|
+
|
247
|
+
let(:entity) { TestUser.new }
|
248
|
+
|
249
|
+
it 'removes all the records' do
|
250
|
+
@adapter.clear(collection)
|
251
|
+
@adapter.all(collection).must_be_empty
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe '#query' do
|
256
|
+
before do
|
257
|
+
@adapter.clear(collection)
|
258
|
+
end
|
259
|
+
|
260
|
+
let(:user1) { TestUser.new(name: 'L', age: '32') }
|
261
|
+
let(:user2) { TestUser.new(name: 'MG', age: 31) }
|
262
|
+
|
263
|
+
describe 'where' do
|
264
|
+
describe 'with an empty collection' do
|
265
|
+
it 'returns an empty result set' do
|
266
|
+
result = @adapter.query(collection) do
|
267
|
+
where(id: 23)
|
268
|
+
end.all
|
269
|
+
|
270
|
+
result.must_be_empty
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe 'with a filled collection' do
|
275
|
+
before do
|
276
|
+
@adapter.create(collection, user1)
|
277
|
+
@adapter.create(collection, user2)
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'returns selected records' do
|
281
|
+
id = user1.id
|
282
|
+
|
283
|
+
query = Proc.new {
|
284
|
+
where(id: id)
|
285
|
+
}
|
286
|
+
|
287
|
+
result = @adapter.query(collection, &query).all
|
288
|
+
result.must_equal [user1]
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'can use multiple where conditions' do
|
292
|
+
id = user1.id
|
293
|
+
name = user1.name
|
294
|
+
|
295
|
+
query = Proc.new {
|
296
|
+
where(id: id).where(name: name)
|
297
|
+
}
|
298
|
+
|
299
|
+
result = @adapter.query(collection, &query).all
|
300
|
+
result.must_equal [user1]
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'can use multiple where conditions with "and" alias' do
|
304
|
+
id = user1.id
|
305
|
+
name = user1.name
|
306
|
+
|
307
|
+
query = Proc.new {
|
308
|
+
where(id: id).and(name: name)
|
309
|
+
}
|
310
|
+
|
311
|
+
result = @adapter.query(collection, &query).all
|
312
|
+
result.must_equal [user1]
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
describe 'exclude' do
|
318
|
+
describe 'with an empty collection' do
|
319
|
+
it 'returns an empty result set' do
|
320
|
+
result = @adapter.query(collection) do
|
321
|
+
exclude(id: 23)
|
322
|
+
end.all
|
323
|
+
|
324
|
+
result.must_be_empty
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe 'with a filled collection' do
|
329
|
+
before do
|
330
|
+
@adapter.create(collection, user1)
|
331
|
+
@adapter.create(collection, user2)
|
332
|
+
@adapter.create(collection, user3)
|
333
|
+
end
|
334
|
+
|
335
|
+
let(:user3) { TestUser.new(name: 'S', age: 2) }
|
336
|
+
|
337
|
+
it 'returns selected records' do
|
338
|
+
id = user1.id
|
339
|
+
|
340
|
+
query = Proc.new {
|
341
|
+
exclude(id: id)
|
342
|
+
}
|
343
|
+
|
344
|
+
result = @adapter.query(collection, &query).all
|
345
|
+
result.must_equal [user2, user3]
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'can use multiple exclude conditions' do
|
349
|
+
id = user1.id
|
350
|
+
name = user2.name
|
351
|
+
|
352
|
+
query = Proc.new {
|
353
|
+
exclude(id: id).exclude(name: name)
|
354
|
+
}
|
355
|
+
|
356
|
+
result = @adapter.query(collection, &query).all
|
357
|
+
result.must_equal [user3]
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'can use multiple exclude conditions with "not" alias' do
|
361
|
+
id = user1.id
|
362
|
+
name = user2.name
|
363
|
+
|
364
|
+
query = Proc.new {
|
365
|
+
self.not(id: id).not(name: name)
|
366
|
+
}
|
367
|
+
|
368
|
+
result = @adapter.query(collection, &query).all
|
369
|
+
result.must_equal [user3]
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
describe 'or' do
|
375
|
+
describe 'with an empty collection' do
|
376
|
+
it 'returns an empty result set' do
|
377
|
+
result = @adapter.query(collection) do
|
378
|
+
where(name: 'L').or(name: 'MG')
|
379
|
+
end.all
|
380
|
+
|
381
|
+
result.must_be_empty
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
describe 'with a filled collection' do
|
386
|
+
before do
|
387
|
+
@adapter.create(collection, user1)
|
388
|
+
@adapter.create(collection, user2)
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'returns selected records' do
|
392
|
+
name1 = user1.name
|
393
|
+
name2 = user2.name
|
394
|
+
|
395
|
+
query = Proc.new {
|
396
|
+
where(name: name1).or(name: name2)
|
397
|
+
}
|
398
|
+
|
399
|
+
result = @adapter.query(collection, &query).all
|
400
|
+
result.must_equal [user1, user2]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
describe 'select' do
|
406
|
+
describe 'with an empty collection' do
|
407
|
+
it 'returns an empty result' do
|
408
|
+
result = @adapter.query(collection) do
|
409
|
+
select(:age)
|
410
|
+
end.all
|
411
|
+
|
412
|
+
result.must_be_empty
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
describe 'with a filled collection' do
|
417
|
+
before do
|
418
|
+
@adapter.create(collection, user1)
|
419
|
+
@adapter.create(collection, user2)
|
420
|
+
@adapter.create(collection, user3)
|
421
|
+
end
|
422
|
+
|
423
|
+
let(:user1) { TestUser.new(name: 'L', age: 32) }
|
424
|
+
let(:user3) { TestUser.new(name: 'S') }
|
425
|
+
let(:users) { [user1, user2, user3] }
|
426
|
+
|
427
|
+
it 'returns the selected columnts from all the records' do
|
428
|
+
query = Proc.new {
|
429
|
+
select(:age)
|
430
|
+
}
|
431
|
+
|
432
|
+
result = @adapter.query(collection, &query).all
|
433
|
+
|
434
|
+
users.each do |user|
|
435
|
+
record = result.find {|r| r.age == user.age }
|
436
|
+
record.wont_be_nil
|
437
|
+
record.name.must_be_nil
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
it 'returns only the select of requested records' do
|
442
|
+
name = user2.name
|
443
|
+
|
444
|
+
query = Proc.new {
|
445
|
+
where(name: name).select(:age)
|
446
|
+
}
|
447
|
+
|
448
|
+
result = @adapter.query(collection, &query).all
|
449
|
+
|
450
|
+
record = result.first
|
451
|
+
record.age.must_equal(user2.age)
|
452
|
+
record.name.must_be_nil
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'returns only the multiple select of requested records' do
|
456
|
+
name = user2.name
|
457
|
+
|
458
|
+
query = Proc.new {
|
459
|
+
where(name: name).select(:name, :age)
|
460
|
+
}
|
461
|
+
|
462
|
+
result = @adapter.query(collection, &query).all
|
463
|
+
|
464
|
+
record = result.first
|
465
|
+
record.name.must_equal(user2.name)
|
466
|
+
record.age.must_equal(user2.age)
|
467
|
+
record.id.must_be_nil
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe 'order' do
|
473
|
+
describe 'with an empty collection' do
|
474
|
+
it 'returns an empty result set' do
|
475
|
+
result = @adapter.query(collection) do
|
476
|
+
order(:id)
|
477
|
+
end.all
|
478
|
+
|
479
|
+
result.must_be_empty
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
describe 'with a filled collection' do
|
484
|
+
before do
|
485
|
+
@adapter.create(collection, user1)
|
486
|
+
@adapter.create(collection, user2)
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'returns sorted records' do
|
490
|
+
query = Proc.new {
|
491
|
+
order(:id)
|
492
|
+
}
|
493
|
+
|
494
|
+
result = @adapter.query(collection, &query).all
|
495
|
+
result.must_equal [user1, user2]
|
496
|
+
end
|
497
|
+
|
498
|
+
it 'returns sorted records, using multiple columns' do
|
499
|
+
query = Proc.new {
|
500
|
+
order(:age, :id)
|
501
|
+
}
|
502
|
+
|
503
|
+
result = @adapter.query(collection, &query).all
|
504
|
+
result.must_equal [user2, user1]
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'returns sorted records, using multiple invokations' do
|
508
|
+
query = Proc.new {
|
509
|
+
order(:age).order(:id)
|
510
|
+
}
|
511
|
+
|
512
|
+
result = @adapter.query(collection, &query).all
|
513
|
+
result.must_equal [user2, user1]
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
describe 'asc' do
|
519
|
+
describe 'with an empty collection' do
|
520
|
+
it 'returns an empty result set' do
|
521
|
+
result = @adapter.query(collection) do
|
522
|
+
asc(:id)
|
523
|
+
end.all
|
524
|
+
|
525
|
+
result.must_be_empty
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
describe 'with a filled collection' do
|
530
|
+
before do
|
531
|
+
@adapter.create(collection, user1)
|
532
|
+
@adapter.create(collection, user2)
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'returns sorted records' do
|
536
|
+
query = Proc.new {
|
537
|
+
asc(:id)
|
538
|
+
}
|
539
|
+
|
540
|
+
result = @adapter.query(collection, &query).all
|
541
|
+
result.must_equal [user1, user2]
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
describe 'desc' do
|
547
|
+
describe 'with an empty collection' do
|
548
|
+
it 'returns an empty result set' do
|
549
|
+
result = @adapter.query(collection) do
|
550
|
+
desc(:id)
|
551
|
+
end.all
|
552
|
+
|
553
|
+
result.must_be_empty
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
describe 'with a filled collection' do
|
558
|
+
before do
|
559
|
+
@adapter.create(collection, user1)
|
560
|
+
@adapter.create(collection, user2)
|
561
|
+
end
|
562
|
+
|
563
|
+
it 'returns reverse sorted records' do
|
564
|
+
query = Proc.new {
|
565
|
+
desc(:id)
|
566
|
+
}
|
567
|
+
|
568
|
+
result = @adapter.query(collection, &query).all
|
569
|
+
result.must_equal [user2, user1]
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'returns sorted records, using multiple columns' do
|
573
|
+
query = Proc.new {
|
574
|
+
desc(:age, :id)
|
575
|
+
}
|
576
|
+
|
577
|
+
result = @adapter.query(collection, &query).all
|
578
|
+
result.must_equal [user1, user2]
|
579
|
+
end
|
580
|
+
|
581
|
+
it 'returns sorted records, using multiple invokations' do
|
582
|
+
query = Proc.new {
|
583
|
+
desc(:age).desc(:id)
|
584
|
+
}
|
585
|
+
|
586
|
+
result = @adapter.query(collection, &query).all
|
587
|
+
result.must_equal [user1, user2]
|
588
|
+
end
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
describe 'limit' do
|
593
|
+
describe 'with an empty collection' do
|
594
|
+
it 'returns an empty result set' do
|
595
|
+
result = @adapter.query(collection) do
|
596
|
+
limit(1)
|
597
|
+
end.all
|
598
|
+
|
599
|
+
result.must_be_empty
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
describe 'with a filled collection' do
|
604
|
+
before do
|
605
|
+
@adapter.create(collection, user1)
|
606
|
+
@adapter.create(collection, user2)
|
607
|
+
@adapter.create(collection, TestUser.new(name: user2.name))
|
608
|
+
end
|
609
|
+
|
610
|
+
it 'returns only the number of requested records' do
|
611
|
+
name = user2.name
|
612
|
+
|
613
|
+
query = Proc.new {
|
614
|
+
where(name: name).limit(1)
|
615
|
+
}
|
616
|
+
|
617
|
+
result = @adapter.query(collection, &query).all
|
618
|
+
result.must_equal [user2]
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
describe 'offset' do
|
624
|
+
describe 'with an empty collection' do
|
625
|
+
it 'returns an empty result set' do
|
626
|
+
result = @adapter.query(collection) do
|
627
|
+
limit(1).offset(1)
|
628
|
+
end.all
|
629
|
+
|
630
|
+
result.must_be_empty
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
describe 'with a filled collection' do
|
635
|
+
before do
|
636
|
+
@adapter.create(collection, user1)
|
637
|
+
@adapter.create(collection, user2)
|
638
|
+
@adapter.create(collection, user3)
|
639
|
+
end
|
640
|
+
|
641
|
+
let(:user3) { TestUser.new(name: user2.name) }
|
642
|
+
|
643
|
+
it 'returns only the number of requested records' do
|
644
|
+
name = user2.name
|
645
|
+
|
646
|
+
query = Proc.new {
|
647
|
+
where(name: name).limit(1).offset(1)
|
648
|
+
}
|
649
|
+
|
650
|
+
result = @adapter.query(collection, &query).all
|
651
|
+
result.must_equal [user3]
|
652
|
+
end
|
653
|
+
end
|
654
|
+
end
|
655
|
+
|
656
|
+
describe 'exist?' do
|
657
|
+
describe 'with an empty collection' do
|
658
|
+
it 'returns false' do
|
659
|
+
result = @adapter.query(collection) do
|
660
|
+
where(id: 23)
|
661
|
+
end.exist?
|
662
|
+
|
663
|
+
result.must_equal false
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
describe 'with a filled collection' do
|
668
|
+
before do
|
669
|
+
@adapter.create(collection, user1)
|
670
|
+
@adapter.create(collection, user2)
|
671
|
+
end
|
672
|
+
|
673
|
+
it 'returns true when there are matched records' do
|
674
|
+
id = user1.id
|
675
|
+
|
676
|
+
query = Proc.new {
|
677
|
+
where(id: id)
|
678
|
+
}
|
679
|
+
|
680
|
+
result = @adapter.query(collection, &query).exist?
|
681
|
+
result.must_equal true
|
682
|
+
end
|
683
|
+
|
684
|
+
it 'returns false when there are matched records' do
|
685
|
+
query = Proc.new {
|
686
|
+
where(id: 'unknown')
|
687
|
+
}
|
688
|
+
|
689
|
+
result = @adapter.query(collection, &query).exist?
|
690
|
+
result.must_equal false
|
691
|
+
end
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
describe 'count' do
|
696
|
+
describe 'with an empty collection' do
|
697
|
+
it 'returns 0' do
|
698
|
+
result = @adapter.query(collection) do
|
699
|
+
all
|
700
|
+
end.count
|
701
|
+
|
702
|
+
result.must_equal 0
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
describe 'with a filled collection' do
|
707
|
+
before do
|
708
|
+
@adapter.create(collection, user1)
|
709
|
+
@adapter.create(collection, user2)
|
710
|
+
end
|
711
|
+
|
712
|
+
it 'returns the count of all the records' do
|
713
|
+
query = Proc.new {
|
714
|
+
all
|
715
|
+
}
|
716
|
+
|
717
|
+
result = @adapter.query(collection, &query).count
|
718
|
+
result.must_equal 2
|
719
|
+
end
|
720
|
+
|
721
|
+
it 'returns the count from an empty query block' do
|
722
|
+
query = Proc.new {
|
723
|
+
}
|
724
|
+
|
725
|
+
result = @adapter.query(collection, &query).count
|
726
|
+
result.must_equal 2
|
727
|
+
end
|
728
|
+
|
729
|
+
it 'returns only the count of requested records' do
|
730
|
+
name = user2.name
|
731
|
+
|
732
|
+
query = Proc.new {
|
733
|
+
where(name: name)
|
734
|
+
}
|
735
|
+
|
736
|
+
result = @adapter.query(collection, &query).count
|
737
|
+
result.must_equal 1
|
738
|
+
end
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
describe 'sum' do
|
743
|
+
describe 'with an empty collection' do
|
744
|
+
it 'returns nil' do
|
745
|
+
result = @adapter.query(collection) do
|
746
|
+
all
|
747
|
+
end.sum(:age)
|
748
|
+
|
749
|
+
result.must_be_nil
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
describe 'with a filled collection' do
|
754
|
+
before do
|
755
|
+
@adapter.create(collection, user1)
|
756
|
+
@adapter.create(collection, user2)
|
757
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
758
|
+
end
|
759
|
+
|
760
|
+
it 'returns the sum of all the records' do
|
761
|
+
query = Proc.new {
|
762
|
+
all
|
763
|
+
}
|
764
|
+
|
765
|
+
result = @adapter.query(collection, &query).sum(:age)
|
766
|
+
result.must_equal 63
|
767
|
+
end
|
768
|
+
|
769
|
+
it 'returns the sum from an empty query block' do
|
770
|
+
query = Proc.new {
|
771
|
+
}
|
772
|
+
|
773
|
+
result = @adapter.query(collection, &query).sum(:age)
|
774
|
+
result.must_equal 63
|
775
|
+
end
|
776
|
+
|
777
|
+
it 'returns only the sum of requested records' do
|
778
|
+
name = user2.name
|
779
|
+
|
780
|
+
query = Proc.new {
|
781
|
+
where(name: name)
|
782
|
+
}
|
783
|
+
|
784
|
+
result = @adapter.query(collection, &query).sum(:age)
|
785
|
+
result.must_equal 31
|
786
|
+
end
|
787
|
+
end
|
788
|
+
end
|
789
|
+
|
790
|
+
describe 'average' do
|
791
|
+
describe 'with an empty collection' do
|
792
|
+
it 'returns nil' do
|
793
|
+
result = @adapter.query(collection) do
|
794
|
+
all
|
795
|
+
end.average(:age)
|
796
|
+
|
797
|
+
result.must_be_nil
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
describe 'with a filled collection' do
|
802
|
+
before do
|
803
|
+
@adapter.create(collection, user1)
|
804
|
+
@adapter.create(collection, user2)
|
805
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
806
|
+
end
|
807
|
+
|
808
|
+
it 'returns the average of all the records' do
|
809
|
+
query = Proc.new {
|
810
|
+
all
|
811
|
+
}
|
812
|
+
|
813
|
+
result = @adapter.query(collection, &query).average(:age)
|
814
|
+
result.must_equal 31.5
|
815
|
+
end
|
816
|
+
|
817
|
+
it 'returns the average from an empty query block' do
|
818
|
+
query = Proc.new {
|
819
|
+
}
|
820
|
+
|
821
|
+
result = @adapter.query(collection, &query).average(:age)
|
822
|
+
result.must_equal 31.5
|
823
|
+
end
|
824
|
+
|
825
|
+
it 'returns only the average of requested records' do
|
826
|
+
name = user2.name
|
827
|
+
|
828
|
+
query = Proc.new {
|
829
|
+
where(name: name)
|
830
|
+
}
|
831
|
+
|
832
|
+
result = @adapter.query(collection, &query).average(:age)
|
833
|
+
result.must_equal 31
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
describe 'avg' do
|
839
|
+
describe 'with an empty collection' do
|
840
|
+
it 'returns nil' do
|
841
|
+
result = @adapter.query(collection) do
|
842
|
+
all
|
843
|
+
end.avg(:age)
|
844
|
+
|
845
|
+
result.must_be_nil
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
describe 'with a filled collection' do
|
850
|
+
before do
|
851
|
+
@adapter.create(collection, user1)
|
852
|
+
@adapter.create(collection, user2)
|
853
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
854
|
+
end
|
855
|
+
|
856
|
+
it 'returns the average of all the records' do
|
857
|
+
query = Proc.new {
|
858
|
+
all
|
859
|
+
}
|
860
|
+
|
861
|
+
result = @adapter.query(collection, &query).avg(:age)
|
862
|
+
result.must_equal 31.5
|
863
|
+
end
|
864
|
+
|
865
|
+
it 'returns the average from an empty query block' do
|
866
|
+
query = Proc.new {
|
867
|
+
}
|
868
|
+
|
869
|
+
result = @adapter.query(collection, &query).avg(:age)
|
870
|
+
result.must_equal 31.5
|
871
|
+
end
|
872
|
+
|
873
|
+
it 'returns only the average of requested records' do
|
874
|
+
name = user2.name
|
875
|
+
|
876
|
+
query = Proc.new {
|
877
|
+
where(name: name)
|
878
|
+
}
|
879
|
+
|
880
|
+
result = @adapter.query(collection, &query).avg(:age)
|
881
|
+
result.must_equal 31
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
describe 'max' do
|
887
|
+
describe 'with an empty collection' do
|
888
|
+
it 'returns nil' do
|
889
|
+
result = @adapter.query(collection) do
|
890
|
+
all
|
891
|
+
end.max(:age)
|
892
|
+
|
893
|
+
result.must_be_nil
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
describe 'with a filled collection' do
|
898
|
+
before do
|
899
|
+
@adapter.create(collection, user1)
|
900
|
+
@adapter.create(collection, user2)
|
901
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
902
|
+
end
|
903
|
+
|
904
|
+
it 'returns the maximum of all the records' do
|
905
|
+
query = Proc.new {
|
906
|
+
all
|
907
|
+
}
|
908
|
+
|
909
|
+
result = @adapter.query(collection, &query).max(:age)
|
910
|
+
result.must_equal 32
|
911
|
+
end
|
912
|
+
|
913
|
+
it 'returns the maximum from an empty query block' do
|
914
|
+
query = Proc.new {
|
915
|
+
}
|
916
|
+
|
917
|
+
result = @adapter.query(collection, &query).max(:age)
|
918
|
+
result.must_equal 32
|
919
|
+
end
|
920
|
+
|
921
|
+
it 'returns only the maximum of requested records' do
|
922
|
+
name = user2.name
|
923
|
+
|
924
|
+
query = Proc.new {
|
925
|
+
where(name: name)
|
926
|
+
}
|
927
|
+
|
928
|
+
result = @adapter.query(collection, &query).max(:age)
|
929
|
+
result.must_equal 31
|
930
|
+
end
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
describe 'min' do
|
935
|
+
describe 'with an empty collection' do
|
936
|
+
it 'returns nil' do
|
937
|
+
result = @adapter.query(collection) do
|
938
|
+
all
|
939
|
+
end.min(:age)
|
940
|
+
|
941
|
+
result.must_be_nil
|
942
|
+
end
|
943
|
+
end
|
944
|
+
|
945
|
+
describe 'with a filled collection' do
|
946
|
+
before do
|
947
|
+
@adapter.create(collection, user1)
|
948
|
+
@adapter.create(collection, user2)
|
949
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
950
|
+
end
|
951
|
+
|
952
|
+
it 'returns the minimum of all the records' do
|
953
|
+
query = Proc.new {
|
954
|
+
all
|
955
|
+
}
|
956
|
+
|
957
|
+
result = @adapter.query(collection, &query).min(:age)
|
958
|
+
result.must_equal 31
|
959
|
+
end
|
960
|
+
|
961
|
+
it 'returns the minimum from an empty query block' do
|
962
|
+
query = Proc.new {
|
963
|
+
}
|
964
|
+
|
965
|
+
result = @adapter.query(collection, &query).min(:age)
|
966
|
+
result.must_equal 31
|
967
|
+
end
|
968
|
+
|
969
|
+
it 'returns only the minimum of requested records' do
|
970
|
+
name = user1.name
|
971
|
+
|
972
|
+
query = Proc.new {
|
973
|
+
where(name: name)
|
974
|
+
}
|
975
|
+
|
976
|
+
result = @adapter.query(collection, &query).min(:age)
|
977
|
+
result.must_equal 32
|
978
|
+
end
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
982
|
+
describe 'interval' do
|
983
|
+
describe 'with an empty collection' do
|
984
|
+
it 'returns nil' do
|
985
|
+
result = @adapter.query(collection) do
|
986
|
+
all
|
987
|
+
end.interval(:age)
|
988
|
+
|
989
|
+
result.must_be_nil
|
990
|
+
end
|
991
|
+
end
|
992
|
+
|
993
|
+
describe 'with a filled collection' do
|
994
|
+
before do
|
995
|
+
@adapter.create(collection, user1)
|
996
|
+
@adapter.create(collection, user2)
|
997
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
998
|
+
end
|
999
|
+
|
1000
|
+
it 'returns the interval of all the records' do
|
1001
|
+
query = Proc.new {
|
1002
|
+
all
|
1003
|
+
}
|
1004
|
+
|
1005
|
+
result = @adapter.query(collection, &query).interval(:age)
|
1006
|
+
result.must_equal 1
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
it 'returns the interval from an empty query block' do
|
1010
|
+
query = Proc.new {
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
result = @adapter.query(collection, &query).interval(:age)
|
1014
|
+
result.must_equal 1
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
it 'returns only the interval of requested records' do
|
1018
|
+
name = user1.name
|
1019
|
+
|
1020
|
+
query = Proc.new {
|
1021
|
+
where(name: name)
|
1022
|
+
}
|
1023
|
+
|
1024
|
+
result = @adapter.query(collection, &query).interval(:age)
|
1025
|
+
result.must_equal 0
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
describe 'range' do
|
1031
|
+
describe 'with an empty collection' do
|
1032
|
+
it 'returns nil' do
|
1033
|
+
result = @adapter.query(collection) do
|
1034
|
+
all
|
1035
|
+
end.range(:age)
|
1036
|
+
|
1037
|
+
result.must_equal nil..nil
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
describe 'with a filled collection' do
|
1042
|
+
before do
|
1043
|
+
@adapter.create(collection, user1)
|
1044
|
+
@adapter.create(collection, user2)
|
1045
|
+
@adapter.create(collection, TestUser.new(name: 'S'))
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
it 'returns the range of all the records' do
|
1049
|
+
query = Proc.new {
|
1050
|
+
all
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
result = @adapter.query(collection, &query).range(:age)
|
1054
|
+
result.must_equal 31..32
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
it 'returns the range from an empty query block' do
|
1058
|
+
query = Proc.new {
|
1059
|
+
}
|
1060
|
+
|
1061
|
+
result = @adapter.query(collection, &query).range(:age)
|
1062
|
+
result.must_equal 31..32
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
it 'returns only the range of requested records' do
|
1066
|
+
name = user2.name
|
1067
|
+
|
1068
|
+
query = Proc.new {
|
1069
|
+
where(name: name)
|
1070
|
+
}
|
1071
|
+
|
1072
|
+
result = @adapter.query(collection, &query).range(:age)
|
1073
|
+
result.must_equal 31..31
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
end
|