memstore 1.2.1 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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