memstore 1.2.1 → 2.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.
- 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
|
+
```
|