memstore 1.2.0 → 1.2.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/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.
|