schron 0.0.2

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +19 -0
  5. data/lib/schron.rb +4 -0
  6. data/lib/schron/archive.rb +88 -0
  7. data/lib/schron/archive/interface.rb +52 -0
  8. data/lib/schron/datastore/interface.rb +60 -0
  9. data/lib/schron/datastore/memory.rb +152 -0
  10. data/lib/schron/datastore/mongo.rb +173 -0
  11. data/lib/schron/datastore/mongo/metadata.rb +49 -0
  12. data/lib/schron/datastore/mongo/serializer.rb +38 -0
  13. data/lib/schron/datastore/sequel.rb +137 -0
  14. data/lib/schron/datastore/serializer.rb +46 -0
  15. data/lib/schron/dsl.rb +28 -0
  16. data/lib/schron/error.rb +7 -0
  17. data/lib/schron/id.rb +22 -0
  18. data/lib/schron/identity_map.rb +26 -0
  19. data/lib/schron/paginated_results.rb +28 -0
  20. data/lib/schron/paging.rb +5 -0
  21. data/lib/schron/query.rb +122 -0
  22. data/lib/schron/repository.rb +35 -0
  23. data/lib/schron/repository/interface.rb +36 -0
  24. data/lib/schron/test.rb +22 -0
  25. data/lib/schron/test/archive_examples.rb +133 -0
  26. data/lib/schron/test/datastore_examples.rb +419 -0
  27. data/lib/schron/test/entity.rb +21 -0
  28. data/lib/schron/test/repository_examples.rb +68 -0
  29. data/lib/schron/util.rb +27 -0
  30. data/schron.gemspec +24 -0
  31. data/spec/lib/schron/archive_spec.rb +26 -0
  32. data/spec/lib/schron/datastore/memory_spec.rb +9 -0
  33. data/spec/lib/schron/datastore/mongo_spec.rb +23 -0
  34. data/spec/lib/schron/datastore/sequel_spec.rb +40 -0
  35. data/spec/lib/schron/dsl_spec.rb +46 -0
  36. data/spec/lib/schron/identity_map_spec.rb +36 -0
  37. data/spec/lib/schron/paginaged_results_spec.rb +27 -0
  38. data/spec/lib/schron/query_spec.rb +46 -0
  39. data/spec/lib/schron/repository_spec.rb +27 -0
  40. data/spec/spec_helper.rb +9 -0
  41. data/spec/support/test_entities.rb +15 -0
  42. metadata +192 -0
