memstore 1.0.0 → 1.0.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.
- data/README.md +106 -79
- data/lib/memstore.rb +22 -7
- data/lib/memstore/json.rb +3 -3
- data/lib/memstore/msgpack.rb +3 -3
- data/lib/memstore/version.rb +1 -1
- data/lib/memstore/yaml.rb +3 -3
- data/spec/memstore_json_spec.rb +19 -19
- data/spec/memstore_msgpack_spec.rb +19 -19
- data/spec/memstore_spec.rb +214 -168
- data/spec/memstore_yaml_spec.rb +19 -19
- metadata +2 -2
data/README.md
CHANGED
@@ -10,15 +10,21 @@ It’s not in any way supposed to be a “real” database. However, it can repl
|
|
10
10
|
|
11
11
|
Add this line to your application's Gemfile:
|
12
12
|
|
13
|
-
|
13
|
+
```ruby
|
14
|
+
gem "memstore"
|
15
|
+
```
|
14
16
|
|
15
17
|
And then execute:
|
16
18
|
|
17
|
-
|
19
|
+
```sh
|
20
|
+
$ bundle
|
21
|
+
```
|
18
22
|
|
19
23
|
Or install it yourself as:
|
20
24
|
|
21
|
-
|
25
|
+
```sh
|
26
|
+
$ gem install memstore
|
27
|
+
```
|
22
28
|
|
23
29
|
## Usage
|
24
30
|
|
@@ -40,7 +46,7 @@ Or install it yourself as:
|
|
40
46
|
Creating a data store is utterly simple:
|
41
47
|
|
42
48
|
```ruby
|
43
|
-
|
49
|
+
store = MemStore.new
|
44
50
|
```
|
45
51
|
|
46
52
|
By default, objects are indexed using `Object#hash`.
|
@@ -48,7 +54,7 @@ By default, objects are indexed using `Object#hash`.
|
|
48
54
|
If a different property should be used, it can be specified like this:
|
49
55
|
|
50
56
|
```ruby
|
51
|
-
|
57
|
+
store = MemStore.new(:id)
|
52
58
|
```
|
53
59
|
|
54
60
|
The property needs to be truly unique for all objects since it’s used as a hash key internally.
|
@@ -56,8 +62,8 @@ The property needs to be truly unique for all objects since it’s used as a has
|
|
56
62
|
An items collection can also be provided on creation:
|
57
63
|
|
58
64
|
```ruby
|
59
|
-
|
60
|
-
|
65
|
+
store = MemStore.new(nil, { ... }) # to use Object.hash as key
|
66
|
+
store = MemStore.new(:id, { ... }) # to use custom key
|
61
67
|
```
|
62
68
|
|
63
69
|
The collection must be a hash that correctly maps the used key to each item.
|
@@ -71,15 +77,15 @@ They’re basically the same, but `ObjectStore` accesses items through `item.att
|
|
71
77
|
`ObjectStore` is the default variant:
|
72
78
|
|
73
79
|
```ruby
|
74
|
-
|
80
|
+
store = MemStore.new
|
75
81
|
# is equal to
|
76
|
-
|
82
|
+
store = MemStore::ObjectStore.new
|
77
83
|
```
|
78
84
|
|
79
85
|
`HashStore` needs to be created explicitly:
|
80
86
|
|
81
87
|
```ruby
|
82
|
-
|
88
|
+
store = MemStore::HashStore.new
|
83
89
|
```
|
84
90
|
|
85
91
|
If no key attribute is specified, `HashStore` will also use `Object#hash`.
|
@@ -89,32 +95,32 @@ If no key attribute is specified, `HashStore` will also use `Object#hash`.
|
|
89
95
|
`items` provides direct access to the internal items hash.
|
90
96
|
|
91
97
|
```ruby
|
92
|
-
|
98
|
+
store.items
|
93
99
|
# => {}
|
94
|
-
|
100
|
+
store.items = { 1 => a, 2 => b, 3 => c }
|
95
101
|
# => { 1 => a, 2 => b, 3 => c }
|
96
102
|
```
|
97
103
|
|
98
104
|
`insert` adds one or multiple items and returns the data store itself:
|
99
105
|
|
100
106
|
```ruby
|
101
|
-
|
102
|
-
# =>
|
107
|
+
store.insert(a, b, c)
|
108
|
+
# => store
|
103
109
|
```
|
104
110
|
|
105
111
|
Since it returns the data store, items can be added right after instantiation:
|
106
112
|
|
107
113
|
```ruby
|
108
|
-
|
109
|
-
# =>
|
114
|
+
store = MemStore.new.insert(a, b, c)
|
115
|
+
# => store
|
110
116
|
```
|
111
117
|
|
112
118
|
MemStore also supports the shovel operator `<<` for adding items.
|
113
119
|
Only one item can be added at a time but it’s chainable:
|
114
120
|
|
115
121
|
```ruby
|
116
|
-
|
117
|
-
# =>
|
122
|
+
store << a << b << c
|
123
|
+
# => store
|
118
124
|
```
|
119
125
|
|
120
126
|
### Retrieving Items
|
@@ -122,7 +128,7 @@ mb << a << b << c
|
|
122
128
|
`size` returns the current number of items:
|
123
129
|
|
124
130
|
```ruby
|
125
|
-
|
131
|
+
store.size
|
126
132
|
# => 3
|
127
133
|
```
|
128
134
|
|
@@ -131,18 +137,18 @@ If a single key is given, a single item will be returned.
|
|
131
137
|
If multiple keys are given, an array of items will be returned with `nil` when there is no item for a key.
|
132
138
|
|
133
139
|
```ruby
|
134
|
-
|
140
|
+
store[1]
|
135
141
|
# => a
|
136
|
-
|
142
|
+
store[1, 2, 3]
|
137
143
|
# => [a, b, c]
|
138
144
|
```
|
139
145
|
|
140
146
|
Ranges are also supported and can even be combined with single keys:
|
141
147
|
|
142
148
|
```ruby
|
143
|
-
|
149
|
+
store[1..3]
|
144
150
|
# => [a, b, c]
|
145
|
-
|
151
|
+
store[1..3, 6]
|
146
152
|
# => [a, b, c, f]
|
147
153
|
```
|
148
154
|
|
@@ -155,25 +161,25 @@ If one item is given, it is deleted and returned.
|
|
155
161
|
If multiple items are given, they are deleted and returned as an array.
|
156
162
|
|
157
163
|
```ruby
|
158
|
-
|
164
|
+
store.delete_item(a)
|
159
165
|
# => a
|
160
|
-
|
166
|
+
store.delete_items(b, c, d)
|
161
167
|
# => [b, c, d]
|
162
|
-
|
168
|
+
store.delete(e, f, g)
|
163
169
|
# => [e, f, g]
|
164
170
|
```
|
165
171
|
|
166
172
|
This is considered the default use case and therefore also available as `delete`.
|
167
173
|
|
168
174
|
`delete_keys` (or `delete_key`) deletes items by key and returns them.
|
169
|
-
Again, one or multiple items can be deleted at a time and even ranges are
|
175
|
+
Again, one or multiple items can be deleted at a time and even ranges are accepted.
|
170
176
|
|
171
177
|
```ruby
|
172
|
-
|
178
|
+
store.delete_key(1)
|
173
179
|
# => a
|
174
|
-
|
180
|
+
store.delete_keys(2, 3, 4)
|
175
181
|
# => [b, c, d]
|
176
|
-
|
182
|
+
store.delete_keys(5..7, 9)
|
177
183
|
# => [e, f, g, i]
|
178
184
|
```
|
179
185
|
|
@@ -221,10 +227,10 @@ The hash maps attributes names to conditions that should be tested.
|
|
221
227
|
Conditions are evaluated using the `===` operator and can be virtually anything:
|
222
228
|
|
223
229
|
```ruby
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
230
|
+
store.find(name: "Fred", age: 25)
|
231
|
+
store.find(name: /red/i, age: 10..30)
|
232
|
+
store.find(child: MyClass)
|
233
|
+
store.find(child: -> child { child.valid? })
|
228
234
|
```
|
229
235
|
|
230
236
|
Additional types can be used in conditions by supporting the `===` operator. For example:
|
@@ -236,20 +242,20 @@ class Array
|
|
236
242
|
end
|
237
243
|
end
|
238
244
|
|
239
|
-
|
245
|
+
store.find age: [23, 25, 27]
|
240
246
|
```
|
241
247
|
|
242
248
|
The block is invoked with every item and can do more complex tests.
|
243
249
|
Its return value is interpreted as a boolean value:
|
244
250
|
|
245
251
|
```ruby
|
246
|
-
|
252
|
+
store.find { |item| item.age - item.child.age > 20 }
|
247
253
|
```
|
248
254
|
|
249
255
|
In addition to the evaluation logic, the arrays returned by all variants of `find` can be merged:
|
250
256
|
|
251
257
|
```ruby
|
252
|
-
|
258
|
+
store.find(...) | store.find(...) | store.find(...)
|
253
259
|
```
|
254
260
|
|
255
261
|
Note that the pipe operator `|` already eliminates duplicates:
|
@@ -263,28 +269,43 @@ Note that the pipe operator `|` already eliminates duplicates:
|
|
263
269
|
|
264
270
|
### Serialization
|
265
271
|
|
272
|
+
MemStore support various ways of de-/serializing the data store.
|
273
|
+
|
274
|
+
- `ObjectStore` supports binary format.
|
275
|
+
- `HashStore` supports binary format, hash, [YAML](http://yaml.org/), [JSON](http://www.json.org/) and [MessagePack](http://msgpack.org/).
|
276
|
+
|
277
|
+
**Important:** When file IO or deserialization fails, all variants of `from_*` return `nil`.
|
278
|
+
|
279
|
+
The following style ensures that there will be a (correctly configured) data store:
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
store = MemStore.from_file(file) || MemStore::HashStore(key, items)
|
283
|
+
```
|
284
|
+
|
266
285
|
#### Binary
|
267
286
|
|
268
|
-
|
287
|
+
Both `ObjectStore` and `HashStore` can easily be serialized to and from binary format:
|
269
288
|
|
270
289
|
```ruby
|
271
|
-
|
290
|
+
store.to_binary
|
291
|
+
# => binary string
|
292
|
+
store.to_file(file)
|
272
293
|
# => number of bytes written
|
273
|
-
MemStore.
|
274
|
-
# => instance of ObjectStore or HashStore
|
294
|
+
MemStore.from_binary(binary)
|
295
|
+
# => instance of ObjectStore or HashStore or nil
|
296
|
+
MemStore.from_file(file)
|
297
|
+
# => instance of ObjectStore or HashStore or nil
|
275
298
|
```
|
276
299
|
|
277
|
-
MemStore will automatically restore the correct class (`ObjectStore`/`HashStore`), key and items.
|
278
|
-
|
279
300
|
#### Hash
|
280
301
|
|
281
302
|
`HashStore` can be converted to and from a hash:
|
282
303
|
|
283
304
|
```ruby
|
284
|
-
|
305
|
+
store.to_hash
|
285
306
|
# => { key: ..., items: { ... } }
|
286
|
-
MemStore::HashStore.from_hash(
|
287
|
-
# => instance of HashStore
|
307
|
+
MemStore::HashStore.from_hash(hash)
|
308
|
+
# => instance of HashStore or nil
|
288
309
|
```
|
289
310
|
|
290
311
|
#### YAML
|
@@ -294,14 +315,14 @@ MemStore::HashStore.from_hash(h)
|
|
294
315
|
```ruby
|
295
316
|
require "memstore/yaml" # requires "yaml"
|
296
317
|
|
297
|
-
|
318
|
+
store.to_yaml
|
298
319
|
# => YAML string
|
299
|
-
|
320
|
+
store.to_yaml_file(file)
|
300
321
|
# => number of bytes written
|
301
322
|
MemStore::HashStore.from_yaml(yaml)
|
302
|
-
# => instance of HashStore
|
323
|
+
# => instance of HashStore or nil
|
303
324
|
MemStore::HashStore.from_yaml_file(file)
|
304
|
-
# => instance of HashStore
|
325
|
+
# => instance of HashStore or nil
|
305
326
|
```
|
306
327
|
|
307
328
|
De/serialization is seamless since YAML can handle symbols and non-string keys (i.e. Psych converts them correctly).
|
@@ -313,34 +334,34 @@ De/serialization is seamless since YAML can handle symbols and non-string keys (
|
|
313
334
|
```ruby
|
314
335
|
require "memstore/json" # requires "json"
|
315
336
|
|
316
|
-
|
337
|
+
store.to_json
|
317
338
|
# => JSON string
|
318
|
-
|
339
|
+
store.to_json_file(file)
|
319
340
|
# => number of bytes written
|
320
341
|
MemStore::HashStore.from_json(json)
|
321
|
-
# => instance of HashStore
|
342
|
+
# => instance of HashStore or nil
|
322
343
|
MemStore::HashStore.from_json_file(file)
|
323
|
-
# => instance of HashStore
|
344
|
+
# => instance of HashStore or nil
|
324
345
|
```
|
325
346
|
|
326
347
|
**Important:** Symbols will be converted to strings and JSON only allows string keys.
|
327
348
|
|
328
349
|
```ruby
|
329
|
-
|
330
|
-
|
331
|
-
|
350
|
+
store = MemStore::HashStore.new(:id)
|
351
|
+
store << { id: 1 }
|
352
|
+
store.to_hash
|
332
353
|
# => { :key => :id, :items => { 1 => { :id => 1 } } }
|
333
|
-
|
334
|
-
|
354
|
+
store = MemStore::HashStore.from_json(store.to_json)
|
355
|
+
store.to_hash
|
335
356
|
# => { :key => "id", :items => { "1" => { "id" => 1 } } }
|
336
357
|
```
|
337
358
|
|
338
359
|
The following style ensures consistent access before and after serialization:
|
339
360
|
|
340
361
|
```ruby
|
341
|
-
|
342
|
-
|
343
|
-
|
362
|
+
store = MemStore::HashStore.new("id")
|
363
|
+
store << { "id" => "1" }
|
364
|
+
store["1"]
|
344
365
|
# => { "id" => "1" }
|
345
366
|
```
|
346
367
|
|
@@ -351,41 +372,47 @@ mb["1"]
|
|
351
372
|
```ruby
|
352
373
|
require "memstore/msgpack" # requires "msgpack"
|
353
374
|
|
354
|
-
|
375
|
+
store.to_msgpack
|
355
376
|
# => MessagePack binary format
|
356
|
-
|
377
|
+
store.to_msgpack_file(file)
|
357
378
|
# => number of bytes written
|
358
379
|
MemStore::HashStore.from_msgpack(msgpack)
|
359
|
-
# => instance of HashStore
|
380
|
+
# => instance of HashStore or nil
|
360
381
|
MemStore::HashStore.from_msgpack_file(file)
|
361
|
-
# => instance of HashStore
|
382
|
+
# => instance of HashStore or nil
|
362
383
|
```
|
363
384
|
|
364
385
|
**Important:** Symbols will be converted to strings but non-string keys are allowed.
|
365
386
|
|
366
387
|
```ruby
|
367
|
-
|
368
|
-
|
369
|
-
|
388
|
+
store = MemStore::HashStore.new(:id)
|
389
|
+
store << { id: 1 }
|
390
|
+
store.to_hash
|
370
391
|
# => { :key => :id, :items => { 1 => { :id => 1 } } }
|
371
|
-
|
372
|
-
|
392
|
+
store = MemStore::HashStore.from_msgpack(store.to_msgpack)
|
393
|
+
store.to_hash
|
373
394
|
# => { :key => "id", :items => { 1 => { "id" => 1 } } }
|
374
395
|
```
|
375
396
|
|
376
397
|
The following style ensures consistent access before and after serialization:
|
377
398
|
|
378
399
|
```ruby
|
379
|
-
|
380
|
-
|
381
|
-
|
400
|
+
store = MemStore::HashStore.new("id")
|
401
|
+
store << { "id" => 1 }
|
402
|
+
store[1]
|
382
403
|
# => { "id" => 1 }
|
383
404
|
```
|
384
405
|
|
385
406
|
## Contributing
|
386
407
|
|
387
|
-
1. Fork it
|
388
|
-
2. Create
|
389
|
-
|
390
|
-
|
391
|
-
|
408
|
+
1. Fork it on [GitHub](https://github.com/sklppr/memstore).
|
409
|
+
2. Create a feature branch containing your changes:
|
410
|
+
|
411
|
+
```sh
|
412
|
+
$ git checkout -b feature/my-new-feature
|
413
|
+
# code, code, code
|
414
|
+
$ git commit -am "Add some feature"
|
415
|
+
$ git push origin feature/my-new-feature
|
416
|
+
```
|
417
|
+
|
418
|
+
3. Create a Pull Request on [GitHub](https://github.com/sklppr/memstore).
|
data/lib/memstore.rb
CHANGED
@@ -2,12 +2,16 @@ require "memstore/version"
|
|
2
2
|
|
3
3
|
module MemStore
|
4
4
|
|
5
|
-
def self.new(key=nil)
|
6
|
-
ObjectStore.new(key)
|
5
|
+
def self.new(key=nil, items={})
|
6
|
+
ObjectStore.new(key, items)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_binary(binary)
|
10
|
+
begin Marshal.load(binary) rescue nil end
|
7
11
|
end
|
8
12
|
|
9
13
|
def self.from_file(file)
|
10
|
-
|
14
|
+
begin self.from_binary(IO.read(file)) rescue nil end
|
11
15
|
end
|
12
16
|
|
13
17
|
class ObjectStore
|
@@ -32,7 +36,7 @@ module MemStore
|
|
32
36
|
def [](*keys)
|
33
37
|
return @items[keys.first] if keys.length == 1 && !keys.first.is_a?(Range)
|
34
38
|
keys.inject [] do |items, key|
|
35
|
-
if key.is_a?
|
39
|
+
if key.is_a?(Range) then key.inject(items) { |i, k| i << @items[k] }
|
36
40
|
else items << @items[key] end
|
37
41
|
end
|
38
42
|
end
|
@@ -51,7 +55,7 @@ module MemStore
|
|
51
55
|
def delete_keys(*keys)
|
52
56
|
return @items.delete(keys.first) if keys.length == 1 && !keys.first.is_a?(Range)
|
53
57
|
keys.inject [] do |items, key|
|
54
|
-
if key.is_a?
|
58
|
+
if key.is_a?(Range) then key.inject(items) { |i, k| i << @items.delete(k) }
|
55
59
|
else items << @items.delete(key) end
|
56
60
|
end
|
57
61
|
end
|
@@ -99,8 +103,12 @@ module MemStore
|
|
99
103
|
all.detect { |item| instance_exec(item, conditions, block, &FIND_NONE) }
|
100
104
|
end
|
101
105
|
|
106
|
+
def to_binary
|
107
|
+
Marshal.dump(self)
|
108
|
+
end
|
109
|
+
|
102
110
|
def to_file(file)
|
103
|
-
IO.write
|
111
|
+
IO.write(file, self.to_binary)
|
104
112
|
end
|
105
113
|
|
106
114
|
private
|
@@ -138,7 +146,14 @@ module MemStore
|
|
138
146
|
class HashStore < ObjectStore
|
139
147
|
|
140
148
|
def self.from_hash(hash)
|
141
|
-
|
149
|
+
begin
|
150
|
+
key = hash[:key] || hash["key"]
|
151
|
+
items = hash[:items] || hash["items"]
|
152
|
+
return nil if key.nil? || items.nil?
|
153
|
+
self.new(key, items)
|
154
|
+
rescue
|
155
|
+
nil
|
156
|
+
end
|
142
157
|
end
|
143
158
|
|
144
159
|
def initialize(key=nil, items={})
|
data/lib/memstore/json.rb
CHANGED
@@ -9,15 +9,15 @@ module MemStore
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_json_file(file)
|
12
|
-
IO.write
|
12
|
+
IO.write(file, self.to_json)
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.from_json(json)
|
16
|
-
self.from_hash
|
16
|
+
begin self.from_hash(JSON.parse(json)) rescue nil end
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.from_json_file(file)
|
20
|
-
self.from_json
|
20
|
+
begin self.from_json(IO.read(file)) rescue nil end
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
data/lib/memstore/msgpack.rb
CHANGED
@@ -9,15 +9,15 @@ module MemStore
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_msgpack_file(file)
|
12
|
-
IO.write
|
12
|
+
IO.write(file, self.to_msgpack)
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.from_msgpack(msgpack)
|
16
|
-
self.from_hash
|
16
|
+
begin self.from_hash(MessagePack.unpack(msgpack)) rescue nil end
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.from_msgpack_file(file)
|
20
|
-
self.from_msgpack
|
20
|
+
begin self.from_msgpack(IO.read(file)) rescue nil end
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
data/lib/memstore/version.rb
CHANGED
data/lib/memstore/yaml.rb
CHANGED
@@ -9,15 +9,15 @@ module MemStore
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_yaml_file(file)
|
12
|
-
IO.write
|
12
|
+
IO.write(file, self.to_yaml)
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.from_yaml(yaml)
|
16
|
-
self.from_hash
|
16
|
+
begin self.from_hash(YAML.load(yaml)) rescue nil end
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.from_yaml_file(file)
|
20
|
-
self.from_yaml
|
20
|
+
begin self.from_yaml(IO.read(file)) rescue nil end
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
data/spec/memstore_json_spec.rb
CHANGED
@@ -6,26 +6,26 @@ require "memstore"
|
|
6
6
|
require "memstore/json"
|
7
7
|
|
8
8
|
describe MemStore::HashStore do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
|
10
|
+
before do
|
11
|
+
@key = "id"
|
12
|
+
@store = MemStore::HashStore.new(@key)
|
13
|
+
10.times { |i| @store << { "id" => i.to_s } }
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
it "can be converted to and from JSON" do
|
17
|
+
restored = MemStore::HashStore.from_json(@store.to_json)
|
18
|
+
restored.items.must_equal @store.items
|
19
|
+
restored.instance_variable_get(:@key).must_equal @key
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
it "can be serialized to and deserialized from a JSON file" do
|
23
|
+
tmp = Tempfile.new("memstore")
|
24
|
+
@store.to_json_file(tmp)
|
25
|
+
restored = MemStore::HashStore.from_json_file(tmp)
|
26
|
+
restored.items.must_equal @store.items
|
27
|
+
restored.instance_variable_get(:@key).must_equal @key
|
28
|
+
tmp.unlink
|
29
|
+
end
|
30
30
|
|
31
31
|
end
|
@@ -6,26 +6,26 @@ require "memstore"
|
|
6
6
|
require "memstore/msgpack"
|
7
7
|
|
8
8
|
describe MemStore::HashStore do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
|
10
|
+
before do
|
11
|
+
@key = "id"
|
12
|
+
@store = MemStore::HashStore.new(@key)
|
13
|
+
10.times { |i| @store << { "id" => i } }
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
it "can be converted to and from MessagePack" do
|
17
|
+
restored = MemStore::HashStore.from_msgpack(@store.to_msgpack)
|
18
|
+
restored.items.must_equal @store.items
|
19
|
+
restored.instance_variable_get(:@key).must_equal @key
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
it "can be serialized to and deserialized from a MessagePack file" do
|
23
|
+
tmp = Tempfile.new("memstore")
|
24
|
+
@store.to_msgpack_file(tmp)
|
25
|
+
restored = MemStore::HashStore.from_msgpack_file(tmp)
|
26
|
+
restored.items.must_equal @store.items
|
27
|
+
restored.instance_variable_get(:@key).must_equal @key
|
28
|
+
tmp.unlink
|
29
|
+
end
|
30
30
|
|
31
31
|
end
|
data/spec/memstore_spec.rb
CHANGED
@@ -4,189 +4,235 @@ require "minitest/autorun"
|
|
4
4
|
require "tempfile"
|
5
5
|
require "memstore"
|
6
6
|
|
7
|
+
class Dummy
|
8
|
+
|
9
|
+
attr_accessor :id, :name, :child
|
10
|
+
|
11
|
+
def initialize(id, name=nil, child=nil)
|
12
|
+
@id, @name, @child = id, name, child
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(obj)
|
16
|
+
obj.is_a?(Dummy) &&
|
17
|
+
obj.id == @id &&
|
18
|
+
obj.name == @name &&
|
19
|
+
obj.child == @child
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
7
24
|
describe MemStore::ObjectStore do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
25
|
+
|
26
|
+
it "can be instantiated with items" do
|
27
|
+
h = { a: 1, b: 2, c: 3 }
|
28
|
+
store = MemStore::ObjectStore.new(nil, h)
|
29
|
+
store.items.must_equal h
|
30
|
+
end
|
31
|
+
|
32
|
+
it "is the default when instantiating MemStore" do
|
33
|
+
MemStore.new.must_be_instance_of MemStore::ObjectStore
|
34
|
+
end
|
35
|
+
|
36
|
+
it "indexes items by Object#hash by default" do
|
37
|
+
o = Object.new
|
38
|
+
store = MemStore::ObjectStore.new.insert(o)
|
39
|
+
store.items[o.hash].must_equal o
|
40
|
+
end
|
41
|
+
|
42
|
+
it "indexes items using a custom key" do
|
43
|
+
o = Dummy.new("custom key")
|
44
|
+
store = MemStore::ObjectStore.new(:id).insert(o)
|
45
|
+
store.items[o.id].must_equal o
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can be converted to and from binary" do
|
49
|
+
o = Dummy.new("custom key")
|
50
|
+
store = MemStore::ObjectStore.new(:id).insert(o)
|
51
|
+
restored = MemStore.from_binary(store.to_binary)
|
52
|
+
restored.must_be_instance_of MemStore::ObjectStore
|
53
|
+
restored.items.must_equal store.items
|
54
|
+
restored.instance_variable_get(:@key).must_equal :id
|
55
|
+
end
|
56
|
+
|
57
|
+
it "can be serialized to and deserialized from a binary file" do
|
58
|
+
tmp = Tempfile.new("memstore")
|
59
|
+
MemStore::ObjectStore.new.to_file tmp
|
60
|
+
MemStore.from_file(tmp).must_be_instance_of MemStore::ObjectStore
|
61
|
+
tmp.unlink
|
62
|
+
end
|
37
63
|
|
38
64
|
end
|
39
65
|
|
40
66
|
describe MemStore::HashStore do
|
41
67
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
68
|
+
it "can be instantiated with items" do
|
69
|
+
h = { a: 1, b: 2, c: 3 }
|
70
|
+
store = MemStore::HashStore.new(nil, h)
|
71
|
+
store.items.must_equal h
|
72
|
+
end
|
73
|
+
|
74
|
+
it "indexes items by Object#hash by default" do
|
75
|
+
h = {}
|
76
|
+
store = MemStore::HashStore.new.insert(h)
|
77
|
+
store.items[h.hash].must_equal h
|
78
|
+
end
|
79
|
+
|
80
|
+
it "indexes items using a custom key" do
|
81
|
+
h = { id: "custom key" }
|
82
|
+
store = MemStore::HashStore.new(:id).insert(h)
|
83
|
+
store.items[h[:id]].must_equal h
|
84
|
+
end
|
85
|
+
|
86
|
+
it "can be converted to and from binary" do
|
87
|
+
store = MemStore::HashStore.new(:id)
|
88
|
+
10.times { |i| store << { id: i } }
|
89
|
+
restored = MemStore.from_binary(store.to_binary)
|
90
|
+
restored.must_be_instance_of MemStore::HashStore
|
91
|
+
restored.items.must_equal store.items
|
92
|
+
restored.instance_variable_get(:@key).must_equal :id
|
93
|
+
end
|
94
|
+
|
95
|
+
it "can be serialized to and deserialized from a binary file" do
|
96
|
+
tmp = Tempfile.new("memstore")
|
97
|
+
MemStore::HashStore.new.to_file tmp
|
98
|
+
MemStore.from_file(tmp).must_be_instance_of MemStore::HashStore
|
99
|
+
tmp.unlink
|
100
|
+
end
|
101
|
+
|
102
|
+
it "can be converted to and from a hash" do
|
103
|
+
store = MemStore::HashStore.new(:id)
|
104
|
+
10.times { |i| store << { id: i } }
|
105
|
+
restored = MemStore::HashStore.from_hash(store.to_hash)
|
106
|
+
restored.items.must_equal store.items
|
107
|
+
restored.instance_variable_get(:@key).must_equal :id
|
108
|
+
end
|
109
|
+
|
110
|
+
it "returns nil when conversion from hash fails" do
|
111
|
+
MemStore::HashStore.from_hash(nil).must_equal nil
|
112
|
+
MemStore::HashStore.from_hash({}).must_equal nil
|
113
|
+
end
|
74
114
|
|
75
115
|
end
|
76
116
|
|
77
117
|
describe MemStore do
|
78
118
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
119
|
+
before do
|
120
|
+
@store = MemStore.new(:to_i)
|
121
|
+
# Use float as objects and integer as key
|
122
|
+
10.times { |i| @store << i.to_f }
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns a single item by itself" do
|
126
|
+
@store[3].must_equal 3.0
|
127
|
+
end
|
128
|
+
|
129
|
+
it "returns multiple items as an array" do
|
130
|
+
@store[3, 4, 5, 6].must_equal [3.0, 4.0, 5.0, 6.0]
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns multiple items using a Range as an array" do
|
134
|
+
@store[0..9].must_equal @store.all
|
135
|
+
end
|
136
|
+
|
137
|
+
it "deletes a single item by reference and returns it by itself" do
|
138
|
+
@store.delete_item(3.0).must_equal 3.0
|
139
|
+
end
|
140
|
+
|
141
|
+
it "deletes multiple items by reference and returns them" do
|
142
|
+
@store.delete_items(3.0, 4.0, 5.0, 6.0).must_equal [3.0, 4.0, 5.0, 6.0]
|
143
|
+
@store.all.must_equal [0.0, 1.0, 2.0, 7.0, 8.0, 9.0]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "deletes a single item by key and returns it by itself" do
|
147
|
+
@store.delete_key(3).must_equal 3.0
|
148
|
+
end
|
149
|
+
|
150
|
+
it "deletes multiple items by key and returns them as an array" do
|
151
|
+
@store.delete_keys(3, 4, 5, 6).must_equal [3.0, 4.0, 5.0, 6.0]
|
152
|
+
@store.all.must_equal [0.0, 1.0, 2.0, 7.0, 8.0, 9.0]
|
153
|
+
end
|
154
|
+
|
155
|
+
it "deletes multiple items by key using a Range and returns them as an array" do
|
156
|
+
@store.delete_keys(3..6).must_equal [3.0, 4.0, 5.0, 6.0]
|
157
|
+
@store.all.must_equal [0.0, 1.0, 2.0, 7.0, 8.0, 9.0]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "can be serialized to and deserialized from a binary file" do
|
161
|
+
tmp = Tempfile.new("memstore")
|
162
|
+
@store.to_file tmp
|
163
|
+
MemStore.from_file(tmp).items.must_equal @store.items
|
164
|
+
tmp.unlink
|
165
|
+
end
|
166
|
+
|
167
|
+
it "returns nil when conversion from binary fails" do
|
168
|
+
MemStore.from_binary(nil).must_equal nil
|
169
|
+
end
|
170
|
+
|
171
|
+
it "returns nil when deserialization from binary file fails" do
|
172
|
+
MemStore.from_file("does_not_exist").must_equal nil
|
173
|
+
end
|
126
174
|
|
127
175
|
end
|
128
176
|
|
129
|
-
Dummy = Struct.new(:id, :name, :child)
|
130
|
-
|
131
177
|
describe MemStore do
|
132
178
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
179
|
+
before do
|
180
|
+
@store = MemStore.new
|
181
|
+
strings = %w(foo moo boo faa maa baa foa moa boa lao)
|
182
|
+
classes = [String, Array]
|
183
|
+
10.times do |i|
|
184
|
+
@store << Dummy.new(i, strings[i], classes[i%2].new)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it "finds all items fulfilling all conditions" do
|
189
|
+
matches = @store.find_all(id: 3..7, child: String)
|
190
|
+
matches.collect{ |m| m.id }.must_equal [4, 6]
|
191
|
+
end
|
192
|
+
|
193
|
+
it "finds all items fulfilling at least one condition" do
|
194
|
+
matches = @store.find_any(id: 3..7, child: String)
|
195
|
+
matches.collect{ |m| m.id }.must_equal [0, 2, 3, 4, 5, 6, 7, 8]
|
196
|
+
end
|
197
|
+
|
198
|
+
it "finds all items fulfilling exactly one condition" do
|
199
|
+
matches = @store.find_one(name: /o/, child: String)
|
200
|
+
matches.collect{ |m| m.id }.must_equal [1, 4, 7, 9]
|
201
|
+
end
|
202
|
+
|
203
|
+
it "finds all items violating at least one condition" do
|
204
|
+
matches = @store.find_not_all(name: /o/, child: String)
|
205
|
+
matches.collect{ |m| m.id }.must_equal [1, 3, 4, 5, 7, 9]
|
206
|
+
end
|
207
|
+
|
208
|
+
it "finds all items violating all conditions" do
|
209
|
+
matches = @store.find_none(name: /o/, child: String)
|
210
|
+
matches.collect{ |m| m.id }.must_equal [3, 5]
|
211
|
+
end
|
212
|
+
|
213
|
+
it "finds the first item fulfilling all conditions" do
|
214
|
+
match = @store.first_all(id: 3..7, child: String)
|
215
|
+
match.id.must_equal 4
|
216
|
+
end
|
217
|
+
|
218
|
+
it "finds the first item fulfilling at least one condition" do
|
219
|
+
match = @store.first_any(id: 3..7, child: String)
|
220
|
+
match.id.must_equal 0
|
221
|
+
end
|
222
|
+
|
223
|
+
it "finds the first item fulfilling exactly one condition" do
|
224
|
+
match = @store.first_one(name: /o/, child: String)
|
225
|
+
match.id.must_equal 1
|
226
|
+
end
|
227
|
+
|
228
|
+
it "finds the first item violating at least one condition" do
|
229
|
+
match = @store.first_not_all(name: /o/, child: String)
|
230
|
+
match.id.must_equal 1
|
231
|
+
end
|
232
|
+
|
233
|
+
it "finds the first item violating all conditions" do
|
234
|
+
match = @store.first_none(name: /o/, child: String)
|
235
|
+
match.id.must_equal 3
|
236
|
+
end
|
191
237
|
|
192
238
|
end
|
data/spec/memstore_yaml_spec.rb
CHANGED
@@ -6,26 +6,26 @@ require "memstore"
|
|
6
6
|
require "memstore/yaml"
|
7
7
|
|
8
8
|
describe MemStore::HashStore do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
|
10
|
+
before do
|
11
|
+
@key = :id
|
12
|
+
@store = MemStore::HashStore.new(@key)
|
13
|
+
10.times { |i| @store << { id: i } }
|
14
|
+
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
it "can be converted to and from YAML" do
|
17
|
+
restored = MemStore::HashStore.from_yaml(@store.to_yaml)
|
18
|
+
restored.items.must_equal @store.items
|
19
|
+
restored.instance_variable_get(:@key).must_equal @key
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
it "can be serialized to and deserialized from a YAML file" do
|
23
|
+
tmp = Tempfile.new("memstore")
|
24
|
+
@store.to_yaml_file(tmp)
|
25
|
+
restored = MemStore::HashStore.from_yaml_file(tmp)
|
26
|
+
restored.items.must_equal @store.items
|
27
|
+
restored.instance_variable_get(:@key).must_equal @key
|
28
|
+
tmp.unlink
|
29
|
+
end
|
30
30
|
|
31
31
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memstore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: MemStore is a simple in-memory data store that supports adding, retrieving
|
15
15
|
and deleting items as well as complex search queries and easy serialization.
|