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.
data/lib/memstore.rb CHANGED
@@ -1,30 +1,4 @@
1
- # encoding: utf-8
2
-
3
1
  require "memstore/version"
4
- require "memstore/objectstore"
5
- require "memstore/hashstore"
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
@@ -1,144 +1,184 @@
1
- # encoding: utf-8
1
+ class MemStore
2
2
 
3
- module MemStore
3
+ using Refinements
4
4
 
5
- class ObjectStore
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.
22
- def find_all(conditions={}, &block)
23
- all.select { |item| instance_exec(item, conditions, block, &FIND_ALL) }
24
- end
25
- alias_method :find, :find_all
26
-
27
- # Returns an Array of items that fulfill at least one condition.
28
- def find_any(conditions={}, &block)
29
- all.select { |item| instance_exec(item, conditions, block, &FIND_ANY) }
30
- end
31
-
32
- # Returns an Array of items that fulfill exactly one condition.
33
- def find_one(conditions={}, &block)
34
- all.select { |item| instance_exec(item, conditions, block, &FIND_ONE) }
35
- end
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
- # Returns an Array of items that violate at least one condition.
38
- def find_not_all(conditions={}, &block)
39
- all.reject { |item| instance_exec(item, conditions, block, &FIND_ALL) }
40
- end
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
- # Returns an Array of items that violate all conditions.
43
- def find_none(conditions={}, &block)
44
- all.select { |item| instance_exec(item, conditions, block, &FIND_NONE) }
45
- end
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
- ### first ###
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
- # Also available as #first.
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
- # Returns the first item that fulfills at least one condition.
57
- def first_any(conditions={}, &block)
58
- all.detect { |item| instance_exec(item, conditions, block, &FIND_ANY) }
59
- end
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
- # Returns the first item that fulfills exactly one condition.
62
- def first_one(conditions={}, &block)
63
- all.detect { |item| instance_exec(item, conditions, block, &FIND_ONE) }
64
- end
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
- # which is equivalent to: !condition || !condition || ... [|| !block]
67
- # Returns the first item that violates at least one condition.
68
- def first_not_all(conditions={}, &block)
69
- all.detect { |item| !instance_exec(item, conditions, block, &FIND_ALL) }
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
- # Returns the first item that violates all conditions.
73
- def first_none(conditions={}, &block)
74
- all.detect { |item| instance_exec(item, conditions, block, &FIND_NONE) }
75
- end
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
- ### count ###
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
- # Also available as #count.
80
- # Returns the number of items that fulfill all conditions.
81
- def count_all(conditions={}, &block)
82
- all.count { |item| instance_exec(item, conditions, block, &FIND_ALL) }
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
- alias_method :count, :count_all
117
+ end
118
+ alias_method :delete, :delete_all
85
119
 
86
- # Returns the number of items that fulfill at least one condition.
87
- def count_any(conditions={}, &block)
88
- all.count { |item| instance_exec(item, conditions, block, &FIND_ANY) }
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
- # Returns the number of items that fulfill exactly one condition.
92
- def count_one(conditions={}, &block)
93
- all.count { |item| instance_exec(item, conditions, block, &FIND_ONE) }
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
- # which is equivalent to: !condition || !condition || ... [|| !block]
97
- # Returns the number of items that violate at least one condition.
98
- def count_not_all(conditions={}, &block)
99
- all.count { |item| !instance_exec(item, conditions, block, &FIND_ALL) }
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
- # Returns the number of items that violate all conditions.
103
- def count_none(conditions={}, &block)
104
- all.count { |item| instance_exec(item, conditions, block, &FIND_NONE) }
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
- private
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]
119
- FIND_ALL = Proc.new do |item, conditions, block|
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
- # Internal: Evaluates conditions using XOR, i.e. condition ^ condition ^ condition ... [^ block]
131
- FIND_ONE = Proc.new do |item, conditions, block|
132
- conditions.one? { |attribute, condition| condition === attr(item, attribute) } ^
133
- if block then !!block.call(item) else false end
134
- end
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
- # 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
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