@@ -0,0 +1,35 @@
1
+ require 'schron/query'
2
+ require 'schron/archive'
3
+ require 'schron/dsl'
4
+ require 'schron/repository/interface'
5
+
6
+ module Schron
7
+ module Repository
8
+
9
+ include Schron::Archive::Interface
10
+ include Schron::Repository::Interface
11
+
12
+ include Schron::Archive
13
+
14
+ def self.included(archive_class)
15
+ archive_class.extend Schron::DSL
16
+ end
17
+
18
+ def update(object)
19
+ maybe_load(@datastore.update(@kind, dump(object)))
20
+ end
21
+
22
+ def multi_update(objects)
23
+ load_all(@datastore.multi_update(@kind, dump_all(objects)))
24
+ end
25
+
26
+ def remove(id)
27
+ @datastore.remove(@kind, id) && nil
28
+ end
29
+
30
+ def multi_remove(ids)
31
+ @datastore.multi_remove(@kind, ids) && nil
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ require 'schron/archive/interface'
2
+
3
+ module Schron
4
+ module Repository
5
+ module Interface
6
+
7
+ include Schron::Archive::Interface
8
+
9
+ def update(object)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def multi_update(objects)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def remove(id)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def multi_remove(ids)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def dump(object)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def load(attributes)
30
+ raise NotImplementedError
31
+ end
32
+
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ Dir[File.join(__dir__, 'test', '**', '*.rb')].each do |f|
2
+ require f
3
+ end
4
+
5
+ module Schron
6
+ module Test
7
+ module_function
8
+
9
+ def expect_same_ids(entity_set1, entity_set2)
10
+ ids1 = entity_set1.map(&:id)
11
+ ids2 = entity_set2.map(&:id)
12
+ expect(ids1.to_set).to eq(ids2.to_set)
13
+ end
14
+
15
+ def expect_same_entity(e1, e2)
16
+ expect(e1.id).to eq(e2.id)
17
+ expect(e1.attributes).to eq(e2.attributes)
18
+ end
19
+
20
+
21
+ end
22
+ end
@@ -0,0 +1,133 @@
1
+ if defined?(RSpec)
2
+
3
+ require 'schron/query'
4
+ require 'schron/test'
5
+
6
+ # Be sure to set the archive before inclusion
7
+ # let(:archive) { ... }
8
+ shared_examples "schron archive" do
9
+
10
+ include Schron::Test
11
+
12
+ let(:entity_class) { archive.entity_class }
13
+
14
+ describe 'schron archive behavior' do
15
+
16
+ describe 'query' do
17
+ it 'yields a query with the archives database and kind' do
18
+ entity = archive.insert(entity_class.new)
19
+ expect(archive.datastore).to receive(:exec_query).and_call_original
20
+ results = archive.query do |q|
21
+ expect(q.kind).to eq(archive.kind)
22
+ end
23
+ expect_same_entity results.first, entity
24
+ end
25
+ end
26
+
27
+ describe 'first' do
28
+ it 'yields a query, automatically adding limit 1 and returning the first result' do
29
+ entity = archive.insert(entity_class.new)
30
+ expect(archive.datastore).to receive(:exec_query).and_call_original
31
+ result = archive.first do |q|
32
+ expect(q.kind).to eq(archive.kind)
33
+ end
34
+ expect_same_entity result, entity
35
+ end
36
+ end
37
+
38
+ describe 'get' do
39
+ it 'returns nil when the id is not found' do
40
+ expect(archive.get('123')).to be_nil
41
+ end
42
+
43
+ it 'does not call load if no data is found' do
44
+ expect(archive).not_to receive(:load)
45
+ expect(archive.get('123'))
46
+ end
47
+
48
+ it 'returns the loaded object when present' do
49
+ david = archive.insert(entity_class.new(id: '1', name: 'david'))
50
+ expect_same_entity archive.get(david.id), david
51
+ end
52
+ end
53
+
54
+ describe 'multi_get' do
55
+ it 'returns objects by id' do
56
+ entities = archive.multi_insert([
57
+ entity_class.new(a: '1'),
58
+ entity_class.new(b: '2')
59
+ ])
60
+
61
+ results = archive.multi_get(entities.map(&:id))
62
+ expect_same_ids results, entities
63
+ end
64
+
65
+ it 'returns an empty array when given an empty array' do
66
+ expect_same_ids archive.multi_get([]), []
67
+ end
68
+
69
+ it 'does not call load if no results are found' do
70
+ expect(archive).not_to receive(:load)
71
+ archive.multi_get(['123'])
72
+ end
73
+
74
+ it 'returns an empty array when no ids are found' do
75
+ expect_same_ids archive.multi_get(['12']), []
76
+ end
77
+
78
+ it 'ignores non existent ids mixed in with existing ones' do
79
+ a = archive.insert(entity_class.new)
80
+ results = archive.multi_get([a.id, 'made-up'])
81
+ expect_same_ids results, [a]
82
+ end
83
+ end
84
+
85
+ describe 'insert' do
86
+ it 'returns the inserted object with an id set when none is provided' do
87
+ object = archive.insert(entity_class.new)
88
+ expect(object.id).not_to be_nil
89
+ expect_same_entity archive.get(object.id), object
90
+ end
91
+
92
+ it 'returns the inserted object, using the id passed in when provided' do
93
+ object = archive.insert(entity_class.new(id: '23'))
94
+ expect(object.id).to eq('23')
95
+ expect_same_entity archive.get('23'), object
96
+ end
97
+ end
98
+
99
+ describe 'multi_insert' do
100
+ it 'returns the inserted objects, with ids set when none are provided' do
101
+ objects = archive.multi_insert([entity_class.new, entity_class.new])
102
+ objects.each do |object|
103
+ expect_same_entity archive.get(object.id), object
104
+ end
105
+ end
106
+
107
+ it 'returns the inserted objects, using the id passed in if provided' do
108
+ objects = archive.multi_insert([
109
+ entity_class.new(id: '23'),
110
+ entity_class.new(id: 'shrike')
111
+ ])
112
+ expect(objects.map(&:id).to_set).to eq(Set.new(['23', 'shrike']))
113
+ end
114
+ end
115
+
116
+ describe 'load' do
117
+ it 'should pass the attributes to the constructor of the entity class' do
118
+ expect(archive.entity_class).to receive(:new).with({a: 'b', c: 'd'})
119
+ archive.load(a: 'b', c: 'd')
120
+ end
121
+ end
122
+
123
+ describe 'dump' do
124
+ it 'should call attributes on the object' do
125
+ obj = double(attributes: {id: 3, name: 'david'})
126
+ hash = archive.dump(obj)
127
+ expect(hash).to eq(id: 3, name: 'david')
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ end
@@ -0,0 +1,419 @@
1
+ if defined?(RSpec)
2
+
3
+ require 'schron/query'
4
+ shared_examples 'schron datastore' do
5
+ let(:entity_kind) { :entities }
6
+ let(:named_kind) { :named }
7
+ let(:with_types_kind) { :with_types }
8
+
9
+ describe 'datastore interface' do
10
+ describe 'get' do
11
+ it 'should return nil if no matching id is found' do
12
+ expect(ds.get(entity_kind, 'no-data')).to be_nil
13
+ end
14
+
15
+ it 'should return the data if present' do
16
+ data = ds.insert(named_kind, {name: "dave"})
17
+ expect(ds.get(named_kind, data[:id])).to eq(name: "dave", id: data[:id])
18
+ end
19
+
20
+ it 'should return a copy of the data' do
21
+ data = ds.insert(entity_kind, {})
22
+ data = ds.get(entity_kind, data[:id])
23
+ data[:change] = 'true'
24
+ expect(ds.get(entity_kind, data[:id])[:change]).to be_nil
25
+ end
26
+ end
27
+
28
+ describe 'multi get' do
29
+ it 'should return an empty array if passed an empty array' do
30
+ expect(ds.multi_get(entity_kind, [])).to eq([])
31
+ end
32
+
33
+ it 'should return data by id' do
34
+ d1 = ds.insert(named_kind, {name: 'a'})
35
+ d2 = ds.insert(named_kind, {name: 'b'})
36
+ ids = [d1[:id], d2[:id]]
37
+ expect(ds.multi_get(named_kind, ids)).to include(d1, d2)
38
+ end
39
+
40
+ it 'should not return nils when ids dont exist' do
41
+ d = ds.insert(entity_kind, {})
42
+ expect(ds.multi_get(entity_kind, ['no-data', d[:id]])).to eq([d])
43
+ end
44
+
45
+ it 'should return copies of the data' do
46
+ data = ds.insert(entity_kind, {})
47
+ data_list = ds.multi_get(entity_kind, [data[:id]])
48
+ data_list.first[:changed] = 'true'
49
+ retrieved = ds.multi_get(entity_kind, data_list.map{ |d| d[:id] })
50
+ expect(retrieved).to eq([{id: data[:id]}])
51
+ end
52
+
53
+ it 'should return the data in the order of the requested ids' do
54
+ d1 = ds.insert(named_kind, {name: 'a'})
55
+ d2 = ds.insert(named_kind, {name: 'b'})
56
+ ids = [d1[:id], d2[:id]]
57
+ expect(ds.multi_get(named_kind, ids)).to eq([d1, d2])
58
+ end
59
+
60
+ end
61
+
62
+ describe 'insert' do
63
+ it 'should assign an id if none is provided' do
64
+ data = ds.insert(entity_kind, {})
65
+ expect(data[:id]).not_to be_nil
66
+ end
67
+
68
+ it 'should use the provided id if present' do
69
+ data = ds.insert(entity_kind, {id: '123'})
70
+ expect(data[:id]).to eq('123')
71
+ end
72
+
73
+ it 'should return a copy of the data' do
74
+ data = ds.insert(entity_kind, {})
75
+ data[:a] = 'b'
76
+ expect(ds.get(entity_kind, data[:id])[:a]).to be_nil
77
+ end
78
+
79
+ end
80
+
81
+ describe 'multi insert' do
82
+ it 'inserts all the data and returns it' do
83
+ data = ds.multi_insert(named_kind, [{name: 'a'}, {name: 'b'}])
84
+ expect(data.map{ |d| d[:name] }).to include('a', 'b')
85
+ expect(data.size).to eq(2)
86
+ end
87
+
88
+ it 'assigns ids when none are provided' do
89
+ data = ds.multi_insert(entity_kind, [{}, {}])
90
+ expect(data.any?{ |d| d[:id].nil? }).to be(false)
91
+ end
92
+
93
+ it 'uses provided ids when present' do
94
+ data = ds.multi_insert(entity_kind, [{id: '123'}, {id: '456'}])
95
+ expect(data.map{ |d| d[:id] }).to eq(['123', '456'])
96
+ end
97
+ end
98
+
99
+ describe 'update' do
100
+ it 'should raise an error if no id is provided' do
101
+ expect { ds.update(entity_kind, {}) }.to raise_error
102
+ end
103
+
104
+ it 'should update the data by id' do
105
+ inserted = ds.insert(named_kind, {})
106
+ inserted[:name] = 'david'
107
+ updated = ds.update(named_kind, inserted)
108
+ expect(updated[:name]).to eq('david')
109
+ expect(updated[:id]).to eq(inserted[:id])
110
+ end
111
+
112
+ it 'should return a copy of the data' do
113
+ data = ds.insert(named_kind, {})
114
+ data = ds.update(named_kind, data.merge(name: 'joe'))
115
+ data[:c] = 'd'
116
+ expect(ds.get(named_kind, data[:id])[:c]).to be_nil
117
+ end
118
+ end
119
+
120
+ describe 'multi update' do
121
+ it 'raises an error if any ids are missing' do
122
+ expect do
123
+ ds.multi_update(entity_kind, [{id: '123'}, {}])
124
+ end.to raise_error
125
+ end
126
+
127
+ it 'updates the data by ids' do
128
+ inserted = ds.multi_insert(named_kind, [{}, {}])
129
+ inserted[0][:name] = 'a'
130
+ inserted[1][:name] = 'b'
131
+ updated = ds.multi_update(named_kind, inserted)
132
+ expect(updated.map{ |d| d[:name] }.to_set).to eq(Set.new(['a', 'b']))
133
+ expect(updated.map{ |d| d[:id] }.to_set).to eq(Set.new(inserted.map{ |d| d[:id] }))
134
+ end
135
+
136
+ it 'returns copies of the data' do
137
+ data = ds.insert(named_kind, {})
138
+ data = ds.multi_update(named_kind, [data.merge(name: 'joe')]).first
139
+ data[:c] = 'd'
140
+ expect(ds.get(named_kind, data[:id])[:c]).to be_nil
141
+ end
142
+ end
143
+
144
+ describe 'remove' do
145
+ it 'should remove data from the store by id' do
146
+ data = ds.insert(entity_kind, {})
147
+ ds.remove(entity_kind, data[:id])
148
+ expect(ds.get(entity_kind, data[:id])).to be_nil
149
+ end
150
+
151
+ it 'should return nil' do
152
+ data = ds.insert(entity_kind, {})
153
+ expect(ds.remove(entity_kind, data[:id])).to be_nil
154
+ end
155
+ end
156
+
157
+ describe 'multi remove' do
158
+ it 'should remove the data from the store by ids' do
159
+ data = ds.multi_insert(entity_kind, [{}, {}])
160
+ ids = data.map { |d| d[:id] }
161
+ ds.multi_remove(entity_kind, ids)
162
+ expect(ds.multi_get(entity_kind, ids)).to be_empty
163
+ end
164
+
165
+ it 'should return nil' do
166
+ data = ds.insert(entity_kind, {})
167
+ expect(ds.multi_remove(entity_kind, [data[:id]])).to be_nil
168
+ end
169
+ end
170
+
171
+ describe 'exec query' do
172
+ let(:query) { Schron::Query.new(named_kind, ds) }
173
+ let(:no_result_query) { Schron::Query.new(entity_kind, ds) }
174
+
175
+ before(:each) do
176
+ @c = ds.insert(named_kind, name: 'c')
177
+ @a1 = ds.insert(named_kind, name: 'a')
178
+ @b = ds.insert(named_kind, name: 'b')
179
+ @a2 = ds.insert(named_kind, name: 'a')
180
+ @nil = ds.insert(named_kind, {name: nil})
181
+ end
182
+
183
+ describe 'filters' do
184
+ it 'filters by equality with a hash' do
185
+ found = query.filter(name: 'b').all
186
+ expect(found.size).to eq(1)
187
+ expect(found.first[:id]).to eq(@b[:id])
188
+ end
189
+
190
+ it 'filters by inclusion with a hash' do
191
+ found = query.filter(name: ['b', 'c']).all
192
+ expect(found.size).to eq(2)
193
+ expect(found.map{ |h| h[:id] }).to include(@b[:id], @c[:id])
194
+ end
195
+
196
+ it 'filters by "=" operator' do
197
+ found = query.filter(:name, '=', 'b').all
198
+ expect(found.size).to eq(1)
199
+ expect(found.first[:id]).to eq(@b[:id])
200
+ end
201
+
202
+ it 'filters by "in" operator' do
203
+ found = query.filter(:name, 'in', ['a', 'b']).all
204
+ expect(found.size).to eq(3)
205
+ expect(found.map{ |h| h[:id] }).to include(@a1[:id], @a2[:id], @b[:id])
206
+ end
207
+
208
+ it 'filters by ">" operator' do
209
+ found = query.filter(:name, '>', 'a').all
210
+ expect(found.size).to be(2)
211
+ expect(found.map{ |h| h[:id] }).to include(@b[:id], @c[:id])
212
+ end
213
+
214
+ it 'filters by ">=" operator' do
215
+ found = query.filter(:name, '>=', 'a').all
216
+ expect(found.size).to be(4)
217
+ expect(found.map{ |h| h[:id] }).to include(@a1[:id], @a2[:id], @b[:id], @c[:id])
218
+ end
219
+
220
+ it 'filters by "<" operator' do
221
+ found = query.filter(:name, '<', 'b').all
222
+ expect(found.size).to be(2)
223
+ expect(found.map{ |h| h[:id] }).to include(@a1[:id], @a2[:id])
224
+ end
225
+
226
+ it 'filters by "<=" operator' do
227
+ found = query.filter(:name, '<=', 'b').all
228
+ expect(found.size).to be(3)
229
+ expect(found.map{ |h| h[:id] }).to include(@a1[:id], @a2[:id], @b[:id])
230
+ end
231
+
232
+ it 'filters by "!=" operator' do
233
+ found = query.filter(:name, '!=', 'a').all
234
+ expect(found.size).to eq(3)
235
+ expect(found.map{ |h| h[:id] }).to include(@c[:id], @b[:id], @nil[:id])
236
+ end
237
+
238
+ it 'returns an empty list if no results match' do
239
+ expect(query.filter(name: '123').all).to eq([])
240
+ end
241
+
242
+ context 'nil filtering' do
243
+ it 'filters by = nil' do
244
+ found = query.filter(name: nil).all
245
+ expect(found.size).to eq(1)
246
+ expect(found.first[:id]).to eq(@nil[:id])
247
+ end
248
+
249
+ it 'filters by != nil' do
250
+ found = query.filter(:name, '!=', nil).all
251
+ expect(found.size).to eq(4)
252
+ expect(found.map{ |h| h[:id] }).to include(@a1[:id], @a2[:id], @b[:id], @c[:id])
253
+ end
254
+
255
+ it 'filters by in with nil' do
256
+ found = query.filter(:name, 'in', [nil, 'b']).all
257
+ expect(found.size).to eq(2)
258
+ expect(found.map{ |h| h[:id] }).to include(@b[:id], @nil[:id])
259
+ end
260
+ end
261
+ end
262
+
263
+ describe 'limit' do
264
+ it 'limits the results' do
265
+ found = query.limit(1).all
266
+ expect(found.size).to eq(1)
267
+ end
268
+
269
+ it 'returns an empty list if no results match' do
270
+ q = no_result_query
271
+ expect(q.limit(2).all).to eq([])
272
+ end
273
+ end
274
+
275
+ describe 'offset' do
276
+ it 'offsets the results' do
277
+ all = 4.times.reduce([]) do |ary, i|
278
+ ary + query.limit(1).offset(i).all
279
+ end
280
+ all_ids = all.map { |h| h[:id] }
281
+ expect(all_ids).to include(@a1[:id], @a2[:id], @b[:id], @c[:id])
282
+ end
283
+
284
+ it 'returns an empty list if no results match' do
285
+ expect(query.offset(10).all).to eq([])
286
+ end
287
+ end
288
+
289
+ describe 'sorts' do
290
+ it 'sorts in asc direction by default' do
291
+ found = query.sort(:name).all
292
+ expect(found.map{ |h| h[:name] }).to eq(
293
+ [nil, 'a', 'a', 'b', 'c']
294
+ )
295
+ end
296
+
297
+ it 'sorts ascending' do
298
+ found = query.sort(:name, :asc).all
299
+ expect(found.map{ |h| h[:name] }).to eq([nil, 'a', 'a', 'b', 'c'])
300
+ end
301
+
302
+ it 'sorts descending' do
303
+ found = query.sort(:name, :desc).all
304
+ expect(found.map { |h| h[:name] }).to eq(['c', 'b', 'a', 'a', nil])
305
+ end
306
+
307
+ it 'sorts by multiple fields' do
308
+ found = query.sort(:name).sort(:id).all
309
+ expected = [@nil[:id]] + [@a1[:id], @a2[:id]].sort + [@b[:id], @c[:id]]
310
+ expect(found.map { |h| h[:id] }).to eq(expected)
311
+ end
312
+
313
+ it 'returns an empty list if no results match' do
314
+ expect(no_result_query.sort(:id).all).to eq([])
315
+ end
316
+ end
317
+
318
+ end
319
+ end
320
+
321
+ describe 'datastore type handling' do
322
+
323
+ describe 'arrays' do
324
+ xit 'saves and loads empty arrays' do
325
+ data = ds.insert(with_types_kind, {
326
+ array: []
327
+ })
328
+ loaded = ds.get(with_types_kind, data[:id])
329
+ expect(loaded[:array]).to eq([])
330
+ end
331
+ xit 'stores and loads arrays of strings, numbers and arrays' do
332
+ data = ds.insert(with_types_kind, {
333
+ array: [1, "a", ["b"]]
334
+ })
335
+ loaded = ds.get(with_types_kind, data[:id])
336
+ expect(loaded[:array]).to eq([1, "a", ["b"]])
337
+ end
338
+ end
339
+
340
+ describe 'dates and times' do
341
+ xit 'saves and loads Date' do
342
+ data = ds.insert(with_types_kind, {
343
+ date: Date.new(2010, 10, 6)
344
+ })
345
+ loaded = ds.get(with_types_kind, data[:id])
346
+ expect(loaded[:date]).to eq(Date.new(2010, 10, 6))
347
+ end
348
+
349
+ xit 'saves and loads Time' do
350
+ data = ds.insert(with_types_kind, {
351
+ time: Time.new(2013, 10, 6)
352
+ })
353
+ loaded = ds.get(with_types_kind, data[:id])
354
+ expect(loaded[:time]).to eq(Time.new(2013, 10, 6))
355
+ end
356
+
357
+ xit 'saves and loads DateTime' do
358
+ data = ds.insert(with_types_kind, {
359
+ date_time: DateTime.new(2013, 10, 6)
360
+ })
361
+ loaded = ds.get(with_types_kind, data[:id])
362
+ expect(loaded[:date_time]).to eq(DateTime.new(2013, 10, 6))
363
+ end
364
+ end
365
+
366
+ describe 'sets' do
367
+ xit 'saves and loads Sets of numbers, strings and arrays' do
368
+ data = ds.insert(with_types_kind, {
369
+ set: Set.new([1, "2", ["a"]])
370
+ })
371
+ loaded = ds.get(with_types_kind, data[:id])
372
+ expect(loaded[:set]).to eq(Set.new([1, "2", ["a"]]))
373
+ end
374
+ end
375
+
376
+ describe 'nested hashes' do
377
+ xit 'saves and loads nested hashes' do
378
+ data = ds.insert(with_types_kind, {
379
+ hash: {a: {b: 'c'}, d: 3}
380
+ })
381
+ loaded = ds.get(with_types_kind, data[:id])
382
+ expect(loaded[:hash]).to eq({a: {b: 'c'}, d: 3})
383
+ end
384
+ end
385
+
386
+ describe 'recursive structure' do
387
+ xit 'saves and loads' do
388
+ data = ds.insert(with_types_kind, {
389
+ name: 'score',
390
+ children: [
391
+ {name: 'diet', children: [
392
+ {name: 'eating'},
393
+ {name: 'not eating'}
394
+ ]},
395
+ {name: 'exercise', children: [
396
+ {name: 'workout'},
397
+ {name: 'sit'}
398
+ ]}
399
+ ]})
400
+ loaded = ds.get(with_types_kind, data[:id])
401
+ loaded.delete(:id)
402
+ expect(loaded).to eq({
403
+ name: 'score',
404
+ children: [
405
+ {name: 'diet', children: [
406
+ {name: 'eating'},
407
+ {name: 'not eating'}
408
+ ]},
409
+ {name: 'exercise', children: [
410
+ {name: 'workout'},
411
+ {name: 'sit'}
412
+ ]}
413
+ ]})
414
+ end
415
+ end
416
+ end
417
+ end
418
+
419
+ end