memstore 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/memstore.rb +4 -0
- data/lib/memstore/hashstore.rb +64 -10
- data/lib/memstore/json.rb +1 -1
- data/lib/memstore/msgpack.rb +1 -1
- data/lib/memstore/objectstore.rb +175 -14
- data/lib/memstore/queries.rb +57 -7
- data/lib/memstore/version.rb +3 -1
- data/lib/memstore/yaml.rb +1 -1
- data/spec/hashstore_spec.rb +9 -0
- data/spec/objectstore_spec.rb +4 -0
- metadata +2 -2
data/lib/memstore.rb
CHANGED
@@ -7,18 +7,22 @@ require "memstore/queries"
|
|
7
7
|
|
8
8
|
module MemStore
|
9
9
|
|
10
|
+
# Shortcut to ObjectStore::new
|
10
11
|
def self.new(key=nil, items={})
|
11
12
|
ObjectStore.new(key, items)
|
12
13
|
end
|
13
14
|
|
15
|
+
# Shortcut to ObjectStore::from_binary
|
14
16
|
def self.from_binary(binary)
|
15
17
|
ObjectStore.from_binary(binary)
|
16
18
|
end
|
17
19
|
|
20
|
+
# Shortcut to ObjectStore::from_file
|
18
21
|
def self.from_file(file)
|
19
22
|
ObjectStore.from_file(file)
|
20
23
|
end
|
21
24
|
|
25
|
+
# Shortcut to ObjectStore::with_file
|
22
26
|
def self.with_file(file, key=nil, items={}, &block)
|
23
27
|
ObjectStore.with_file(file, key, items, &block)
|
24
28
|
end
|
data/lib/memstore/hashstore.rb
CHANGED
@@ -2,11 +2,61 @@
|
|
2
2
|
|
3
3
|
module MemStore
|
4
4
|
|
5
|
+
# A HashStore accesses item attributes through item[attribute].
|
5
6
|
class HashStore < ObjectStore
|
6
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.
|
7
58
|
def self.from_hash(hash)
|
8
59
|
begin
|
9
|
-
|
10
60
|
if hash.has_key?(:key) then key = hash[:key]
|
11
61
|
elsif hash.has_key? "key" then key = hash["key"]
|
12
62
|
else return nil end
|
@@ -16,26 +66,30 @@ module MemStore
|
|
16
66
|
else return nil end
|
17
67
|
|
18
68
|
if items.is_a?(Hash) then self.new(key, items) else self.new(key) end
|
19
|
-
|
20
69
|
rescue
|
21
70
|
nil
|
22
71
|
end
|
23
72
|
end
|
24
73
|
|
25
|
-
def initialize(key=nil, items={})
|
26
|
-
@key, @items = key, items
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_hash
|
30
|
-
{ key: @key, items: @items }
|
31
|
-
end
|
32
|
-
|
33
74
|
private
|
34
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.
|
35
82
|
def key(item)
|
36
83
|
if @key.nil? then item.hash else item[@key] end
|
37
84
|
end
|
38
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.
|
39
93
|
def attr(item, attribute)
|
40
94
|
item[attribute]
|
41
95
|
end
|
data/lib/memstore/json.rb
CHANGED
@@ -23,7 +23,7 @@ module MemStore
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.with_json_file(file, key=nil, items={}, &block)
|
26
|
-
self.
|
26
|
+
self.execute_with_file(:from_json_file, :to_json_file, file, key, items, &block)
|
27
27
|
end
|
28
28
|
|
29
29
|
end
|
data/lib/memstore/msgpack.rb
CHANGED
@@ -23,7 +23,7 @@ module MemStore
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.with_msgpack_file(file, key=nil, items={}, &block)
|
26
|
-
self.
|
26
|
+
self.execute_with_file(:from_msgpack_file, :to_msgpack_file, file, key, items, &block)
|
27
27
|
end
|
28
28
|
|
29
29
|
end
|
data/lib/memstore/objectstore.rb
CHANGED
@@ -2,39 +2,100 @@
|
|
2
2
|
|
3
3
|
module MemStore
|
4
4
|
|
5
|
+
# An ObjectStore accesses item attributes through item#attribute.
|
5
6
|
class ObjectStore
|
6
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.
|
7
22
|
def initialize(key=nil, items={})
|
8
23
|
@key = key || :hash
|
9
24
|
@items = items
|
10
25
|
end
|
11
26
|
|
27
|
+
# Provides access to internal items collection (which is simply a Hash).
|
12
28
|
attr_accessor :items
|
13
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.
|
14
43
|
def insert(*items)
|
15
44
|
items.each { |item| @items[key(item)] = item }
|
16
45
|
self
|
17
46
|
end
|
18
47
|
alias_method :<<, :insert
|
19
48
|
|
49
|
+
# Returns total number of items in the data store. Also available as #size.
|
20
50
|
def length
|
21
51
|
@items.length
|
22
52
|
end
|
23
53
|
alias_method :size, :length
|
24
|
-
alias_method :count, :length
|
25
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.
|
26
71
|
def [](*keys)
|
27
72
|
return @items[keys.first] if keys.length == 1 && !keys.first.is_a?(Range)
|
28
|
-
keys.inject
|
73
|
+
keys.inject([]) do |items, key|
|
29
74
|
if key.is_a?(Range) then key.inject(items) { |i, k| i << @items[k] }
|
30
75
|
else items << @items[key] end
|
31
76
|
end
|
32
77
|
end
|
33
78
|
|
79
|
+
# Returns all items as an Array.
|
34
80
|
def all
|
35
81
|
@items.values
|
36
82
|
end
|
37
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.
|
38
99
|
def delete_items(*items)
|
39
100
|
return @items.delete(key(items.first)) if items.length == 1
|
40
101
|
items.collect { |item| @items.delete(key(item)) }
|
@@ -42,47 +103,147 @@ module MemStore
|
|
42
103
|
alias_method :delete_item, :delete_items
|
43
104
|
alias_method :delete, :delete_items
|
44
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.
|
45
122
|
def delete_keys(*keys)
|
46
123
|
return @items.delete(keys.first) if keys.length == 1 && !keys.first.is_a?(Range)
|
47
|
-
keys.inject
|
124
|
+
keys.inject([]) do |items, key|
|
48
125
|
if key.is_a?(Range) then key.inject(items) { |i, k| i << @items.delete(k) }
|
49
126
|
else items << @items.delete(key) end
|
50
127
|
end
|
51
128
|
end
|
52
129
|
alias_method :delete_key, :delete_keys
|
53
130
|
|
54
|
-
|
55
|
-
|
56
|
-
if restored.kind_of?(ObjectStore) || restored.kind_of?(HashStore) then restored else nil end
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.from_file(file)
|
60
|
-
self.from_binary(IO.read(file)) rescue nil
|
61
|
-
end
|
62
|
-
|
131
|
+
# Returns data store in binary format.
|
132
|
+
# Raises whatever Marshal::dump raises.
|
63
133
|
def to_binary
|
64
134
|
Marshal.dump(self)
|
65
135
|
end
|
66
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.
|
67
143
|
def to_file(file)
|
68
144
|
IO.write(file, self.to_binary)
|
69
145
|
end
|
70
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.
|
71
201
|
def self.with_file(file, key=nil, items={}, &block)
|
72
|
-
self.
|
202
|
+
self.execute_with_file(:from_file, :to_file, file, key, items, &block)
|
73
203
|
end
|
74
204
|
|
75
205
|
private
|
76
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.
|
77
213
|
def key(item)
|
78
214
|
item.send(@key)
|
79
215
|
end
|
80
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.
|
81
224
|
def attr(item, attribute)
|
82
225
|
item.send(attribute)
|
83
226
|
end
|
84
227
|
|
85
|
-
|
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)
|
86
247
|
File.open(file) do |file|
|
87
248
|
file.flock(File::LOCK_EX)
|
88
249
|
store = self.send(from_file_method, file) || self.new(key, items)
|
data/lib/memstore/queries.rb
CHANGED
@@ -4,91 +4,141 @@ module MemStore
|
|
4
4
|
|
5
5
|
class ObjectStore
|
6
6
|
|
7
|
+
# All methods have the following signature:
|
8
|
+
#
|
9
|
+
# conditions - Hash mapping attributes to conditions.
|
10
|
+
# Attributes can be Symbols or String for ObjectStore
|
11
|
+
# and any kind of Object for HashStore.
|
12
|
+
# Conditions can be any kind of Object that responds to #===.
|
13
|
+
# block - Optional block taking an item and returning a bool indicating
|
14
|
+
# whether the item passed the tests within the block.
|
15
|
+
#
|
16
|
+
# Yields every item in the data store after the conditions were evaluated for it.
|
17
|
+
|
18
|
+
### find ###
|
19
|
+
|
20
|
+
# Also available as #find.
|
21
|
+
# Returns an Array of items that fulfill all conditions.
|
7
22
|
def find_all(conditions={}, &block)
|
8
23
|
all.select { |item| instance_exec(item, conditions, block, &FIND_ALL) }
|
9
24
|
end
|
10
25
|
alias_method :find, :find_all
|
11
26
|
|
27
|
+
# Returns an Array of items that fulfill at least one condition.
|
12
28
|
def find_any(conditions={}, &block)
|
13
29
|
all.select { |item| instance_exec(item, conditions, block, &FIND_ANY) }
|
14
30
|
end
|
15
31
|
|
32
|
+
# Returns an Array of items that fulfill exactly one condition.
|
16
33
|
def find_one(conditions={}, &block)
|
17
34
|
all.select { |item| instance_exec(item, conditions, block, &FIND_ONE) }
|
18
35
|
end
|
19
36
|
|
37
|
+
# Returns an Array of items that violate at least one condition.
|
20
38
|
def find_not_all(conditions={}, &block)
|
21
39
|
all.reject { |item| instance_exec(item, conditions, block, &FIND_ALL) }
|
22
40
|
end
|
23
41
|
|
42
|
+
# Returns an Array of items that violate all conditions.
|
24
43
|
def find_none(conditions={}, &block)
|
25
44
|
all.select { |item| instance_exec(item, conditions, block, &FIND_NONE) }
|
26
45
|
end
|
27
46
|
|
47
|
+
### first ###
|
48
|
+
|
49
|
+
# Also available as #first.
|
50
|
+
# Returns the first item that fulfills all conditions.
|
28
51
|
def first_all(conditions={}, &block)
|
29
52
|
all.detect { |item| instance_exec(item, conditions, block, &FIND_ALL) }
|
30
53
|
end
|
31
54
|
alias_method :first, :first_all
|
32
55
|
|
56
|
+
# Returns the first item that fulfills at least one condition.
|
33
57
|
def first_any(conditions={}, &block)
|
34
58
|
all.detect { |item| instance_exec(item, conditions, block, &FIND_ANY) }
|
35
59
|
end
|
36
60
|
|
61
|
+
# Returns the first item that fulfills exactly one condition.
|
37
62
|
def first_one(conditions={}, &block)
|
38
63
|
all.detect { |item| instance_exec(item, conditions, block, &FIND_ONE) }
|
39
64
|
end
|
40
65
|
|
66
|
+
# which is equivalent to: !condition || !condition || ... [|| !block]
|
67
|
+
# Returns the first item that violates at least one condition.
|
41
68
|
def first_not_all(conditions={}, &block)
|
42
69
|
all.detect { |item| !instance_exec(item, conditions, block, &FIND_ALL) }
|
43
70
|
end
|
44
71
|
|
72
|
+
# Returns the first item that violates all conditions.
|
45
73
|
def first_none(conditions={}, &block)
|
46
74
|
all.detect { |item| instance_exec(item, conditions, block, &FIND_NONE) }
|
47
75
|
end
|
48
76
|
|
77
|
+
### count ###
|
78
|
+
|
79
|
+
# Also available as #count.
|
80
|
+
# Returns the number of items that fulfill all conditions.
|
49
81
|
def count_all(conditions={}, &block)
|
50
82
|
all.count { |item| instance_exec(item, conditions, block, &FIND_ALL) }
|
51
83
|
end
|
52
84
|
alias_method :count, :count_all
|
53
85
|
|
86
|
+
# Returns the number of items that fulfill at least one condition.
|
54
87
|
def count_any(conditions={}, &block)
|
55
88
|
all.count { |item| instance_exec(item, conditions, block, &FIND_ANY) }
|
56
89
|
end
|
57
90
|
|
91
|
+
# Returns the number of items that fulfill exactly one condition.
|
58
92
|
def count_one(conditions={}, &block)
|
59
93
|
all.count { |item| instance_exec(item, conditions, block, &FIND_ONE) }
|
60
94
|
end
|
61
95
|
|
96
|
+
# which is equivalent to: !condition || !condition || ... [|| !block]
|
97
|
+
# Returns the number of items that violate at least one condition.
|
62
98
|
def count_not_all(conditions={}, &block)
|
63
99
|
all.count { |item| !instance_exec(item, conditions, block, &FIND_ALL) }
|
64
100
|
end
|
65
101
|
|
102
|
+
# Returns the number of items that violate all conditions.
|
66
103
|
def count_none(conditions={}, &block)
|
67
104
|
all.count { |item| instance_exec(item, conditions, block, &FIND_NONE) }
|
68
105
|
end
|
69
106
|
|
70
107
|
private
|
71
|
-
|
108
|
+
|
109
|
+
# All blocks have the following signature:
|
110
|
+
#
|
111
|
+
# item - The item (Object for ObjectStore, Hash for HashStore) to be tested.
|
112
|
+
# conditions - Hash of conditions to be evaluated.
|
113
|
+
# block - Optional block that can test the item after the conditions are evaluated.
|
114
|
+
#
|
115
|
+
# Returns a bool indicating whether the item was a match
|
116
|
+
# for the given conditions and matching logic.
|
117
|
+
|
118
|
+
# Internal: Evaluates conditions using AND, i.e. condition && condition && ... [&& block]
|
72
119
|
FIND_ALL = Proc.new do |item, conditions, block|
|
73
120
|
conditions.all? { |attribute, condition| condition === attr(item, attribute) } &&
|
74
121
|
if block then !!block.call(item) else true end
|
75
122
|
end
|
76
123
|
|
124
|
+
# Internal: Evaluates conditions using OR, i.e. condition || condition || ... [|| block]
|
77
125
|
FIND_ANY = Proc.new do |item, conditions, block|
|
78
126
|
conditions.any? { |attribute, condition| condition === attr(item, attribute) } ||
|
79
127
|
if block then !!block.call(item) else false end
|
80
128
|
end
|
81
129
|
|
82
|
-
|
83
|
-
conditions.none? { |attribute, condition| condition === attr(item, attribute) } &&
|
84
|
-
if block then !!block.call(item) else true end
|
85
|
-
end
|
86
|
-
|
130
|
+
# Internal: Evaluates conditions using XOR, i.e. condition ^ condition ^ condition ... [^ block]
|
87
131
|
FIND_ONE = Proc.new do |item, conditions, block|
|
88
|
-
conditions.one? { |attribute, condition| condition === attr(item, attribute) }
|
132
|
+
conditions.one? { |attribute, condition| condition === attr(item, attribute) } ^
|
89
133
|
if block then !!block.call(item) else false end
|
90
134
|
end
|
91
135
|
|
136
|
+
# Internal: Evaluates condition using AND NOT, i.e. !condition && !condition && ... [&& !block]
|
137
|
+
FIND_NONE = Proc.new do |item, conditions, block|
|
138
|
+
conditions.none? { |attribute, condition| condition === attr(item, attribute) } &&
|
139
|
+
if block then !!!block.call(item) else true end
|
140
|
+
end
|
141
|
+
|
92
142
|
end
|
93
143
|
|
94
144
|
end
|
data/lib/memstore/version.rb
CHANGED
data/lib/memstore/yaml.rb
CHANGED
@@ -27,7 +27,7 @@ module MemStore
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.with_yaml_file(file, key=nil, items={}, &block)
|
30
|
-
self.
|
30
|
+
self.execute_with_file(:from_yaml_file, :to_yaml_file, file, key, items, &block)
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
data/spec/hashstore_spec.rb
CHANGED
@@ -34,6 +34,15 @@ describe MemStore::HashStore do
|
|
34
34
|
restored.instance_variable_get(:@key).must_equal @store.instance_variable_get(:@key)
|
35
35
|
end
|
36
36
|
|
37
|
+
it "returns nil when conversion from binary fails" do
|
38
|
+
MemStore::HashStore.from_binary(nil).must_equal nil
|
39
|
+
MemStore::HashStore.from_binary(Marshal.dump(Object.new)).must_equal nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns nil when marshalled object isn’t instance of HashStore" do
|
43
|
+
MemStore::HashStore.from_binary(MemStore::ObjectStore.new.to_binary).must_equal nil
|
44
|
+
end
|
45
|
+
|
37
46
|
it "can be serialized to and deserialized from a binary file" do
|
38
47
|
tmp = Tempfile.new("memstore")
|
39
48
|
@store.to_file(tmp)
|
data/spec/objectstore_spec.rb
CHANGED
@@ -82,6 +82,10 @@ describe MemStore::ObjectStore do
|
|
82
82
|
MemStore::ObjectStore.from_binary(Marshal.dump(Object.new)).must_equal nil
|
83
83
|
end
|
84
84
|
|
85
|
+
it "returns nil when marshalled object isn’t instance of ObjectStore" do
|
86
|
+
MemStore::ObjectStore.from_binary(MemStore::HashStore.new.to_binary).must_equal nil
|
87
|
+
end
|
88
|
+
|
85
89
|
it "can be serialized to and deserialized from a binary file" do
|
86
90
|
tmp = Tempfile.new("memstore")
|
87
91
|
@store.to_file(tmp)
|
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.2.
|
4
|
+
version: 1.2.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-21 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.
|