perobs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,108 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # MIT License
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
27
+
28
+ require 'fileutils'
29
+ require 'time'
30
+ require 'perobs'
31
+
32
+ class O1 < PEROBS::Object
33
+
34
+ po_attr :a1
35
+
36
+ def initialize(store)
37
+ super
38
+ end
39
+
40
+ end
41
+
42
+ class O2 < PEROBS::Object
43
+
44
+ po_attr :a1, :a2, :a3, :a4
45
+
46
+ def initialize(store)
47
+ super
48
+ init_attr(:a1, 'a1')
49
+ init_attr(:a2, nil)
50
+ init_attr(:a4, 42)
51
+ end
52
+
53
+ end
54
+
55
+ describe PEROBS::Store do
56
+
57
+ before(:each) do
58
+ FileUtils.rm_rf('test_db')
59
+ @store = PEROBS::Store.new('test_db')
60
+ end
61
+
62
+ after(:all) do
63
+ FileUtils.rm_rf('test_db')
64
+ end
65
+
66
+ it 'should initialize attributes with default values' do
67
+ @store['o1'] = o1 = O1.new(@store)
68
+ @store['o2'] = o2 = O2.new(@store)
69
+ o2.a1.should == 'a1'
70
+ o2.a2.should be_nil
71
+ o2.a3.should be_nil
72
+ o2.a4.should == 42
73
+ end
74
+
75
+ it 'should assign values to attributes' do
76
+ @store['o1'] = o1 = O1.new(@store)
77
+ @store['o2'] = o2 = O2.new(@store)
78
+ o1.a1 = 'a1'
79
+ o2.a1 = nil
80
+ o2.a3 = o1
81
+
82
+ o1.a1.should == 'a1'
83
+ o2.a1.should be_nil
84
+ o2.a3.should == o1
85
+ o2.a4.should == 42
86
+ @store.sync
87
+ end
88
+
89
+ it 'should persist assigned values' do
90
+ @store['o1'] = o1 = O1.new(@store)
91
+ @store['o2'] = o2 = O2.new(@store)
92
+ o1.a1 = 'a1'
93
+ o2.a1 = nil
94
+ o2.a3 = o1
95
+ o2.a4 = PEROBS::Array.new(@store)
96
+ o2.a4 += [ 0, 1, 2 ]
97
+ @store.sync
98
+
99
+ @store = PEROBS::Store.new('test_db')
100
+ o1 = @store['o1']
101
+ o2 = @store['o2']
102
+ o1.a1.should == 'a1'
103
+ o2.a1.should be_nil
104
+ o2.a3.should == o1
105
+ o2.a4.should == [ 0, 1, 2 ]
106
+ end
107
+
108
+ end
@@ -0,0 +1,412 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # MIT License
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
27
+
28
+ require 'fileutils'
29
+ require 'time'
30
+ require 'perobs'
31
+
32
+ class POSError < RuntimeError
33
+ end
34
+
35
+ class Person < PEROBS::Object
36
+
37
+ po_attr :name, :zip, :bmi, :married, :related, :relatives
38
+
39
+ def initialize(store)
40
+ super
41
+ init_attr(:name, '')
42
+ init_attr(:bmi, 22.2)
43
+ init_attr(:married, false)
44
+ end
45
+
46
+ end
47
+
48
+ describe PEROBS::Store do
49
+
50
+ before(:all) do
51
+ FileUtils.rm_rf('test_db')
52
+ end
53
+
54
+ after(:each) do
55
+ @store.gc
56
+ lambda { @store.check }.should_not raise_error
57
+ FileUtils.rm_rf('test_db')
58
+ end
59
+
60
+ it 'should @store simple objects' do
61
+ @store = PEROBS::Store.new('test_db', { :serializer => :yaml })
62
+ @store['john'] = john = Person.new(@store)
63
+ john.name = 'John'
64
+ john.zip = 4060
65
+ john.bmi = 25.5
66
+ @store['jane'] = jane = Person.new(@store)
67
+ jane.name = 'Jane'
68
+ jane.related = john
69
+ jane.married = true
70
+ jane.relatives = 'test'
71
+
72
+ john.name.should == 'John'
73
+ john.zip.should == 4060
74
+ john.bmi.should == 25.5
75
+ john.married.should be_false
76
+ john.related.should be_nil
77
+ jane = @store['jane']
78
+ jane.name.should == 'Jane'
79
+ jane.related.should == john
80
+ jane.married.should be_true
81
+ end
82
+
83
+ it 'should @store and retrieve simple objects' do
84
+ [ :marshal, :json, :yaml ].each do |serializer|
85
+ FileUtils.rm_rf('test_db')
86
+ @store = PEROBS::Store.new('test_db', { :serializer => serializer })
87
+ @store['john'] = john = Person.new(@store)
88
+ john.name = 'John'
89
+ john.zip = 4060
90
+ john.bmi = 25.5
91
+ @store['jane'] = jane = Person.new(@store)
92
+ jane.name = 'Jane'
93
+ jane.related = john
94
+ jane.married = true
95
+ jane.relatives = 'test'
96
+
97
+ @store.sync
98
+
99
+ @store = PEROBS::Store.new('test_db', { :serializer => serializer })
100
+ john = @store['john']
101
+ john.name.should == 'John'
102
+ john.zip.should == 4060
103
+ john.bmi.should == 25.5
104
+ john.married.should be_false
105
+ john.related.should be_nil
106
+ jane = @store['jane']
107
+ jane.name.should == 'Jane'
108
+ jane.related.should == john
109
+ jane.married.should be_true
110
+ end
111
+ end
112
+
113
+ it 'should flush cached objects when necessary' do
114
+ @store = PEROBS::Store.new('test_db', :cache_bits => 3)
115
+ last_obj = nil
116
+ 0.upto(20) do |i|
117
+ @store["person#{i}"] = obj = Person.new(@store)
118
+ @store["person#{i}"].should == obj
119
+ obj.name = "Person #{i}"
120
+ obj.name.should == "Person #{i}"
121
+ obj.related = last_obj
122
+ obj.related.should == last_obj
123
+ last_obj = obj
124
+ end
125
+ 0.upto(20) do |i|
126
+ @store["person#{i}"].name.should == "Person #{i}"
127
+ end
128
+ end
129
+
130
+ it 'should detect modification to non-working objects' do
131
+ @store = PEROBS::Store.new('test_db', :cache_bits => 3)
132
+ 0.upto(20) do |i|
133
+ @store["person#{i}"] = obj = Person.new(@store)
134
+ obj.name = "Person #{i}"
135
+ end
136
+ 0.upto(20) do |i|
137
+ @store["person#{i}"].name = "New Person #{i}"
138
+ end
139
+ @store.sync
140
+ @store = PEROBS::Store.new('test_db')
141
+ 0.upto(20) do |i|
142
+ @store["person#{i}"].name.should == "New Person #{i}"
143
+ end
144
+ end
145
+
146
+ it 'should garbage collect unlinked objects' do
147
+ @store = PEROBS::Store.new('test_db')
148
+ @store['person1'] = obj = Person.new(@store)
149
+ id1 = obj._id
150
+ @store['person2'] = obj = Person.new(@store)
151
+ id2 = obj._id
152
+ obj.related = obj = Person.new(@store)
153
+ id3 = obj._id
154
+ @store.sync
155
+ @store['person1'] = nil
156
+ @store.gc
157
+ @store = PEROBS::Store.new('test_db')
158
+ @store.object_by_id(id1).should be_nil
159
+ @store['person2']._id.should == id2
160
+ @store['person2'].related._id.should == id3
161
+ end
162
+
163
+ it 'should handle cyclicly linked objects' do
164
+ @store = PEROBS::Store.new('test_db')
165
+ @store['person0'] = p0 = Person.new(@store)
166
+ id0 = p0._id
167
+ p1 = Person.new(@store)
168
+ id1 = p1._id
169
+ p2 = Person.new(@store)
170
+ id2 = p2._id
171
+ p1.related = p2
172
+ p2.related = p1
173
+ p0.related = p1
174
+ @store.sync
175
+ @store.gc
176
+ @store = PEROBS::Store.new('test_db')
177
+ @store['person0']._id.should == id0
178
+ @store['person0'].related._id.should == id1
179
+ @store['person0'].related.related._id.should == id2
180
+
181
+ @store['person0'].related = nil
182
+ @store.gc
183
+ @store.object_by_id(id1).should be_nil
184
+ @store.object_by_id(id2).should be_nil
185
+
186
+ @store = PEROBS::Store.new('test_db')
187
+ @store.object_by_id(id1).should be_nil
188
+ @store.object_by_id(id2).should be_nil
189
+ end
190
+
191
+ it 'should support a successful transaction' do
192
+ @store = PEROBS::Store.new('test_db')
193
+ @store.transaction do
194
+ @store['person0'] = p0 = Person.new(@store)
195
+ p0.name = 'Jimmy'
196
+ end
197
+ @store['person0'].name.should == 'Jimmy'
198
+ end
199
+
200
+ it 'should handle a failed transaction 1' do
201
+ @store = PEROBS::Store.new('test_db')
202
+ begin
203
+ @store.transaction do
204
+ @store['person0'] = p0 = Person.new(@store)
205
+ p0.name = 'Jimmy'
206
+ raise POSError
207
+ end
208
+ rescue POSError
209
+ end
210
+ @store['person0'].should be_nil
211
+ end
212
+
213
+ it 'should handle a failed transaction 2' do
214
+ @store = PEROBS::Store.new('test_db')
215
+ @store['person1'] = p1 = Person.new(@store)
216
+ p1.name = 'Joe'
217
+ begin
218
+ @store.transaction do
219
+ @store['person0'] = p0 = Person.new(@store)
220
+ p0.name = 'Jimmy'
221
+ raise POSError
222
+ end
223
+ rescue POSError
224
+ end
225
+ @store['person1'].name.should == 'Joe'
226
+ @store['person0'].should be_nil
227
+ end
228
+
229
+ it 'should support a successful nested transaction' do
230
+ @store = PEROBS::Store.new('test_db')
231
+ @store.transaction do
232
+ @store['person0'] = p0 = Person.new(@store)
233
+ p0.name = 'Jimmy'
234
+ @store.transaction do
235
+ @store['person1'] = p1 = Person.new(@store)
236
+ p1.name = 'Joe'
237
+ end
238
+ end
239
+ @store['person0'].name.should == 'Jimmy'
240
+ @store['person1'].name.should == 'Joe'
241
+ end
242
+
243
+ it 'should handle a failed nested transaction 1' do
244
+ @store = PEROBS::Store.new('test_db')
245
+ begin
246
+ @store.transaction do
247
+ @store['person0'] = p0 = Person.new(@store)
248
+ p0.name = 'Jimmy'
249
+ begin
250
+ @store.transaction do
251
+ @store['person1'] = p1 = Person.new(@store)
252
+ p1.name = 'Joe'
253
+ raise POSError
254
+ end
255
+ rescue POSError
256
+ end
257
+ end
258
+ rescue POSError
259
+ end
260
+ @store['person0'].name.should == 'Jimmy'
261
+ @store['person1'].should be_nil
262
+ end
263
+
264
+ it 'should handle a failed nested transaction 2' do
265
+ @store = PEROBS::Store.new('test_db')
266
+ begin
267
+ @store.transaction do
268
+ @store['person0'] = p0 = Person.new(@store)
269
+ p0.name = 'Jimmy'
270
+ @store.transaction do
271
+ @store['person1'] = p1 = Person.new(@store)
272
+ p1.name = 'Joe'
273
+ end
274
+ raise POSError
275
+ end
276
+ rescue POSError
277
+ end
278
+ @store['person0'].should be_nil
279
+ @store['person1'].should be_nil
280
+ end
281
+
282
+ it 'should support a successful 2-level nested transaction' do
283
+ @store = PEROBS::Store.new('test_db')
284
+ @store.transaction do
285
+ @store['person0'] = p0 = Person.new(@store)
286
+ p0.name = 'Jimmy'
287
+ @store.transaction do
288
+ @store['person1'] = p1 = Person.new(@store)
289
+ p1.name = 'Joe'
290
+ @store.transaction do
291
+ @store['person2'] = p2 = Person.new(@store)
292
+ p2.name = 'Jane'
293
+ end
294
+ end
295
+ end
296
+ @store['person0'].name.should == 'Jimmy'
297
+ @store['person1'].name.should == 'Joe'
298
+ @store['person2'].name.should == 'Jane'
299
+ end
300
+
301
+ it 'should handle a failed 2-level nested transaction 1' do
302
+ @store = PEROBS::Store.new('test_db')
303
+ @store.transaction do
304
+ @store['person0'] = p0 = Person.new(@store)
305
+ p0.name = 'Jimmy'
306
+ @store.transaction do
307
+ @store['person1'] = p1 = Person.new(@store)
308
+ p1.name = 'Joe'
309
+ begin
310
+ @store.transaction do
311
+ @store['person2'] = p2 = Person.new(@store)
312
+ p2.name = 'Jane'
313
+ raise POSError
314
+ end
315
+ rescue POSError
316
+ end
317
+ end
318
+ end
319
+ @store['person0'].name.should == 'Jimmy'
320
+ @store['person1'].name.should == 'Joe'
321
+ @store['person2'].should be_nil
322
+ end
323
+
324
+ it 'should handle a failed 2-level nested transaction 2' do
325
+ @store = PEROBS::Store.new('test_db')
326
+ @store.transaction do
327
+ @store['person0'] = p0 = Person.new(@store)
328
+ p0.name = 'Jimmy'
329
+ @store.transaction do
330
+ @store['person1'] = p1 = Person.new(@store)
331
+ p1.name = 'Joe'
332
+ begin
333
+ @store.transaction do
334
+ @store['person2'] = p2 = Person.new(@store)
335
+ p2.name = 'Jane'
336
+ raise POSError
337
+ end
338
+ rescue POSError
339
+ end
340
+ p1.name = 'Jane'
341
+ end
342
+ end
343
+ @store['person0'].name.should == 'Jimmy'
344
+ @store['person1'].name.should == 'Jane'
345
+ @store['person2'].should be_nil
346
+ end
347
+
348
+ it 'should survive a real world usage test' do
349
+ options = { :engine => PEROBS::HashedBlocksDB, :dir_nibbles => 1 }
350
+ @store = PEROBS::Store.new('test_db', options)
351
+ ref = {}
352
+
353
+ 0.upto(2000) do |i|
354
+ key = "o#{i}"
355
+ case i % 8
356
+ when 0
357
+ value = 'A' * rand(512)
358
+ @store[key] = p = Person.new(@store)
359
+ p.name = value
360
+ ref[key] = value
361
+ @store.sync
362
+ when 1
363
+ value = 'B' * rand(128)
364
+ @store[key] = p = Person.new(@store)
365
+ p.name = value
366
+ ref[key] = value
367
+ when 2
368
+ index = i - rand(20)
369
+ if index >= 0
370
+ key = "o#{i - rand(20)}"
371
+ @store[key] = nil
372
+ ref.delete(key)
373
+ end
374
+ when 3
375
+ @store.gc if rand(30) == 0
376
+ when 4
377
+ if rand(15) == 0
378
+ @store.sync
379
+ @store = PEROBS::Store.new('test_db', options)
380
+ end
381
+ when 5
382
+ index = i - rand(10)
383
+ if rand(3) == 0 && index >= 0
384
+ key = "o#{i - rand(10)}"
385
+ value = 'C' * rand(1024)
386
+ @store[key] = p = Person.new(@store)
387
+ p.name = value
388
+ ref[key] = value
389
+ end
390
+ when 6
391
+ if rand(50) == 0
392
+ @store.sync
393
+ @store.check(false)
394
+ end
395
+ when 7
396
+ index = rand(i)
397
+ if ref[key]
398
+ @store[key].name.should == ref[key]
399
+ end
400
+ end
401
+
402
+ if ref[key]
403
+ @store[key].name.should == ref[key]
404
+ end
405
+ end
406
+
407
+ ref.each do |k, v|
408
+ @store[k].name.should == v
409
+ end
410
+ end
411
+
412
+ end
@@ -0,0 +1,155 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
4
+ #
5
+ # MIT License
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
27
+
28
+ require 'fileutils'
29
+ require 'time'
30
+ require 'perobs'
31
+
32
+ class Person < PEROBS::Object
33
+
34
+ po_attr :name, :zip, :bmi, :married, :related, :relatives
35
+
36
+ def initialize(store)
37
+ super
38
+ init_attr(:name, '')
39
+ init_attr(:bmi, 22.2)
40
+ init_attr(:married, false)
41
+ end
42
+
43
+ end
44
+
45
+ describe PEROBS::Store do
46
+
47
+ before(:all) do
48
+ FileUtils.rm_rf('test_db')
49
+ end
50
+
51
+ after(:each) do
52
+ FileUtils.rm_rf('test_db')
53
+ end
54
+
55
+ it 'should store simple objects' do
56
+ store = PEROBS::Store.new('test_db')
57
+ store['john'] = john = Person.new(store)
58
+ john.name = 'John'
59
+ john.zip = 4060
60
+ john.bmi = 25.5
61
+ store['jane'] = jane = Person.new(store)
62
+ jane.name = 'Jane'
63
+ jane.related = john
64
+ jane.married = true
65
+ jane.relatives = 'test'
66
+
67
+ john.name.should == 'John'
68
+ john.zip.should == 4060
69
+ john.bmi.should == 25.5
70
+ john.married.should be_false
71
+ john.related.should be_nil
72
+ jane = store['jane']
73
+ jane.name.should == 'Jane'
74
+ jane.related.should == john
75
+ jane.married.should be_true
76
+ end
77
+
78
+ it 'should store and retrieve simple objects' do
79
+ store = PEROBS::Store.new('test_db')
80
+ store['john'] = john = Person.new(store)
81
+ john.name = 'John'
82
+ john.zip = 4060
83
+ john.bmi = 25.5
84
+ store['jane'] = jane = Person.new(store)
85
+ jane.name = 'Jane'
86
+ jane.related = john
87
+ jane.married = true
88
+ jane.relatives = 'test'
89
+
90
+ store.sync
91
+
92
+ store = PEROBS::Store.new('test_db')
93
+ john = store['john']
94
+ john.name.should == 'John'
95
+ john.zip.should == 4060
96
+ john.bmi.should == 25.5
97
+ john.married.should be_false
98
+ john.related.should be_nil
99
+ jane = store['jane']
100
+ jane.name.should == 'Jane'
101
+ jane.related.should == john
102
+ jane.married.should be_true
103
+ end
104
+
105
+ it 'should flush cached objects when necessary' do
106
+ store = PEROBS::Store.new('test_db', :cache_bits => 3)
107
+ last_obj = nil
108
+ 0.upto(20) do |i|
109
+ store["person#{i}"] = obj = Person.new(store)
110
+ store["person#{i}"].should == obj
111
+ obj.name = "Person #{i}"
112
+ obj.name.should == "Person #{i}"
113
+ obj.related = last_obj
114
+ obj.related.should == last_obj
115
+ last_obj = obj
116
+ end
117
+ 0.upto(20) do |i|
118
+ store["person#{i}"].name.should == "Person #{i}"
119
+ end
120
+ end
121
+
122
+ it 'should detect modification to non-working objects' do
123
+ store = PEROBS::Store.new('test_db', :cache_bits => 3)
124
+ 0.upto(20) do |i|
125
+ store["person#{i}"] = obj = Person.new(store)
126
+ obj.name = "Person #{i}"
127
+ end
128
+ 0.upto(20) do |i|
129
+ store["person#{i}"].name = "New Person #{i}"
130
+ end
131
+ store.sync
132
+ store = PEROBS::Store.new('test_db')
133
+ 0.upto(20) do |i|
134
+ store["person#{i}"].name.should == "New Person #{i}"
135
+ end
136
+ end
137
+
138
+ it 'should garbage collect unlinked objects' do
139
+ store = PEROBS::Store.new('test_db')
140
+ store['person1'] = obj = Person.new(store)
141
+ id1 = obj._id
142
+ store['person2'] = obj = Person.new(store)
143
+ id2 = obj._id
144
+ obj.related = obj = Person.new(store)
145
+ id3 = obj._id
146
+ store.sync
147
+ store['person1'] = nil
148
+ store.gc
149
+ store = PEROBS::Store.new('test_db')
150
+ store.object_by_id(id1).should be_nil
151
+ store['person2']._id.should == id2
152
+ store['person2'].related._id.should == id3
153
+ end
154
+
155
+ end