memstore 1.2.1 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rdoc_options +19 -0
- data/README.md +148 -324
- data/lib/memstore.rb +2 -28
- data/lib/memstore/core.rb +129 -0
- data/lib/memstore/queries.rb +157 -117
- data/lib/memstore/refinements.rb +9 -0
- data/lib/memstore/version.rb +2 -4
- data/memstore.gemspec +13 -14
- data/spec/core_methods_spec.rb +67 -0
- data/spec/core_setup_spec.rb +66 -0
- data/spec/queries_spec.rb +66 -38
- metadata +22 -32
- data/lib/memstore/hashstore.rb +0 -99
- data/lib/memstore/json.rb +0 -31
- data/lib/memstore/msgpack.rb +0 -31
- data/lib/memstore/objectstore.rb +0 -259
- data/lib/memstore/yaml.rb +0 -35
- data/spec/hashstore_spec.rb +0 -67
- data/spec/json_spec.rb +0 -50
- data/spec/msgpack_spec.rb +0 -50
- data/spec/objectstore_spec.rb +0 -117
- data/spec/yaml_spec.rb +0 -50
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e09df24ba555d687f405c140c6ccbacaf4b42963
|
4
|
+
data.tar.gz: 462f77bf9be2d36d7cd98f770c59b65149fdd5e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 827ae9142869655c018017fe3ca335333561dc9504ab0ef46b5e0dae02b21b3b8f9c83bdad00090a6e9fc2c1ca9fc4fa289795cc1cdd5465a12680b07d037efb
|
7
|
+
data.tar.gz: 3c92d873c50ddbb71f8ec8a67190edc65f003a67221526ceae430ee6446fb3a3bc6a3714ee2d5b76c07448265af1862e99668eb638b8fabd3990f719d7f38ec5
|
data/.gitignore
CHANGED
data/.rdoc_options
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
--- !ruby/object:RDoc::Options
|
2
|
+
encoding: UTF-8
|
3
|
+
charset: UTF-8
|
4
|
+
static_path: []
|
5
|
+
rdoc_include:
|
6
|
+
- .
|
7
|
+
exclude:
|
8
|
+
- spec
|
9
|
+
- Gemfile
|
10
|
+
- Gemfile.lock
|
11
|
+
- Rakefile
|
12
|
+
- created.rid
|
13
|
+
main_page: README.md
|
14
|
+
markup: tomdoc
|
15
|
+
hyperlink_all: false
|
16
|
+
line_numbers: false
|
17
|
+
show_hash: false
|
18
|
+
tab_width: 2
|
19
|
+
visibility: :protected
|
data/README.md
CHANGED
@@ -2,265 +2,134 @@
|
|
2
2
|
|
3
3
|
*A simple in-memory data store.*
|
4
4
|
|
5
|
-
MemStore is a simple in-memory data store that supports
|
5
|
+
MemStore is a simple in-memory data store that supports complex search queries.
|
6
6
|
|
7
|
-
It’s not in any way supposed to be a
|
7
|
+
It’s not in any way supposed to be a database. However, it can be used instead of database in small applications or prototypes.
|
8
8
|
|
9
|
-
|
9
|
+
**Important: Ruby 2.1 is required.**
|
10
10
|
|
11
|
-
|
11
|
+
## Basics
|
12
12
|
|
13
|
-
|
14
|
-
gem "memstore"
|
15
|
-
```
|
16
|
-
|
17
|
-
And then execute:
|
18
|
-
|
19
|
-
```sh
|
20
|
-
$ bundle
|
21
|
-
```
|
22
|
-
|
23
|
-
Or install it yourself as:
|
24
|
-
|
25
|
-
```sh
|
26
|
-
$ gem install memstore
|
27
|
-
```
|
28
|
-
|
29
|
-
## Usage
|
30
|
-
|
31
|
-
- [Basics](#basics)
|
32
|
-
- [Objects vs. Hashes](#objects-vs-hashes)
|
33
|
-
- [Adding Items](#adding-items)
|
34
|
-
- [Retrieving Items](#retrieving-items)
|
35
|
-
- [Deleting Items](#deleting-items)
|
36
|
-
- [Search Queries](#search-queries)
|
37
|
-
- [Serialization](#serialization)
|
38
|
-
- [Binary](#binary)
|
39
|
-
- [Hash](#hash)
|
40
|
-
- [YAML](#yaml)
|
41
|
-
- [JSON](#json)
|
42
|
-
- [MessagePack](#messagepack)
|
43
|
-
- [Concurrent Access](#concurrent-access)
|
44
|
-
|
45
|
-
### Basics
|
46
|
-
|
47
|
-
Creating a data store is utterly simple:
|
13
|
+
Creating a data store is straightforward:
|
48
14
|
|
49
15
|
```ruby
|
50
16
|
store = MemStore.new
|
51
|
-
```
|
52
|
-
|
53
|
-
By default, objects are indexed using `Object#hash`.
|
54
|
-
|
55
|
-
If a different property should be used, it can be specified like this:
|
56
|
-
|
57
|
-
```ruby
|
58
|
-
store = MemStore.new(:id)
|
59
|
-
```
|
60
|
-
|
61
|
-
The property needs to be truly unique for all objects since it’s used as a hash key internally.
|
62
|
-
|
63
|
-
An items collection can also be provided on creation:
|
64
|
-
|
65
|
-
```ruby
|
66
|
-
store = MemStore.new(nil, { ... }) # to use Object.hash as key
|
67
|
-
store = MemStore.new(:id, { ... }) # to use custom key
|
68
|
-
```
|
69
|
-
|
70
|
-
The collection must be a hash that correctly maps the used key to each item.
|
71
|
-
|
72
|
-
#### Objects vs. Hashes
|
73
|
-
|
74
|
-
MemStore comes in two flavors: `ObjectStore` and `HashStore`.
|
75
|
-
|
76
|
-
They’re basically the same, but `ObjectStore` accesses items through `item.attribute` while `HashStore` accesses items through `item[attribute]`.
|
77
|
-
|
78
|
-
`ObjectStore` is the default variant:
|
79
|
-
|
80
|
-
```ruby
|
81
|
-
store = MemStore.new
|
82
|
-
# is equal to
|
83
|
-
store = MemStore::ObjectStore.new
|
84
|
-
```
|
85
|
-
|
86
|
-
`HashStore` needs to be created explicitly:
|
87
|
-
|
88
|
-
```ruby
|
89
|
-
store = MemStore::HashStore.new
|
90
|
-
```
|
91
|
-
|
92
|
-
If no key attribute is specified, `HashStore` will also use `Object#hash`.
|
93
|
-
|
94
|
-
#### Adding Items
|
95
|
-
|
96
|
-
`items` provides direct access to the internal items hash.
|
97
|
-
|
98
|
-
```ruby
|
99
|
-
store.items
|
100
|
-
# => {}
|
101
|
-
store.items = { 1 => a, 2 => b, 3 => c }
|
102
|
-
# => { 1 => a, 2 => b, 3 => c }
|
103
|
-
```
|
104
|
-
|
105
|
-
`insert` adds one or multiple items and returns the data store itself:
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
store.insert(a, b, c)
|
109
17
|
# => store
|
110
18
|
```
|
111
19
|
|
112
|
-
|
20
|
+
Adding items is equally simple. Add a single item using the shovel operator `<<` or multiple items using `add`:
|
113
21
|
|
114
22
|
```ruby
|
115
|
-
store
|
23
|
+
store << a
|
116
24
|
# => store
|
117
|
-
|
118
|
-
|
119
|
-
MemStore also supports the shovel operator `<<` for adding items.
|
120
|
-
Only one item can be added at a time but it’s chainable:
|
121
|
-
|
122
|
-
```ruby
|
123
|
-
store << a << b << c
|
25
|
+
store.add(a, b, c)
|
124
26
|
# => store
|
125
27
|
```
|
126
28
|
|
127
|
-
|
128
|
-
|
129
|
-
`length` (or `size`) returns the current number of items:
|
29
|
+
To make things easier, MemStore’s constructor takes a collection of items that will be added right away:
|
130
30
|
|
131
31
|
```ruby
|
132
|
-
store.
|
133
|
-
# =>
|
32
|
+
store = MemStore.new(items: [a, b, c])
|
33
|
+
# => store
|
134
34
|
```
|
135
35
|
|
136
|
-
|
137
|
-
If a single key is given, a single item will be returned.
|
138
|
-
If multiple keys are given, an array of items will be returned with `nil` when there is no item for a key.
|
36
|
+
You can access single items by their key (see [Customization](#customization)) using the bracket operator `[]` or multiple items using `get`:
|
139
37
|
|
140
38
|
```ruby
|
141
39
|
store[1]
|
142
40
|
# => a
|
143
|
-
store
|
41
|
+
store.get(1, 2, 3)
|
144
42
|
# => [a, b, c]
|
145
43
|
```
|
146
44
|
|
147
|
-
|
45
|
+
You can also get all items at once using `all` and a hash of all items with their keys using `items`:
|
148
46
|
|
149
47
|
```ruby
|
150
|
-
store
|
48
|
+
store.all
|
151
49
|
# => [a, b, c]
|
152
|
-
store
|
153
|
-
# =>
|
50
|
+
store.items
|
51
|
+
# => { 1 => a, 2 => b, 3 => c }
|
154
52
|
```
|
155
53
|
|
156
|
-
|
54
|
+
## Queries
|
157
55
|
|
158
|
-
|
159
|
-
This is considered the default use case and therefore also available as `delete`.
|
56
|
+
MemStore provides methods to find, count and delete items using complex queries:
|
160
57
|
|
161
|
-
|
162
|
-
|
58
|
+
- `find_*` returns all items matching the query
|
59
|
+
- `first_*` returns the first item matching the query
|
60
|
+
- `count_*` returns the number of items matching the query
|
61
|
+
- `delete_*` deletes and returns all items matching the query
|
163
62
|
|
164
|
-
|
165
|
-
store.delete_item(a)
|
166
|
-
# => a
|
167
|
-
store.delete_items(b, c, d)
|
168
|
-
# => [b, c, d]
|
169
|
-
store.delete(e, f, g)
|
170
|
-
# => [e, f, g]
|
171
|
-
```
|
172
|
-
|
173
|
-
This is considered the default use case and therefore also available as `delete`.
|
174
|
-
|
175
|
-
`delete_keys` (or `delete_key`) deletes items by key and returns them.
|
176
|
-
Again, one or multiple items can be deleted at a time and even ranges are accepted.
|
177
|
-
|
178
|
-
```ruby
|
179
|
-
store.delete_key(1)
|
180
|
-
# => a
|
181
|
-
store.delete_keys(2, 3, 4)
|
182
|
-
# => [b, c, d]
|
183
|
-
store.delete_keys(5..7, 9)
|
184
|
-
# => [e, f, g, i]
|
185
|
-
```
|
63
|
+
These methods have one of the following suffixes:
|
186
64
|
|
187
|
-
|
65
|
+
- `*_all` matches items *fulfilling all* conditions
|
66
|
+
- `*_any` matches items *fulfilling at least one* condition
|
67
|
+
- `*_one` matches items *fulfilling exactly one* condition
|
68
|
+
- `*_not_all` matches items *violating at least one* condition
|
69
|
+
- `*_none` matches items *violating all* conditions
|
188
70
|
|
189
|
-
|
190
|
-
|
191
|
-
- `find_all` (alias `find`)
|
192
|
-
- `find_any`
|
193
|
-
- `find_one`
|
194
|
-
- `find_not_all`
|
195
|
-
- `find_none`
|
196
|
-
- `first_all` (alias `first`)
|
197
|
-
- `first_any`
|
198
|
-
- `first_one`
|
199
|
-
- `first_not_all`
|
200
|
-
- `first_none`
|
201
|
-
- `count_all` (alias `count`)
|
202
|
-
- `count_any`
|
203
|
-
- `count_one`
|
204
|
-
- `count_not_all`
|
205
|
-
- `count_none`
|
206
|
-
|
207
|
-
The first part indicates what is returned:
|
208
|
-
|
209
|
-
- `find_*` returns all matches.
|
210
|
-
- `first_*` returns the first match.
|
211
|
-
- `count_*` returns the number of matches.
|
71
|
+
In other words:
|
212
72
|
|
213
|
-
|
73
|
+
- `all` means `condition && condition && ...`
|
74
|
+
- `any` means `condition || condition || ...`
|
75
|
+
- `one` means `condition ^ condition ^ ...`
|
76
|
+
- `not all` means `!(condition && condition && ...)` or `!condition || !condition || ...`
|
77
|
+
- `none` means `!(condition || condition || ...)` or `!condition && !condition && ...`
|
214
78
|
|
215
|
-
|
216
|
-
- `*_any` matches items *fulfilling at least one* condition.
|
217
|
-
- `*_one` matches items *fulfilling exactly one* condition.
|
218
|
-
- `*_not_all` matches items *violating at least one* condition.
|
219
|
-
- `*_none` matches items *violating all* conditions.
|
79
|
+
For convenience, there are aliases for the `*_all` variants:
|
220
80
|
|
221
|
-
|
81
|
+
- `find` is an alias of `find_all`
|
82
|
+
- `first` is an alias of `first_all`
|
83
|
+
- `count` is an alias of `count_all`
|
84
|
+
- `delete` is an alias of `delete_all`
|
222
85
|
|
223
|
-
|
224
|
-
- `any` means `condition || condition || ...`.
|
225
|
-
- `one` means `condition ^ condition ^ ...`.
|
226
|
-
- `not all` means `!(condition && condition && ...)` or `!condition || !condition || ...`.
|
227
|
-
- `none` means `!(condition || condition || ...)` or `!condition && !condition && ...`.
|
86
|
+
All methods take a hash of conditions and/or a block.
|
228
87
|
|
229
|
-
|
88
|
+
The hash is expected to map attributes (see [Customization](#customization)) to conditions.
|
89
|
+
Conditions are evaluated using the case equality operator: `condition === item`
|
230
90
|
|
231
|
-
|
232
|
-
Conditions are evaluated using the `===` operator and can be virtually anything:
|
91
|
+
This means conditions can be virtually anything:
|
233
92
|
|
234
93
|
```ruby
|
235
|
-
store.find(name: "
|
236
|
-
|
94
|
+
store.find(name: "John", age: 42)
|
95
|
+
# is equivalent to item.name == "John" && item.age == 42
|
96
|
+
store.find(name: /^Jo/, age: 23..42)
|
97
|
+
# is equivalent to /^Jo/ =~ item.name && (23..42).include?(item.age)
|
237
98
|
store.find(child: MyClass)
|
99
|
+
# is equivalent to item.child.kind_of?(MyClass)
|
238
100
|
store.find(child: -> child { child.valid? })
|
101
|
+
# is equivalent to proc.call(item.child)
|
239
102
|
```
|
240
103
|
|
241
|
-
|
104
|
+
You can enable additional types of conditions simply by implementing `===`.
|
105
|
+
For example, MemStore also supports arrays using an internal refinement:
|
242
106
|
|
243
107
|
```ruby
|
244
|
-
class Array
|
245
|
-
def ===(obj)
|
246
|
-
self.include?(obj)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
108
|
store.find(age: [23, 25, 27])
|
109
|
+
# is equivalent to [23, 25, 27].include?(item.age)
|
110
|
+
```
|
111
|
+
|
112
|
+
The implementation looks like this:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
refine Array do
|
116
|
+
def ===(obj)
|
117
|
+
include?(obj)
|
118
|
+
end
|
119
|
+
end
|
251
120
|
```
|
252
121
|
|
253
|
-
The block is invoked with the item *after* the conditions are evaluated.
|
122
|
+
The block is invoked with the item *after* the conditions are evaluated.
|
254
123
|
|
255
124
|
```ruby
|
256
125
|
store.find(age: 25) { |item| item.age - item.child.age > 20 }
|
257
|
-
# is
|
126
|
+
# is equivalent to item.age == 25 && item.age - item.child.age > 20
|
258
127
|
```
|
259
128
|
|
260
|
-
In addition to the
|
129
|
+
In addition to the query logic, you can merge the arrays returned by `find_*`:
|
261
130
|
|
262
131
|
```ruby
|
263
|
-
store.
|
132
|
+
store.find_all(...) | store.find_any(...) | store.find_none(...)
|
264
133
|
```
|
265
134
|
|
266
135
|
Note that the pipe operator `|` already eliminates duplicates:
|
@@ -270,173 +139,128 @@ Note that the pipe operator `|` already eliminates duplicates:
|
|
270
139
|
# => [a, b, c, d, e]
|
271
140
|
```
|
272
141
|
|
273
|
-
|
142
|
+
## Customization
|
274
143
|
|
275
|
-
|
144
|
+
### Default Behavior
|
276
145
|
|
277
|
-
|
278
|
-
- `HashStore` supports binary format, hash, [YAML](http://yaml.org/), [JSON](http://www.json.org/) and [MessagePack](http://msgpack.org/).
|
279
|
-
|
280
|
-
**Important:** When file IO or deserialization fails, all variants of `from_*` return `nil`.
|
281
|
-
|
282
|
-
The following style ensures that there will be a (correctly configured) data store:
|
146
|
+
By default, MemStore indexes items using `Object#hash`:
|
283
147
|
|
284
148
|
```ruby
|
285
|
-
store = MemStore.
|
149
|
+
store = MemStore.new
|
150
|
+
store << item
|
151
|
+
# calls item.hash to retrieve key
|
152
|
+
store[item.hash]
|
153
|
+
# => item
|
286
154
|
```
|
287
155
|
|
288
|
-
|
289
|
-
|
290
|
-
Both `ObjectStore` and `HashStore` can easily be serialized to and from binary format:
|
156
|
+
When you use `find_*`, `first_*`, `count_*` or `delete_*`, MemStore calls attributes as methods on your items using `Object#send`:
|
291
157
|
|
292
158
|
```ruby
|
293
|
-
store.
|
294
|
-
|
295
|
-
store.
|
296
|
-
#
|
297
|
-
MemStore.from_binary(binary)
|
298
|
-
# => instance of ObjectStore or HashStore or nil
|
299
|
-
MemStore.from_file(file)
|
300
|
-
# => instance of ObjectStore or HashStore or nil
|
159
|
+
store = MemStore.new
|
160
|
+
store << item
|
161
|
+
store.find(age: 42, name: "John")
|
162
|
+
# calls item.age and item.name to retrieve attributes
|
301
163
|
```
|
302
164
|
|
303
|
-
|
304
|
-
|
305
|
-
`HashStore` can be converted to and from a hash:
|
165
|
+
This means that it doesn’t make a difference whether you use strings or symbols in the conditions hash:
|
306
166
|
|
307
167
|
```ruby
|
308
|
-
store.
|
309
|
-
#
|
310
|
-
MemStore::HashStore.from_hash(hash)
|
311
|
-
# => instance of HashStore or nil
|
168
|
+
store.find("age" => 42, "name" => "John")
|
169
|
+
# calls item.age and item.name to retrieve attributes
|
312
170
|
```
|
313
171
|
|
314
|
-
|
172
|
+
*Note that using Strings will result in a performance penalty because `Object#send` expects Symbols.*
|
173
|
+
|
174
|
+
### Custom Key
|
315
175
|
|
316
|
-
|
176
|
+
You’ll probably want MemStore to use a specific attribute to index items.
|
177
|
+
This is possible using the `key` parameter when creating a data store:
|
317
178
|
|
318
179
|
```ruby
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
# => number of bytes written
|
325
|
-
MemStore::HashStore.from_yaml(yaml)
|
326
|
-
# => instance of HashStore or nil
|
327
|
-
MemStore::HashStore.from_yaml_file(file)
|
328
|
-
# => instance of HashStore or nil
|
180
|
+
store = MemStore.new(key: :id)
|
181
|
+
store << item
|
182
|
+
# calls item.id to retrieve key
|
183
|
+
store[item.id]
|
184
|
+
# => item
|
329
185
|
```
|
330
186
|
|
331
|
-
|
187
|
+
Whatever you provide as `key` will be treated as an attribute.
|
188
|
+
So, by default, the according method will be called on your item.
|
332
189
|
|
333
|
-
|
190
|
+
### Custom Access Method
|
334
191
|
|
335
|
-
|
192
|
+
If you want to change how attributes are accessed, you can use the `access` parameter when creating a data store:
|
336
193
|
|
337
194
|
```ruby
|
338
|
-
|
339
|
-
|
340
|
-
store
|
341
|
-
#
|
342
|
-
store.
|
343
|
-
#
|
344
|
-
MemStore::HashStore.from_json(json)
|
345
|
-
# => instance of HashStore or nil
|
346
|
-
MemStore::HashStore.from_json_file(file)
|
347
|
-
# => instance of HashStore or nil
|
195
|
+
store = MemStore.new(key: :id, access: :[])
|
196
|
+
# now you can store hashes, e.g. { id: 5, age: 42, name: "John" }
|
197
|
+
store << item
|
198
|
+
# calls item[:id] to retrieve key
|
199
|
+
store.find(age: 42, name: "John")
|
200
|
+
# calls item[:age] and item[:name] to retrieve attributes
|
348
201
|
```
|
349
202
|
|
350
|
-
|
203
|
+
If you provide a Symbol or String, it will be treated as a method name.
|
204
|
+
*Note that providing a String will result in a performance penalty because `Object#send` expects Symbols.*
|
351
205
|
|
352
|
-
|
353
|
-
|
354
|
-
store << { id: 1 }
|
355
|
-
store.to_hash
|
356
|
-
# => { :key => :id, :items => { 1 => { :id => 1 } } }
|
357
|
-
store = MemStore::HashStore.from_json(store.to_json)
|
358
|
-
store.to_hash
|
359
|
-
# => { :key => "id", :items => { "1" => { "id" => 1 } } }
|
360
|
-
```
|
361
|
-
|
362
|
-
The following style ensures consistent access before and after serialization:
|
206
|
+
To access an attribute, MemStore will call the according method on your item and pass the requested attribute to it.
|
207
|
+
This means `key` and attributes in the conditions hash must be whatever your method expects:
|
363
208
|
|
364
209
|
```ruby
|
365
|
-
|
366
|
-
store
|
367
|
-
store
|
368
|
-
#
|
210
|
+
# assuming that items have a method `get` that expects a String:
|
211
|
+
store = MemStore.new(key: "id", access: :get)
|
212
|
+
store << item
|
213
|
+
# calls item.get("id") to retrieve key
|
214
|
+
store.find("age" => 42, "name" => "John")
|
215
|
+
# calls item.get("age") and item.get("name") to retrieve attributes
|
369
216
|
```
|
370
217
|
|
371
|
-
|
218
|
+
### Advanced Customization
|
372
219
|
|
373
|
-
|
220
|
+
If you want to do something special to obtain a key, you can provide a Proc or Method.
|
221
|
+
It will be passed the item for which MemStore needs a key and is expected to return a truly unique identifier for that item:
|
374
222
|
|
375
223
|
```ruby
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
# => MessagePack binary format
|
380
|
-
store.to_msgpack_file(file)
|
381
|
-
# => number of bytes written
|
382
|
-
MemStore::HashStore.from_msgpack(msgpack)
|
383
|
-
# => instance of HashStore or nil
|
384
|
-
MemStore::HashStore.from_msgpack_file(file)
|
385
|
-
# => instance of HashStore or nil
|
386
|
-
```
|
387
|
-
|
388
|
-
**Important:** Symbols will be converted to strings but non-string keys are allowed.
|
224
|
+
def special_hash(item)
|
225
|
+
# ...
|
226
|
+
end
|
389
227
|
|
390
|
-
|
391
|
-
store = MemStore
|
392
|
-
|
393
|
-
store.
|
394
|
-
#
|
395
|
-
store = MemStore
|
396
|
-
store.to_hash
|
397
|
-
# => { :key => "id", :items => { 1 => { "id" => 1 } } }
|
228
|
+
# lambda:
|
229
|
+
store = MemStore.new(key: -> item { special_hash(item) })
|
230
|
+
# Proc:
|
231
|
+
store = MemStore.new(key: Proc.new { |item| special_hash(item) })
|
232
|
+
# Method:
|
233
|
+
store = MemStore.new(key: method(:special_hash))
|
398
234
|
```
|
399
235
|
|
400
|
-
|
236
|
+
Note that this is also a way to circumvent the access method for attributes.
|
237
|
+
For example, you might want to use one method `get` to access all attributes but a different method `id` should be used for indexing:
|
401
238
|
|
402
239
|
```ruby
|
403
|
-
|
404
|
-
store
|
405
|
-
|
406
|
-
|
240
|
+
# this way, item.get(:id) would be used:
|
241
|
+
store = MemStore.new(access: :get, key: :id)
|
242
|
+
# circumvent the access method like this:
|
243
|
+
store = MemStore.new(access: :get, key: -> item { item.id })
|
244
|
+
# or even shorter:
|
245
|
+
store = MemStore.new(access: :get, key: Proc.new(&:id))
|
246
|
+
store << item
|
247
|
+
# calls item.id to retrieve key
|
248
|
+
store.find(age: 42, name: "John")
|
249
|
+
# calls item.get(:age) and item.get(:name) to retrieve attributes
|
407
250
|
```
|
408
251
|
|
409
|
-
|
410
|
-
|
411
|
-
To support concurrent access, e.g. when multiple threads or processes read and write the same data store file, there’s a `with_file` equivalent of `from_file` and `to_file`.
|
412
|
-
|
413
|
-
`with_file` takes a file (name) and a block. It then obtains an exclusive lock on that file, restores a data store from it or creates a new one, invokes the block with the data store and finally saves the data store back to the file. Optionally, key and/or items can be specified for when the data store is created instead of restored.
|
252
|
+
Likewise, you can provide a Proc or Method to be called when accessing attributes.
|
253
|
+
It will be passed both the item in question and the attribute to be retrieved and is expected to return the appropriate value:
|
414
254
|
|
415
255
|
```ruby
|
416
|
-
|
417
|
-
#
|
256
|
+
def special_accessor(item, attribute)
|
257
|
+
# ...
|
418
258
|
end
|
419
|
-
```
|
420
|
-
|
421
|
-
`MemStore.with_file` is a shortcut to `MemStore::ObjectStore.with_file`.
|
422
259
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
## Contributing
|
431
|
-
|
432
|
-
1. Fork it on [GitHub](https://github.com/sklppr/memstore).
|
433
|
-
2. Create a feature branch containing your changes:
|
434
|
-
|
435
|
-
```sh
|
436
|
-
$ git checkout -b feature/my-new-feature
|
437
|
-
# code, code, code
|
438
|
-
$ git commit -am "Add some feature"
|
439
|
-
$ git push origin feature/my-new-feature
|
440
|
-
```
|
441
|
-
|
442
|
-
3. Create a Pull Request on [GitHub](https://github.com/sklppr/memstore).
|
260
|
+
# lambda:
|
261
|
+
store = MemStore.new(access: -> item, attribute { special_accessor(item, attribute) })
|
262
|
+
# Proc:
|
263
|
+
store = MemStore.new(access: Proc.new { |item| special_accessor(item, attribute) })
|
264
|
+
# Method:
|
265
|
+
store = MemStore.new(access: method(:special_accessor))
|
266
|
+
```
|