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
data/lib/memstore.rb
CHANGED
@@ -1,30 +1,4 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require "memstore/version"
|
4
|
-
require "memstore/
|
5
|
-
require "memstore/
|
2
|
+
require "memstore/refinements"
|
3
|
+
require "memstore/core"
|
6
4
|
require "memstore/queries"
|
7
|
-
|
8
|
-
module MemStore
|
9
|
-
|
10
|
-
# Shortcut to ObjectStore::new
|
11
|
-
def self.new(key=nil, items={})
|
12
|
-
ObjectStore.new(key, items)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Shortcut to ObjectStore::from_binary
|
16
|
-
def self.from_binary(binary)
|
17
|
-
ObjectStore.from_binary(binary)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Shortcut to ObjectStore::from_file
|
21
|
-
def self.from_file(file)
|
22
|
-
ObjectStore.from_file(file)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Shortcut to ObjectStore::with_file
|
26
|
-
def self.with_file(file, key=nil, items={}, &block)
|
27
|
-
ObjectStore.with_file(file, key, items, &block)
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
class MemStore
|
2
|
+
|
3
|
+
# Initializes an MemStore.
|
4
|
+
#
|
5
|
+
# key - optional String/Symbol to be sent or Proc/lambda/method to be called
|
6
|
+
# access - optional String/Symbol to be sent or Proc/lambda/method to be called
|
7
|
+
# items - optional Array of items to be added
|
8
|
+
#
|
9
|
+
# Returns initialized MemStore.
|
10
|
+
def initialize(key: nil, access: nil, items: nil)
|
11
|
+
|
12
|
+
@items = {}
|
13
|
+
|
14
|
+
define_singleton_method :access_key,
|
15
|
+
if key.nil?
|
16
|
+
-> item { item.hash }
|
17
|
+
elsif key.respond_to?(:call)
|
18
|
+
-> item { key.call(item) }
|
19
|
+
else
|
20
|
+
-> item { access_attribute(item, key) }
|
21
|
+
end
|
22
|
+
|
23
|
+
define_singleton_method :access_attribute,
|
24
|
+
if access.nil?
|
25
|
+
-> item, attribute { item.send(attribute) }
|
26
|
+
elsif [Symbol, String].include?(access.class)
|
27
|
+
-> item, attribute { item.send(access, attribute) }
|
28
|
+
elsif access.respond_to?(:call)
|
29
|
+
-> item, attribute { access.call(item, attribute) }
|
30
|
+
else
|
31
|
+
raise "No usable access method."
|
32
|
+
end
|
33
|
+
|
34
|
+
add(*items) unless items.nil?
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
# Provides access to internal items collection (which is simply a Hash).
|
39
|
+
attr_accessor :items
|
40
|
+
|
41
|
+
# Returns all items as an Array.
|
42
|
+
def all
|
43
|
+
@items.values
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns total number of items in the data store.
|
47
|
+
def size
|
48
|
+
@items.length
|
49
|
+
end
|
50
|
+
|
51
|
+
# Adds one item to the data store.
|
52
|
+
#
|
53
|
+
# item - item to add
|
54
|
+
#
|
55
|
+
# Returns the data store itself.
|
56
|
+
#
|
57
|
+
# Raises NoMethodError when an item does’t respond to the key attribute method.
|
58
|
+
def <<(item)
|
59
|
+
@items[access_key(item)] = item
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Adds one or more items to the data store.
|
64
|
+
#
|
65
|
+
# items - items to be added
|
66
|
+
#
|
67
|
+
# Returns the data store itself.
|
68
|
+
#
|
69
|
+
# Raises NoMethodError when an item does’t respond to the key attribute method.
|
70
|
+
def add(*items)
|
71
|
+
items.each { |item| self << item }
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# Retrieves one item by key.
|
76
|
+
#
|
77
|
+
# key - key of item to be retrieved
|
78
|
+
#
|
79
|
+
# Returns item if it exists, otherwise nil.
|
80
|
+
def [](key)
|
81
|
+
@items[key]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Retrieves one or more items by key.
|
85
|
+
#
|
86
|
+
# keys - keys of items to be retrieved
|
87
|
+
#
|
88
|
+
# Returns an array of items with nil where no item with that key exists.
|
89
|
+
def get(*keys)
|
90
|
+
keys.collect { |key| @items[key] }
|
91
|
+
end
|
92
|
+
|
93
|
+
# Deletes one item by reference.
|
94
|
+
#
|
95
|
+
# item - item to be deleted
|
96
|
+
#
|
97
|
+
# Returns the item that was deleted or nil if it doesn’t exist.
|
98
|
+
def delete_item(item)
|
99
|
+
@items.delete(access_key(item))
|
100
|
+
end
|
101
|
+
|
102
|
+
# Deletes one or more items by reference.
|
103
|
+
#
|
104
|
+
# items - items to be deleted
|
105
|
+
#
|
106
|
+
# Returns an array of items that were deleted with nil where an item doesn’t exist.
|
107
|
+
def delete_items(*items)
|
108
|
+
items.collect { |item| @items.delete(access_key(item)) }
|
109
|
+
end
|
110
|
+
|
111
|
+
# Deletes one item by key.
|
112
|
+
#
|
113
|
+
# key - key of item to be deleted
|
114
|
+
#
|
115
|
+
# Returns the item that was deleted or nil if it doesn’t exist.
|
116
|
+
def delete_key(key)
|
117
|
+
@items.delete(key)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Deletes one or more items by key.
|
121
|
+
#
|
122
|
+
# keys - keys of items to be deleted
|
123
|
+
#
|
124
|
+
# Returns an array of items that were deleted with nil where no item with that key exists.
|
125
|
+
def delete_keys(*keys)
|
126
|
+
keys.collect { |key| @items.delete(key) }
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
data/lib/memstore/queries.rb
CHANGED
@@ -1,144 +1,184 @@
|
|
1
|
-
|
1
|
+
class MemStore
|
2
2
|
|
3
|
-
|
3
|
+
using Refinements
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
5
|
+
### find ###
|
6
|
+
|
7
|
+
# Finds items that fulfill all provided conditions.
|
8
|
+
#
|
9
|
+
# conditions - Hash mapping attributes to conditions (anything that responds to #===).
|
10
|
+
# block - Optional block taking an item and returning a bool. Evaluated after conditions.
|
11
|
+
#
|
12
|
+
# Returns an Array of items that fulfill all conditions.
|
13
|
+
def find_all(conditions={}, &block)
|
14
|
+
all.select { |item| match_all(item, conditions, &block) }
|
15
|
+
end
|
16
|
+
alias_method :find, :find_all
|
17
|
+
|
18
|
+
# Finds items that fulfill at least one of the provided conditions.
|
19
|
+
def find_any(conditions={}, &block)
|
20
|
+
all.select { |item| match_any(item, conditions, &block) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finds items that fulfill exactly one of the provided conditions.
|
24
|
+
def find_one(conditions={}, &block)
|
25
|
+
all.select { |item| match_one(item, conditions, &block) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Finds items that violate at least one of the provided conditions.
|
29
|
+
def find_not_all(conditions={}, &block)
|
30
|
+
all.reject { |item| match_all(item, conditions, &block) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Finds items that violate all provided conditions.
|
34
|
+
def find_none(conditions={}, &block)
|
35
|
+
all.select { |item| match_none(item, conditions, &block) }
|
36
|
+
end
|
37
|
+
|
38
|
+
### first ###
|
39
|
+
|
40
|
+
# Finds the first item that fulfills all provided conditions.
|
41
|
+
#
|
42
|
+
# conditions - Hash mapping attributes to conditions (anything that responds to #===).
|
43
|
+
# block - Optional block taking an item and returning a bool. Evaluated after conditions.
|
44
|
+
#
|
45
|
+
# Returns the first item that fulfills all conditions.
|
46
|
+
def first_all(conditions={}, &block)
|
47
|
+
all.detect { |item| match_all(item, conditions, &block) }
|
48
|
+
end
|
49
|
+
alias_method :first, :first_all
|
50
|
+
|
51
|
+
# Finds the first item that fulfills at least one of the provided conditions.
|
52
|
+
def first_any(conditions={}, &block)
|
53
|
+
all.detect { |item| match_any(item, conditions, &block) }
|
54
|
+
end
|
36
55
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
56
|
+
# Finds the first item that fulfills exactly one of the provided conditions.
|
57
|
+
def first_one(conditions={}, &block)
|
58
|
+
all.detect { |item| match_one(item, conditions, &block) }
|
59
|
+
end
|
41
60
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
61
|
+
# Finds the first item that violates at least one of the provided conditions.
|
62
|
+
def first_not_all(conditions={}, &block)
|
63
|
+
all.detect { |item| !match_all(item, conditions, &block) }
|
64
|
+
end
|
46
65
|
|
47
|
-
|
66
|
+
# Finds the first item that violates all provided conditions.
|
67
|
+
def first_none(conditions={}, &block)
|
68
|
+
all.detect { |item| match_none(item, conditions, &block) }
|
69
|
+
end
|
48
70
|
|
49
|
-
|
50
|
-
# Returns the first item that fulfills all conditions.
|
51
|
-
def first_all(conditions={}, &block)
|
52
|
-
all.detect { |item| instance_exec(item, conditions, block, &FIND_ALL) }
|
53
|
-
end
|
54
|
-
alias_method :first, :first_all
|
71
|
+
### count ###
|
55
72
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
73
|
+
# Counts the number of items that fulfill all provided conditions.
|
74
|
+
#
|
75
|
+
# conditions - Hash mapping attributes to conditions (anything that responds to #===).
|
76
|
+
# block - Optional block taking an item and returning a bool. Evaluated after conditions.
|
77
|
+
#
|
78
|
+
# Returns the number of items that fulfill all conditions.
|
79
|
+
def count_all(conditions={}, &block)
|
80
|
+
all.count { |item| match_all(item, conditions, &block) }
|
81
|
+
end
|
82
|
+
alias_method :count, :count_all
|
60
83
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
84
|
+
# Counts the number of items that fulfill at least one of the provided conditions.
|
85
|
+
def count_any(conditions={}, &block)
|
86
|
+
all.count { |item| match_any(item, conditions, &block) }
|
87
|
+
end
|
65
88
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
89
|
+
# Counts the number of items that fulfill exactly one of the provided conditions.
|
90
|
+
def count_one(conditions={}, &block)
|
91
|
+
all.count { |item| match_one(item, conditions, &block) }
|
92
|
+
end
|
71
93
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
94
|
+
# Counts the number of items that violate at least one of the provided conditions.
|
95
|
+
def count_not_all(conditions={}, &block)
|
96
|
+
all.count { |item| !match_all(item, conditions, &block) }
|
97
|
+
end
|
76
98
|
|
77
|
-
|
99
|
+
# Counts the number of items that violate all provided conditions.
|
100
|
+
def count_none(conditions={}, &block)
|
101
|
+
all.count { |item| match_none(item, conditions, &block) }
|
102
|
+
end
|
78
103
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
104
|
+
### delete ###
|
105
|
+
|
106
|
+
# Deletes items that fulfill all provided conditions.
|
107
|
+
#
|
108
|
+
# conditions - Hash mapping attributes to conditions (anything that responds to #===).
|
109
|
+
# block - Optional block taking an item and returning a bool. Evaluated after conditions.
|
110
|
+
#
|
111
|
+
# Returns an Array of items that fulfill all conditions and were deleted.
|
112
|
+
def delete_all(conditions={}, &block)
|
113
|
+
@items.inject([]) do |items, (key, item)|
|
114
|
+
items << @items.delete(key) if match_all(item, conditions, &block)
|
115
|
+
items
|
83
116
|
end
|
84
|
-
|
117
|
+
end
|
118
|
+
alias_method :delete, :delete_all
|
85
119
|
|
86
|
-
|
87
|
-
|
88
|
-
|
120
|
+
# Deletes items that fulfill at least one of the provided conditions.
|
121
|
+
def delete_any(conditions={}, &block)
|
122
|
+
@items.inject([]) do |items, (key, item)|
|
123
|
+
items << @items.delete(key) if match_any(item, conditions, &block)
|
124
|
+
items
|
89
125
|
end
|
126
|
+
end
|
90
127
|
|
91
|
-
|
92
|
-
|
93
|
-
|
128
|
+
# Deletes items that fulfill exactly one of the provided conditions.
|
129
|
+
def delete_one(conditions={}, &block)
|
130
|
+
@items.inject([]) do |items, (key, item)|
|
131
|
+
items << @items.delete(key) if match_one(item, conditions, &block)
|
132
|
+
items
|
94
133
|
end
|
134
|
+
end
|
95
135
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
136
|
+
# Deletes items that violate at least one of the provided conditions.
|
137
|
+
def delete_not_all(conditions={}, &block)
|
138
|
+
@items.inject([]) do |items, (key, item)|
|
139
|
+
items << @items.delete(key) if !match_all(item, conditions, &block)
|
140
|
+
items
|
100
141
|
end
|
142
|
+
end
|
101
143
|
|
102
|
-
|
103
|
-
|
104
|
-
|
144
|
+
# Deletes items that violate all provided conditions.
|
145
|
+
def delete_none(conditions={}, &block)
|
146
|
+
@items.inject([]) do |items, (key, item)|
|
147
|
+
items << @items.delete(key) if match_none(item, conditions, &block)
|
148
|
+
items
|
105
149
|
end
|
150
|
+
end
|
106
151
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
conditions.all? { |attribute, condition| condition === attr(item, attribute) } &&
|
121
|
-
if block then !!block.call(item) else true end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Internal: Evaluates conditions using OR, i.e. condition || condition || ... [|| block]
|
125
|
-
FIND_ANY = Proc.new do |item, conditions, block|
|
126
|
-
conditions.any? { |attribute, condition| condition === attr(item, attribute) } ||
|
127
|
-
if block then !!block.call(item) else false end
|
128
|
-
end
|
152
|
+
private
|
153
|
+
|
154
|
+
# Evaluates conditions using AND, i.e. condition && condition && ... [&& block]
|
155
|
+
#
|
156
|
+
# item - The item to be tested.
|
157
|
+
# conditions - Hash of conditions to be evaluated.
|
158
|
+
# block - Optional block that can test the item after the conditions are evaluated.
|
159
|
+
#
|
160
|
+
# Returns a bool indicating whether the item passed the conditions and/or block.
|
161
|
+
def match_all(item, conditions={}, &block)
|
162
|
+
conditions.all? { |attribute, condition| condition === access_attribute(item, attribute) } &&
|
163
|
+
if block_given? then yield(item) else true end
|
164
|
+
end
|
129
165
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
166
|
+
# Evaluates conditions using OR, i.e. condition || condition || ... [|| block]
|
167
|
+
def match_any(item, conditions={}, &block)
|
168
|
+
conditions.any? { |attribute, condition| condition === access_attribute(item, attribute) } ||
|
169
|
+
if block_given? then yield(item) else false end
|
170
|
+
end
|
135
171
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
172
|
+
# Evaluates conditions using XOR, i.e. condition ^ condition ^ condition ... [^ block]
|
173
|
+
def match_one(item, conditions={}, &block)
|
174
|
+
conditions.one? { |attribute, condition| condition === access_attribute(item, attribute) } ^
|
175
|
+
if block_given? then yield(item) else false end
|
176
|
+
end
|
141
177
|
|
178
|
+
# Evaluates condition using AND NOT, i.e. !condition && !condition && ... [&& !block]
|
179
|
+
def match_none(item, conditions={}, &block)
|
180
|
+
conditions.none? { |attribute, condition| condition === access_attribute(item, attribute) } &&
|
181
|
+
if block_given? then !yield(item) else true end
|
142
182
|
end
|
143
183
|
|
144
|
-
end
|
184
|
+
end
|