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
data/lib/memstore/hashstore.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module MemStore
|
4
|
-
|
5
|
-
# A HashStore accesses item attributes through item[attribute].
|
6
|
-
class HashStore < ObjectStore
|
7
|
-
|
8
|
-
# Initializes a HashStore.
|
9
|
-
#
|
10
|
-
# key - Optional Object used as an item’s key attribute (default: nil).
|
11
|
-
# When no key is specified, Object#hash will be used for uniquely identifying items.
|
12
|
-
# items - Optional Hash of items to initialize the data store with (default: empty Hash).
|
13
|
-
#
|
14
|
-
# Examples
|
15
|
-
#
|
16
|
-
# store = HashStore.new
|
17
|
-
# store = HashStore.new(:id)
|
18
|
-
# store = HashStore.new(nil, { a.hash => a, b.hash => b, c.hash => c })
|
19
|
-
# store = HashStore.new(:id, { 1 => a, 2 => b, 3 => c })
|
20
|
-
#
|
21
|
-
# Returns initialized ObjectStore.
|
22
|
-
def initialize(key=nil, items={})
|
23
|
-
@key, @items = key, items
|
24
|
-
end
|
25
|
-
|
26
|
-
# Returns data store as a Hash with the fields :key and :items.
|
27
|
-
def to_hash
|
28
|
-
{ key: @key, items: @items }
|
29
|
-
end
|
30
|
-
|
31
|
-
# Restores a data store from binary format.
|
32
|
-
#
|
33
|
-
# binary - Binary data containing a serialized instance of HashStore.
|
34
|
-
#
|
35
|
-
# Examples
|
36
|
-
#
|
37
|
-
# store = HashStore.from_binary(IO.read(file))
|
38
|
-
#
|
39
|
-
# Returns instance of HashStore
|
40
|
-
# or nil if marshalling failed or marshalled object isn’t a HashStore.
|
41
|
-
# Raises whatever Marshal::load raises.
|
42
|
-
def self.from_binary(binary)
|
43
|
-
restored = Marshal.load(binary) rescue nil
|
44
|
-
if restored.instance_of?(HashStore) then restored else nil end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Restores a data store from a Hash.
|
48
|
-
#
|
49
|
-
# hash - Hash that contains a serialized HashStore.
|
50
|
-
# It must have the fields :key/"key" and :items/"items".
|
51
|
-
#
|
52
|
-
# Examples
|
53
|
-
#
|
54
|
-
# store = HashStore.from_hash(hash)
|
55
|
-
#
|
56
|
-
# Returns instance of HashStore
|
57
|
-
# or nil if hash isn’t a Hash or doesn’t have the required fields.
|
58
|
-
def self.from_hash(hash)
|
59
|
-
begin
|
60
|
-
if hash.has_key?(:key) then key = hash[:key]
|
61
|
-
elsif hash.has_key? "key" then key = hash["key"]
|
62
|
-
else return nil end
|
63
|
-
|
64
|
-
if hash.has_key?(:items) then items = hash[:items]
|
65
|
-
elsif hash.has_key? "items" then items = hash["items"]
|
66
|
-
else return nil end
|
67
|
-
|
68
|
-
if items.is_a?(Hash) then self.new(key, items) else self.new(key) end
|
69
|
-
rescue
|
70
|
-
nil
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
private
|
75
|
-
|
76
|
-
# Internal: Obtains the key attribute of an item.
|
77
|
-
#
|
78
|
-
# item - Object that responds to the [] operator (e.g. Hash).
|
79
|
-
#
|
80
|
-
# Returns result of calling the [] operator with the key attribute on the item.
|
81
|
-
# Raises NoMethodError when item does’t respond to the key attribute method.
|
82
|
-
def key(item)
|
83
|
-
if @key.nil? then item.hash else item[@key] end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Internal: Obtains the specified attribute of an item.
|
87
|
-
#
|
88
|
-
# item - Object that responds to the [] operator (e.g. Hash).
|
89
|
-
# attribute - Object used as the attribute key in the item.
|
90
|
-
#
|
91
|
-
# Returns result of calling the [] operator with the attribute on the item.
|
92
|
-
# Raises NoMethodError when item does’t respond to the [] operator.
|
93
|
-
def attr(item, attribute)
|
94
|
-
item[attribute]
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
data/lib/memstore/json.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
|
5
|
-
module MemStore
|
6
|
-
|
7
|
-
class HashStore
|
8
|
-
|
9
|
-
def to_json
|
10
|
-
self.to_hash.to_json
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_json_file(file)
|
14
|
-
IO.write(file, self.to_json)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.from_json(json)
|
18
|
-
self.from_hash(JSON.parse(json)) rescue nil
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.from_json_file(file)
|
22
|
-
self.from_json(IO.read(file)) rescue nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.with_json_file(file, key=nil, items={}, &block)
|
26
|
-
self.execute_with_file(:from_json_file, :to_json_file, file, key, items, &block)
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
data/lib/memstore/msgpack.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "msgpack"
|
4
|
-
|
5
|
-
module MemStore
|
6
|
-
|
7
|
-
class HashStore
|
8
|
-
|
9
|
-
def to_msgpack
|
10
|
-
self.to_hash.to_msgpack
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_msgpack_file(file)
|
14
|
-
IO.write(file, self.to_msgpack)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.from_msgpack(msgpack)
|
18
|
-
self.from_hash(MessagePack.unpack(msgpack)) rescue nil
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.from_msgpack_file(file)
|
22
|
-
self.from_msgpack(IO.read(file)) rescue nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.with_msgpack_file(file, key=nil, items={}, &block)
|
26
|
-
self.execute_with_file(:from_msgpack_file, :to_msgpack_file, file, key, items, &block)
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
data/lib/memstore/objectstore.rb
DELETED
@@ -1,259 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module MemStore
|
4
|
-
|
5
|
-
# An ObjectStore accesses item attributes through item#attribute.
|
6
|
-
class ObjectStore
|
7
|
-
|
8
|
-
# Initializes an ObjectStore.
|
9
|
-
#
|
10
|
-
# key - Optional Symbol or String naming the method to obtain an item’s key attribute (default: nil).
|
11
|
-
# When no key is specified, Object#hash will be used for uniquely identifying items.
|
12
|
-
# items - Optional Hash of items to initialize the data store with (default: empty Hash).
|
13
|
-
#
|
14
|
-
# Examples
|
15
|
-
#
|
16
|
-
# store = ObjectStore.new
|
17
|
-
# store = ObjectStore.new(:id)
|
18
|
-
# store = ObjectStore.new(nil, { a.hash => a, b.hash => b, c.hash => c })
|
19
|
-
# store = ObjectStore.new(:id, { 1 => a, 2 => b, 3 => c })
|
20
|
-
#
|
21
|
-
# Returns initialized ObjectStore.
|
22
|
-
def initialize(key=nil, items={})
|
23
|
-
@key = key || :hash
|
24
|
-
@items = items
|
25
|
-
end
|
26
|
-
|
27
|
-
# Provides access to internal items collection (which is simply a Hash).
|
28
|
-
attr_accessor :items
|
29
|
-
|
30
|
-
# Inserts one or more items into the data store, can be chained.
|
31
|
-
# Also available as #<<, which only allows for one item at a time.
|
32
|
-
#
|
33
|
-
# items - One or more Objects that respond to the method specified as key attribute.
|
34
|
-
#
|
35
|
-
# Examples
|
36
|
-
#
|
37
|
-
# store.insert(a).insert(b).insert(c)
|
38
|
-
# store.insert(a, b, c)
|
39
|
-
# store << a << b << c
|
40
|
-
#
|
41
|
-
# Returns the data store itself to enable chaining.
|
42
|
-
# Raises NoMethodError when an item does’t respond to the key attribute method.
|
43
|
-
def insert(*items)
|
44
|
-
items.each { |item| @items[key(item)] = item }
|
45
|
-
self
|
46
|
-
end
|
47
|
-
alias_method :<<, :insert
|
48
|
-
|
49
|
-
# Returns total number of items in the data store. Also available as #size.
|
50
|
-
def length
|
51
|
-
@items.length
|
52
|
-
end
|
53
|
-
alias_method :size, :length
|
54
|
-
|
55
|
-
# Retrieves one or more items by key.
|
56
|
-
#
|
57
|
-
# keys - One or more Objects or Ranges that are keys of items.
|
58
|
-
# For a Range, all items with keys in that range are returned.
|
59
|
-
#
|
60
|
-
# Examples
|
61
|
-
#
|
62
|
-
# store[1]
|
63
|
-
# store[1, 2, 3]
|
64
|
-
# store[1..3]
|
65
|
-
# store[1, 3..5, 7]
|
66
|
-
#
|
67
|
-
# Returns an Object if a single key was given
|
68
|
-
# or nil if no item with that key exists
|
69
|
-
# or an Array of Objects when multiple keys were given
|
70
|
-
# in which nil is placed wherever there isn’t an item with that key.
|
71
|
-
def [](*keys)
|
72
|
-
return @items[keys.first] if keys.length == 1 && !keys.first.is_a?(Range)
|
73
|
-
keys.inject([]) do |items, key|
|
74
|
-
if key.is_a?(Range) then key.inject(items) { |i, k| i << @items[k] }
|
75
|
-
else items << @items[key] end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns all items as an Array.
|
80
|
-
def all
|
81
|
-
@items.values
|
82
|
-
end
|
83
|
-
|
84
|
-
# Deletes one or more items by reference. Also available as #delete_item and #delete.
|
85
|
-
#
|
86
|
-
# items - One or more Objects that respond to the method specified as key attribute.
|
87
|
-
#
|
88
|
-
# Examples
|
89
|
-
#
|
90
|
-
# store.delete_item(a)
|
91
|
-
# store.delete_items(a, b, c)
|
92
|
-
# store.delete(a)
|
93
|
-
# store.delete(a, b, c)
|
94
|
-
#
|
95
|
-
# Returns the Object that was removed if a single item was given
|
96
|
-
# or nil if the item isn’t in the data store
|
97
|
-
# or an Array of Objects that were removed when multiple items were given
|
98
|
-
# in which nil is placed wherever that item isn’t in the data store.
|
99
|
-
def delete_items(*items)
|
100
|
-
return @items.delete(key(items.first)) if items.length == 1
|
101
|
-
items.collect { |item| @items.delete(key(item)) }
|
102
|
-
end
|
103
|
-
alias_method :delete_item, :delete_items
|
104
|
-
alias_method :delete, :delete_items
|
105
|
-
|
106
|
-
# Deletes one or more items by key. Also available as #delete_key.
|
107
|
-
#
|
108
|
-
# keys - One or more Objects or Ranges that are keys of items.
|
109
|
-
# For a Range, all items with keys in that range are deleted.
|
110
|
-
#
|
111
|
-
# Examples
|
112
|
-
#
|
113
|
-
# store.delete_key(1)
|
114
|
-
# store.delete_keys(1, 2, 3)
|
115
|
-
# store.delete_keys(1..3)
|
116
|
-
# store.delete_keys(1, 3..5, 7)
|
117
|
-
#
|
118
|
-
# Returns the Object that was removed if a single key was given
|
119
|
-
# or nil if no item with that key exists
|
120
|
-
# or an Array of Objects that were removed when multiple keys were given
|
121
|
-
# in which nil is placed wherever there isn’t an item with that key.
|
122
|
-
def delete_keys(*keys)
|
123
|
-
return @items.delete(keys.first) if keys.length == 1 && !keys.first.is_a?(Range)
|
124
|
-
keys.inject([]) do |items, key|
|
125
|
-
if key.is_a?(Range) then key.inject(items) { |i, k| i << @items.delete(k) }
|
126
|
-
else items << @items.delete(key) end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
alias_method :delete_key, :delete_keys
|
130
|
-
|
131
|
-
# Returns data store in binary format.
|
132
|
-
# Raises whatever Marshal::dump raises.
|
133
|
-
def to_binary
|
134
|
-
Marshal.dump(self)
|
135
|
-
end
|
136
|
-
|
137
|
-
# Writes data store to a file in binary format.
|
138
|
-
#
|
139
|
-
# file - IO stream of file name as String.
|
140
|
-
#
|
141
|
-
# Returns number of bytes that were written to the file.
|
142
|
-
# Raises whatever IO::write raises.
|
143
|
-
def to_file(file)
|
144
|
-
IO.write(file, self.to_binary)
|
145
|
-
end
|
146
|
-
|
147
|
-
# Restores a data store from binary format.
|
148
|
-
#
|
149
|
-
# binary - Binary data containing a serialized instance of ObjectStore.
|
150
|
-
#
|
151
|
-
# Examples
|
152
|
-
#
|
153
|
-
# store = ObjectStore.from_binary(IO.read(file))
|
154
|
-
#
|
155
|
-
# Returns instance of ObjectStore
|
156
|
-
# or nil if marshalling failed or marshalled object isn’t an ObjectStore.
|
157
|
-
# Raises whatever Marshal::load raises.
|
158
|
-
def self.from_binary(binary)
|
159
|
-
restored = Marshal.load(binary) rescue nil
|
160
|
-
if restored.instance_of?(ObjectStore) then restored else nil end
|
161
|
-
end
|
162
|
-
|
163
|
-
# Restores a data store from a binary file.
|
164
|
-
#
|
165
|
-
# file - IO stream or file name as String.
|
166
|
-
#
|
167
|
-
# Examples
|
168
|
-
#
|
169
|
-
# store = ObjectStore.from_file(file)
|
170
|
-
#
|
171
|
-
# Returns instance of ObjectStore or nil (result of ::from_binary)
|
172
|
-
# or nil if file IO failed, e.g. because file doesn’t exist or isn’t readable.
|
173
|
-
# Raises whatever IO::read or Marshal::load raise.
|
174
|
-
def self.from_file(file)
|
175
|
-
self.from_binary(IO.read(file)) rescue nil
|
176
|
-
end
|
177
|
-
|
178
|
-
# Executes a given block while keeping an exclusive lock on a file.
|
179
|
-
# Allows to use the same file for persistence from multiple threads/processes.
|
180
|
-
# Tries to deserialize a data store from the file using ::from_file.
|
181
|
-
# If that fails, a new one will be created using the supplied key and items.
|
182
|
-
# Writes data store back to file using #to_file after block returns.
|
183
|
-
#
|
184
|
-
# file - IO stream or file name as String.
|
185
|
-
# key - Optional key attribute (Symbol or String) to use in ::new (default: nil).
|
186
|
-
# items - Optional items Hash to use in ::new (default: empty Hash).
|
187
|
-
# block - Block that will be called after a data store was restored or created.
|
188
|
-
#
|
189
|
-
# Yields the restored or newly created data store.
|
190
|
-
#
|
191
|
-
# Examples
|
192
|
-
#
|
193
|
-
# size_after_changes = ObjectStore.with_file(file, :id) do |store|
|
194
|
-
# store.delete(a, b, c, d, e)
|
195
|
-
# store.insert(f, g, h)
|
196
|
-
# store.size
|
197
|
-
# end
|
198
|
-
#
|
199
|
-
# Returns whatever the block returns.
|
200
|
-
# Raises whatever File::open, IO::read, Marshal::load, Marshal::dump or IO::write raise.
|
201
|
-
def self.with_file(file, key=nil, items={}, &block)
|
202
|
-
self.execute_with_file(:from_file, :to_file, file, key, items, &block)
|
203
|
-
end
|
204
|
-
|
205
|
-
private
|
206
|
-
|
207
|
-
# Internal: Obtains the key attribute of an item.
|
208
|
-
#
|
209
|
-
# item - Object that responds to the attribute.
|
210
|
-
#
|
211
|
-
# Returns result of calling the key attribute method on the item.
|
212
|
-
# Raises NoMethodError when item does’t respond to the key attribute method.
|
213
|
-
def key(item)
|
214
|
-
item.send(@key)
|
215
|
-
end
|
216
|
-
|
217
|
-
# Internal: Obtains the specified attribute of an item.
|
218
|
-
#
|
219
|
-
# item - Object that responds to the attribute.
|
220
|
-
# attribute - Symbol or String naming the attribute.
|
221
|
-
#
|
222
|
-
# Returns result of calling the attribute method on the item.
|
223
|
-
# Raises NoMethodError when item does’t respond to the attribute method.
|
224
|
-
def attr(item, attribute)
|
225
|
-
item.send(attribute)
|
226
|
-
end
|
227
|
-
|
228
|
-
# Internal: Used to implement with_file and variants for different formats/subclasses.
|
229
|
-
# Takes required method names, can therefore be used by any subclass.
|
230
|
-
# Executes a given block while keeping an exclusive lock on a file.
|
231
|
-
# Tries to deserialize a data store from the file using from_file_method.
|
232
|
-
# If that fails, a new one will be created using the supplied key and items.
|
233
|
-
# Writes data store back to file using to_file_method after block returns.
|
234
|
-
#
|
235
|
-
# from_file_method - Name of class method (Symbol or String) to deserialize data store from file.
|
236
|
-
# to_file_method - Name of instance method (Symbol or String) to serialize data store to file.
|
237
|
-
# file - IO stream or file name as String.
|
238
|
-
# key - Optional key attribute (Symbol or String) to use in ::new (default: nil).
|
239
|
-
# items - Optional items Hash to use in ::new (default: empty Hash).
|
240
|
-
# block - Block that will be called after a data store was restored or created.
|
241
|
-
#
|
242
|
-
# Yields the restored or newly created data store.
|
243
|
-
#
|
244
|
-
# Returns whatever the block returns.
|
245
|
-
# Raises whatever File::open, IO::read, Marshal::load, Marshal::dump or IO::write raise.
|
246
|
-
def self.execute_with_file(from_file_method, to_file_method, file, key=nil, items={}, &block)
|
247
|
-
File.open(file) do |file|
|
248
|
-
file.flock(File::LOCK_EX)
|
249
|
-
store = self.send(from_file_method, file) || self.new(key, items)
|
250
|
-
result = block.call(store)
|
251
|
-
store.send(to_file_method, file)
|
252
|
-
file.flock(File::LOCK_UN)
|
253
|
-
result
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
end
|
258
|
-
|
259
|
-
end
|
data/lib/memstore/yaml.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "yaml"
|
4
|
-
|
5
|
-
module MemStore
|
6
|
-
|
7
|
-
class HashStore
|
8
|
-
|
9
|
-
def to_yaml
|
10
|
-
self.to_hash.to_yaml
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_yaml_file(file)
|
14
|
-
IO.write(file, self.to_yaml)
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.from_yaml(yaml)
|
18
|
-
begin
|
19
|
-
self.from_hash(YAML.load(yaml))
|
20
|
-
rescue StandardError, Psych::SyntaxError
|
21
|
-
nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.from_yaml_file(file)
|
26
|
-
self.from_yaml(IO.read(file)) rescue nil
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.with_yaml_file(file, key=nil, items={}, &block)
|
30
|
-
self.execute_with_file(:from_yaml_file, :to_yaml_file, file, key, items, &block)
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|