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.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
|