kissifer-hash-persistent 0.0.3 → 0.1.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/VERSION +1 -1
- data/hash-persistent.gemspec +8 -8
- data/lib/hash-persistent/counter.rb +22 -0
- data/lib/hash-persistent/resource.rb +43 -0
- data/lib/hash-persistent.rb +2 -2
- data/spec/counter_spec.rb +40 -0
- data/spec/resource_spec.rb +271 -0
- metadata +8 -8
- data/lib/hash-persistent/basic.rb +0 -36
- data/lib/hash-persistent/counting.rb +0 -40
- data/spec/basic_spec.rb +0 -188
- data/spec/counting_spec.rb +0 -196
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.1
|
data/hash-persistent.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{hash-persistent}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.1.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["kissifer"]
|
9
|
-
s.date = %q{2009-05-
|
9
|
+
s.date = %q{2009-05-27}
|
10
10
|
s.email = %q{tierneydrchris@gmail.com}
|
11
11
|
s.extra_rdoc_files = [
|
12
12
|
"LICENSE",
|
@@ -21,10 +21,10 @@ Gem::Specification.new do |s|
|
|
21
21
|
"VERSION",
|
22
22
|
"hash-persistent.gemspec",
|
23
23
|
"lib/hash-persistent.rb",
|
24
|
-
"lib/hash-persistent/
|
25
|
-
"lib/hash-persistent/
|
26
|
-
"spec/
|
27
|
-
"spec/
|
24
|
+
"lib/hash-persistent/counter.rb",
|
25
|
+
"lib/hash-persistent/resource.rb",
|
26
|
+
"spec/counter_spec.rb",
|
27
|
+
"spec/resource_spec.rb",
|
28
28
|
"spec/spec.opts",
|
29
29
|
"spec/spec_helper.rb"
|
30
30
|
]
|
@@ -34,8 +34,8 @@ Gem::Specification.new do |s|
|
|
34
34
|
s.rubygems_version = %q{1.3.3}
|
35
35
|
s.summary = %q{Library of base classes to simplify persisting objects in a moneta store}
|
36
36
|
s.test_files = [
|
37
|
-
"spec/
|
38
|
-
"spec/
|
37
|
+
"spec/counter_spec.rb",
|
38
|
+
"spec/resource_spec.rb",
|
39
39
|
"spec/spec_helper.rb"
|
40
40
|
]
|
41
41
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HashPersistent
|
2
|
+
module Counter
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.module_eval do
|
6
|
+
@next_key = 0
|
7
|
+
@key_inc_lock = Mutex.new
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def next_key
|
13
|
+
the_key = nil
|
14
|
+
@key_inc_lock.synchronize do
|
15
|
+
the_key = @next_key.to_s
|
16
|
+
@next_key += 1
|
17
|
+
end
|
18
|
+
the_key
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HashPersistent
|
2
|
+
module Resource
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.module_eval do
|
6
|
+
@store = {}
|
7
|
+
@prefix = ""
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def persist_to(store, prefix)
|
13
|
+
raise ArgumentError unless store.respond_to?(:has_key?) and prefix.respond_to?(:to_s)
|
14
|
+
@store = store
|
15
|
+
@prefix = prefix
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(key)
|
19
|
+
@store[@prefix + key]
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :store, :prefix
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :key
|
26
|
+
|
27
|
+
def prefix_key
|
28
|
+
self.class.prefix + key
|
29
|
+
end
|
30
|
+
|
31
|
+
def save
|
32
|
+
raise RuntimeError unless key
|
33
|
+
self.class.store[prefix_key] = self
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete
|
37
|
+
raise RuntimeError unless self.class.store.has_key?(prefix_key)
|
38
|
+
self.class.store.delete(prefix_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/lib/hash-persistent.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "HashPersistent::Counter" do
|
4
|
+
context "when mixed-in to a class" do
|
5
|
+
before(:each) do
|
6
|
+
class CounterFoo
|
7
|
+
include HashPersistent::Counter
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should impart a next_key class method" do
|
12
|
+
CounterFoo.respond_to?(:next_key).should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should supply a new key on each next_key call" do
|
16
|
+
CounterFoo.next_key.should_not == CounterFoo.next_key
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be careful when incrementing the key in multi-threaded/process environment" do
|
20
|
+
pending
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when mixed-in to more than one class" do
|
25
|
+
before(:each) do
|
26
|
+
class CounterFred
|
27
|
+
include HashPersistent::Counter
|
28
|
+
end
|
29
|
+
class CounterBarney
|
30
|
+
include HashPersistent::Counter
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should count independently for each class" do
|
35
|
+
CounterFred.next_key.should == CounterBarney.next_key
|
36
|
+
CounterFred.next_key.should == CounterBarney.next_key
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class ResourceFoo
|
4
|
+
include HashPersistent::Resource
|
5
|
+
end
|
6
|
+
|
7
|
+
class ResourceFooWithState
|
8
|
+
include HashPersistent::Resource
|
9
|
+
attr_accessor :dummy
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
return self.class == other.class &&
|
13
|
+
self.dummy == other.dummy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ResourceFooOnlyUsedOnce
|
18
|
+
include HashPersistent::Resource
|
19
|
+
attr_accessor :dummy
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
return self.class == other.class &&
|
23
|
+
self.dummy == other.dummy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "A class that includes HashPersistent::Resource" do
|
28
|
+
context "(class methods)" do
|
29
|
+
it "should acquire a persist_to class method" do
|
30
|
+
ResourceFoo.respond_to?(:persist_to).should be_true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "(persistence mechanism)" do
|
35
|
+
it "should expect to persist to a hash-like store, with a string-like prefix" do
|
36
|
+
ResourceFoo.persist_to({}, "_prefix")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should reject a non-hash-like store" do
|
40
|
+
lambda {ResourceFoo.persist_to(1, "_prefix")}.should raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should reject a non-string-like prefix" do
|
44
|
+
lambda {ResourceFoo.persist_to({}, Dummy_NoStringRep.new)}.should raise_error(ArgumentError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "(an instance that has not been saved)" do
|
49
|
+
it "should maintain a key attribute" do
|
50
|
+
ResourceFoo.new.respond_to?(:key).should be_true
|
51
|
+
ResourceFoo.new.respond_to?(:key=).should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should respond to a save method" do
|
55
|
+
ResourceFoo.new.respond_to?(:save).should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should respond to a delete method" do
|
59
|
+
ResourceFoo.new.respond_to?(:delete).should be_true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not save unless the key has been explictly set" do
|
63
|
+
lambda{ResourceFoo.new.save}.should raise_error
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not be deletable, even after a key is set" do
|
67
|
+
ResourceFoo.persist_to(Hash.new, "")
|
68
|
+
resource = ResourceFoo.new
|
69
|
+
lambda{resource.delete}.should raise_error
|
70
|
+
resource.key = "fred"
|
71
|
+
lambda{resource.delete}.should raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not be findable via its key" do
|
75
|
+
ResourceFoo.persist_to(Hash.new, "")
|
76
|
+
resource = ResourceFoo.new
|
77
|
+
resource.key = "fred"
|
78
|
+
ResourceFoo.find("fred").should == nil
|
79
|
+
ResourceFoo.find("barney").should == nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "(an instance that has been saved)" do
|
84
|
+
it "should be findable via its key" do
|
85
|
+
ResourceFoo.persist_to(Hash.new, "")
|
86
|
+
resource = ResourceFoo.new
|
87
|
+
resource.key = "fred"
|
88
|
+
resource.save
|
89
|
+
ResourceFoo.find("fred").should_not be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should correctly recover all of its state" do
|
93
|
+
ResourceFooWithState.persist_to(Hash.new, "")
|
94
|
+
resource = ResourceFooWithState.new
|
95
|
+
resource.key = "fred"
|
96
|
+
resource.dummy = ["something random", {}]
|
97
|
+
resource.save
|
98
|
+
resource_dup = resource.dup
|
99
|
+
ResourceFooWithState.find("fred").should == resource_dup
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should be deletable" do
|
103
|
+
ResourceFoo.persist_to(Hash.new, "")
|
104
|
+
resource = ResourceFoo.new
|
105
|
+
resource.key = "fred"
|
106
|
+
resource.save
|
107
|
+
lambda{resource.delete}.should_not raise_error
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should save a new state when asked" do
|
111
|
+
ResourceFooWithState.persist_to(Hash.new, "")
|
112
|
+
resource = ResourceFooWithState.new
|
113
|
+
resource.key = "fred"
|
114
|
+
|
115
|
+
resource.dummy = ["something random", {}]
|
116
|
+
resource.save
|
117
|
+
resource_dup_1 = resource.dup
|
118
|
+
|
119
|
+
resource.dummy = ["something else random", 1]
|
120
|
+
resource.save
|
121
|
+
resource_dup_2 = resource.dup
|
122
|
+
|
123
|
+
ResourceFooWithState.find("fred").should_not == resource_dup_1
|
124
|
+
ResourceFooWithState.find("fred").should == resource_dup_2
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "(an instance that has been saved and then deleted)" do
|
129
|
+
it "should not be findable via its key" do
|
130
|
+
ResourceFoo.persist_to(Hash.new, "")
|
131
|
+
resource = ResourceFoo.new
|
132
|
+
resource.key = "fred"
|
133
|
+
resource.save
|
134
|
+
resource.delete
|
135
|
+
ResourceFoo.find("fred").should == nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should not be deletable" do
|
139
|
+
ResourceFoo.persist_to(Hash.new, "")
|
140
|
+
resource = ResourceFoo.new
|
141
|
+
resource.key = "fred"
|
142
|
+
resource.save
|
143
|
+
resource.delete
|
144
|
+
lambda{resource.delete}.should raise_error
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "(use of prefix to namespace keys)" do
|
149
|
+
it "should (transparently) prepend the supplied prefix to the key of the saved instance" do
|
150
|
+
store = Hash.new
|
151
|
+
ResourceFoo.persist_to(store, "a_random_namespace::")
|
152
|
+
resource = ResourceFoo.new
|
153
|
+
resource.key = "fred"
|
154
|
+
resource.save
|
155
|
+
store.should_not have_key("fred")
|
156
|
+
store.should have_key("a_random_namespace::fred")
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should find instances when using a namespace, using only the key" do
|
160
|
+
store = Hash.new
|
161
|
+
ResourceFoo.persist_to(store, "a_random_namespace::")
|
162
|
+
resource = ResourceFoo.new
|
163
|
+
resource.key = "fred"
|
164
|
+
resource.save
|
165
|
+
ResourceFoo.find("fred").should_not == nil
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should not find instances when mistakenly given a namespaced key" do
|
169
|
+
store = Hash.new
|
170
|
+
ResourceFoo.persist_to(store, "a_random_namespace::")
|
171
|
+
resource = ResourceFoo.new
|
172
|
+
resource.key = "fred"
|
173
|
+
resource.save
|
174
|
+
ResourceFoo.find("a_random_namespace::fred").should == nil
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should correctly delete an instance from the store when using a prefix" do
|
178
|
+
store = Hash.new
|
179
|
+
ResourceFoo.persist_to(store, "a_random_namespace::")
|
180
|
+
resource = ResourceFoo.new
|
181
|
+
resource.key = "fred"
|
182
|
+
resource.save
|
183
|
+
resource.delete
|
184
|
+
ResourceFoo.find("fred").should == nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context "(use of store)" do
|
189
|
+
it "should correctly delete items from the store" do
|
190
|
+
store = Hash.new
|
191
|
+
ResourceFoo.persist_to(store, "")
|
192
|
+
resource_1 = ResourceFoo.new
|
193
|
+
resource_1.key = "fred"
|
194
|
+
resource_1.save
|
195
|
+
|
196
|
+
resource_2 = ResourceFoo.new
|
197
|
+
resource_2.key = "barney"
|
198
|
+
resource_2.save
|
199
|
+
store.should_not == Hash.new
|
200
|
+
|
201
|
+
resource_1.delete
|
202
|
+
resource_2.delete
|
203
|
+
store.should == Hash.new
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "(use of restricted hash api)" do
|
208
|
+
it "should use only the restricted hash api provided by moneta" do
|
209
|
+
lambda {
|
210
|
+
ResourceFooWithState.persist_to(Dummy_RestrictedHash.new, "prefix::")
|
211
|
+
resource_1 = ResourceFooWithState.new
|
212
|
+
resource_1.key = "fred"
|
213
|
+
resource_1.dummy = ["barney"]
|
214
|
+
resource_1.save
|
215
|
+
ResourceFooWithState.find("fred").delete
|
216
|
+
}.should_not raise_error
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context "(default store)" do
|
221
|
+
it "should function with a vdefault store/prefix if none is supplied" do
|
222
|
+
# This is implicitly tested elsewhere. Which makes this test documentation, I guess
|
223
|
+
resource_1 = ResourceFooOnlyUsedOnce.new
|
224
|
+
resource_1.key = "fred"
|
225
|
+
resource_1.dummy = ["barney"]
|
226
|
+
resource_1.save
|
227
|
+
ResourceFooOnlyUsedOnce.find("fred").should == resource_1.dup
|
228
|
+
lambda {ResourceFooOnlyUsedOnce.find("fred").delete}.should_not raise_error
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context "(multiple classes using module)" do
|
233
|
+
it "should not cross-contaminate classes that include the module" do
|
234
|
+
store_1 = Hash.new
|
235
|
+
ResourceFoo.persist_to(store_1, "")
|
236
|
+
store_2 = Hash.new
|
237
|
+
ResourceFooWithState.persist_to(store_2, "prefix::")
|
238
|
+
|
239
|
+
store_1.should == Hash.new
|
240
|
+
store_2.should == Hash.new
|
241
|
+
|
242
|
+
resource_1 = ResourceFoo.new
|
243
|
+
resource_1.key = "1"
|
244
|
+
resource_1.save
|
245
|
+
|
246
|
+
store_1.should_not == Hash.new
|
247
|
+
store_2.should == Hash.new
|
248
|
+
store_1.should have_key("1")
|
249
|
+
store_1.should_not have_key("prefix::1")
|
250
|
+
|
251
|
+
resource_1.delete
|
252
|
+
|
253
|
+
store_1.should == Hash.new
|
254
|
+
store_2.should == Hash.new
|
255
|
+
|
256
|
+
resource_2 = ResourceFooWithState.new
|
257
|
+
resource_2.key = "2"
|
258
|
+
resource_2.save
|
259
|
+
|
260
|
+
store_1.should == Hash.new
|
261
|
+
store_2.should_not == Hash.new
|
262
|
+
store_2.should have_key("prefix::2")
|
263
|
+
store_2.should_not have_key("2")
|
264
|
+
|
265
|
+
resource_2.delete
|
266
|
+
|
267
|
+
store_1.should == Hash.new
|
268
|
+
store_2.should == Hash.new
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kissifer-hash-persistent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kissifer
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-05-
|
12
|
+
date: 2009-05-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -31,10 +31,10 @@ files:
|
|
31
31
|
- VERSION
|
32
32
|
- hash-persistent.gemspec
|
33
33
|
- lib/hash-persistent.rb
|
34
|
-
- lib/hash-persistent/
|
35
|
-
- lib/hash-persistent/
|
36
|
-
- spec/
|
37
|
-
- spec/
|
34
|
+
- lib/hash-persistent/counter.rb
|
35
|
+
- lib/hash-persistent/resource.rb
|
36
|
+
- spec/counter_spec.rb
|
37
|
+
- spec/resource_spec.rb
|
38
38
|
- spec/spec.opts
|
39
39
|
- spec/spec_helper.rb
|
40
40
|
has_rdoc: false
|
@@ -64,6 +64,6 @@ signing_key:
|
|
64
64
|
specification_version: 3
|
65
65
|
summary: Library of base classes to simplify persisting objects in a moneta store
|
66
66
|
test_files:
|
67
|
-
- spec/
|
68
|
-
- spec/
|
67
|
+
- spec/counter_spec.rb
|
68
|
+
- spec/resource_spec.rb
|
69
69
|
- spec/spec_helper.rb
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module HashPersistent
|
2
|
-
class Basic
|
3
|
-
class << self
|
4
|
-
@@store = {}
|
5
|
-
@@prefix = ""
|
6
|
-
|
7
|
-
def setup(store, prefix)
|
8
|
-
raise ArgumentError unless store.respond_to?(:has_key?) and prefix.respond_to?(:to_s)
|
9
|
-
@@store = store
|
10
|
-
@@prefix = prefix
|
11
|
-
end
|
12
|
-
|
13
|
-
def find(key)
|
14
|
-
@@store[@@prefix + key]
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_reader :key
|
19
|
-
|
20
|
-
def initialize(key)
|
21
|
-
raise RuntimeError if @@store.has_key?(key)
|
22
|
-
@key = key
|
23
|
-
@@store[@@prefix + @key] = self
|
24
|
-
end
|
25
|
-
|
26
|
-
def delete
|
27
|
-
raise RuntimeError unless @@store.has_key?(@@prefix + @key)
|
28
|
-
@@store.delete(@@prefix + @key)
|
29
|
-
end
|
30
|
-
|
31
|
-
def update
|
32
|
-
raise RuntimeError unless @@store.has_key?(@@prefix + @key)
|
33
|
-
@@store[@@prefix + @key] = self
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module HashPersistent
|
2
|
-
class Counting
|
3
|
-
class << self
|
4
|
-
@@store = {}
|
5
|
-
@@prefix = ""
|
6
|
-
@@next_key = 0
|
7
|
-
@@next_key_inc_lock = Mutex.new
|
8
|
-
|
9
|
-
def setup(store, prefix)
|
10
|
-
raise ArgumentError unless store.respond_to?(:has_key?) and prefix.respond_to?(:to_s)
|
11
|
-
@@store = store
|
12
|
-
@@prefix = prefix
|
13
|
-
end
|
14
|
-
|
15
|
-
def find(key)
|
16
|
-
@@store[@@prefix + key]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
attr_reader :key
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
@@next_key_inc_lock.synchronize do
|
24
|
-
@key = @@next_key.to_s
|
25
|
-
@@next_key += 1
|
26
|
-
end
|
27
|
-
@@store[@@prefix + @key] = self
|
28
|
-
end
|
29
|
-
|
30
|
-
def delete
|
31
|
-
raise RuntimeError unless @@store.has_key?(@@prefix + @key)
|
32
|
-
@@store.delete(@@prefix + @key)
|
33
|
-
end
|
34
|
-
|
35
|
-
def update
|
36
|
-
raise RuntimeError unless @@store.has_key?(@@prefix + @key)
|
37
|
-
@@store[@@prefix + @key] = self
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
data/spec/basic_spec.rb
DELETED
@@ -1,188 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
describe "HashPersistent::Basic" do
|
4
|
-
context "(before use)" do
|
5
|
-
it "should accept a hash-like store, and a string-like prefix, as class initialisers" do
|
6
|
-
HashPersistent::Basic.setup({}, "_prefix")
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should reject a non-hash-like store" do
|
10
|
-
lambda {HashPersistent::Basic.setup(1, "_prefix")}.should raise_error(ArgumentError)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should reject a non-string-like prefix" do
|
14
|
-
lambda {HashPersistent::Basic.setup({}, Dummy_NoStringRep.new)}.should raise_error(ArgumentError)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "(creating objects)" do
|
19
|
-
it "should allow not objects to be created without a key" do
|
20
|
-
HashPersistent::Basic.setup({}, "")
|
21
|
-
lambda{HashPersistent::Basic.new}.should raise_error
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should allow objects to be created with a key" do
|
25
|
-
HashPersistent::Basic.setup({}, "")
|
26
|
-
lambda{HashPersistent::Basic.new("a_key")}.should_not raise_error
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should not allow objects to be created with a duplicate key" do
|
30
|
-
HashPersistent::Basic.setup({}, "")
|
31
|
-
HashPersistent::Basic.new("a_key")
|
32
|
-
lambda{HashPersistent::Basic.new("a_key")}.should raise_error
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should return an object after creation" do
|
36
|
-
HashPersistent::Basic.setup({}, "")
|
37
|
-
new_object = HashPersistent::Basic.new("a_key")
|
38
|
-
new_object.class.should == HashPersistent::Basic
|
39
|
-
new_object.key.should == "a_key"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context "(retrieving objects)" do
|
44
|
-
it "should not return an object when no objects have been created" do
|
45
|
-
HashPersistent::Basic.setup({}, "")
|
46
|
-
HashPersistent::Basic.find("a_key").should be_nil
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should return created objects when given the correct key" do
|
50
|
-
HashPersistent::Basic.setup({}, "")
|
51
|
-
HashPersistent::Basic.new("a_key")
|
52
|
-
new_object = HashPersistent::Basic.find("a_key")
|
53
|
-
new_object.class.should == HashPersistent::Basic
|
54
|
-
new_object.key.should == "a_key"
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should not return an object when given an incorrect key" do
|
58
|
-
HashPersistent::Basic.setup({}, "")
|
59
|
-
HashPersistent::Basic.new("a_key")
|
60
|
-
HashPersistent::Basic.find("another_key").should be_nil
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
context "(deleting objects)" do
|
65
|
-
it "should delete an object when asked" do
|
66
|
-
HashPersistent::Basic.setup({}, "")
|
67
|
-
new_object = HashPersistent::Basic.new("a_key")
|
68
|
-
new_object.delete
|
69
|
-
HashPersistent::Basic.find("a_key").should be_nil
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should not allow an object to be deleted twice" do
|
73
|
-
HashPersistent::Basic.setup({}, "")
|
74
|
-
new_object = HashPersistent::Basic.new("a_key")
|
75
|
-
new_object.delete
|
76
|
-
lambda{new_object.delete}.should raise_error
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
context "(key namespacing)" do
|
81
|
-
it "should (transparently) prepend the supplied prefix to the key of the created object" do
|
82
|
-
store = {}
|
83
|
-
HashPersistent::Basic.setup(store, "a_namespace::")
|
84
|
-
new_object = HashPersistent::Basic.new("a_key")
|
85
|
-
new_object.class.should == HashPersistent::Basic
|
86
|
-
new_object.key.should == "a_key"
|
87
|
-
store.should_not have_key("a_key")
|
88
|
-
store.should have_key("a_namespace::a_key")
|
89
|
-
end
|
90
|
-
|
91
|
-
it "should correctly find objects when given a namespace" do
|
92
|
-
HashPersistent::Basic.setup({}, "a_namespace::")
|
93
|
-
HashPersistent::Basic.new("a_key")
|
94
|
-
new_object = HashPersistent::Basic.find("a_key")
|
95
|
-
new_object.should_not be_nil
|
96
|
-
new_object.key.should == "a_key"
|
97
|
-
end
|
98
|
-
|
99
|
-
it "should not find objects when mistakenly given a namespaced key" do
|
100
|
-
HashPersistent::Basic.setup({}, "a_namespace::")
|
101
|
-
HashPersistent::Basic.new("a_key")
|
102
|
-
HashPersistent::Basic.find("a_namespace::a_key").should be_nil
|
103
|
-
end
|
104
|
-
|
105
|
-
it "should correctly delete objects when given a namespace" do
|
106
|
-
store = {}
|
107
|
-
HashPersistent::Basic.setup(store, "a_namespace::")
|
108
|
-
new_object = HashPersistent::Basic.new("a_key")
|
109
|
-
new_object.delete
|
110
|
-
|
111
|
-
HashPersistent::Basic.find("a_key").should be_nil
|
112
|
-
store.should_not have_key("a_namespace::a_key")
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
|
117
|
-
class Dummy_BasicDerivedClass < HashPersistent::Basic
|
118
|
-
attr_accessor :dummy
|
119
|
-
|
120
|
-
def initialize(key, dummy)
|
121
|
-
super(key)
|
122
|
-
@dummy = dummy
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
context "(derived classes)" do
|
127
|
-
it "should correctly store the full member list of a derived class" do
|
128
|
-
Dummy_BasicDerivedClass.setup({}, "")
|
129
|
-
created_dummy = Dummy_BasicDerivedClass.new("a_key", 123)
|
130
|
-
created_dummy.key.should == "a_key"
|
131
|
-
created_dummy.dummy.should == 123
|
132
|
-
|
133
|
-
retrieved_dummy = Dummy_BasicDerivedClass.find("a_key")
|
134
|
-
retrieved_dummy.key.should == "a_key"
|
135
|
-
retrieved_dummy.dummy.should == 123
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
context "(updating objects)" do
|
140
|
-
it "should correctly update members of a derived class" do
|
141
|
-
Dummy_BasicDerivedClass.setup({}, "prefix::")
|
142
|
-
created_dummy = Dummy_BasicDerivedClass.new("a_key", 123)
|
143
|
-
|
144
|
-
duplicated_dummy = created_dummy.dup
|
145
|
-
duplicated_dummy.dummy = 456
|
146
|
-
duplicated_dummy.update
|
147
|
-
|
148
|
-
retrieved_dummy = Dummy_BasicDerivedClass.find("a_key")
|
149
|
-
retrieved_dummy.dummy.should_not == 123
|
150
|
-
retrieved_dummy.dummy.should == 456
|
151
|
-
end
|
152
|
-
|
153
|
-
it "should not allow a deleted object to update" do
|
154
|
-
Dummy_BasicDerivedClass.setup({}, "prefix::")
|
155
|
-
created_dummy = Dummy_BasicDerivedClass.new("a_key", 123)
|
156
|
-
created_dummy.delete
|
157
|
-
created_dummy.dummy = 456
|
158
|
-
lambda{created_dummy.update}.should raise_error
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
context "(restricted hash api)" do
|
163
|
-
it "should use only the restricted hash api provided by moneta" do
|
164
|
-
lambda {
|
165
|
-
Dummy_BasicDerivedClass.setup(Dummy_RestrictedHash.new, "prefix::")
|
166
|
-
created_dummy = Dummy_BasicDerivedClass.new("a_key", 123)
|
167
|
-
|
168
|
-
duplicated_dummy = created_dummy.dup
|
169
|
-
duplicated_dummy.dummy = 456
|
170
|
-
duplicated_dummy.update
|
171
|
-
|
172
|
-
retrieved_dummy = Dummy_BasicDerivedClass.find("a_key")
|
173
|
-
retrieved_dummy.delete
|
174
|
-
}.should_not raise_error
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
context "(concurrent access)" do
|
179
|
-
it "should be careful doing various operations" do
|
180
|
-
# What to test here? Duplicate in the other similar classes
|
181
|
-
# Test: Update, update vs delete, delete vs find, update vs find
|
182
|
-
# Eugh, how best to do this? Options:
|
183
|
-
# Subclass Hash and slow the set/get methods down?
|
184
|
-
# Monkey patch the Mutex class to add lock/unlock callbacks?
|
185
|
-
pending
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
data/spec/counting_spec.rb
DELETED
@@ -1,196 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
describe "HashPersistent::Counting" do
|
4
|
-
context "(before use)" do
|
5
|
-
it "should accept a hash-like store, and a string-like prefix, as class initialisers" do
|
6
|
-
HashPersistent::Counting.setup({}, "_prefix")
|
7
|
-
end
|
8
|
-
|
9
|
-
it "should reject a non-hash-like store" do
|
10
|
-
lambda {HashPersistent::Counting.setup(1, "_prefix")}.should raise_error(ArgumentError)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should reject a non-string-like prefix" do
|
14
|
-
lambda {HashPersistent::Counting.setup({}, Dummy_NoStringRep.new)}.should raise_error(ArgumentError)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "(creating objects)" do
|
19
|
-
it "should not allow objects to be created with a key" do
|
20
|
-
HashPersistent::Counting.setup({}, "")
|
21
|
-
lambda{HashPersistent::Counting.new("a_key")}.should raise_error
|
22
|
-
end
|
23
|
-
|
24
|
-
it "should allow objects to be created without a key" do
|
25
|
-
HashPersistent::Counting.setup({}, "")
|
26
|
-
lambda{HashPersistent::Counting.new}.should_not raise_error
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should return an object after creation" do
|
30
|
-
HashPersistent::Counting.setup({}, "")
|
31
|
-
new_object = HashPersistent::Counting.new
|
32
|
-
new_object.class.should == HashPersistent::Counting
|
33
|
-
end
|
34
|
-
|
35
|
-
it "generate unique keys for each object creation" do
|
36
|
-
HashPersistent::Counting.setup({}, "")
|
37
|
-
keys = []
|
38
|
-
lots = 100 # How many is enough here? We just have to pick a number I guess...
|
39
|
-
lots.times do
|
40
|
-
keys << HashPersistent::Counting.new.key
|
41
|
-
end
|
42
|
-
keys.uniq.should == keys
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
context "(retrieving objects)" do
|
47
|
-
it "should not return an object when no objects have been created" do
|
48
|
-
HashPersistent::Counting.setup({}, "")
|
49
|
-
HashPersistent::Counting.find("1").should be_nil
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should return created objects when given the correct key" do
|
53
|
-
HashPersistent::Counting.setup({}, "")
|
54
|
-
key = HashPersistent::Counting.new.key
|
55
|
-
new_object = HashPersistent::Counting.find(key)
|
56
|
-
new_object.class.should == HashPersistent::Counting
|
57
|
-
new_object.key.should == key
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should not return an object when given an incorrect key" do
|
61
|
-
HashPersistent::Counting.setup({}, "")
|
62
|
-
key = HashPersistent::Counting.new.key.to_s + "_not_a_valid_key"
|
63
|
-
HashPersistent::Counting.find(key).should be_nil
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "(deleting objects)" do
|
68
|
-
it "should delete an object when asked" do
|
69
|
-
HashPersistent::Counting.setup({}, "")
|
70
|
-
new_object = HashPersistent::Counting.new
|
71
|
-
key = new_object.key
|
72
|
-
new_object.delete
|
73
|
-
HashPersistent::Counting.find(key).should be_nil
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should not allow an object to be deleted twice" do
|
77
|
-
HashPersistent::Counting.setup({}, "")
|
78
|
-
new_object = HashPersistent::Counting.new
|
79
|
-
new_object.delete
|
80
|
-
lambda{new_object.delete}.should raise_error
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
context "(key namespacing)" do
|
85
|
-
it "should (transparently) prepend the supplied prefix to the key of the created object" do
|
86
|
-
store = {}
|
87
|
-
HashPersistent::Counting.setup(store, "a_namespace::")
|
88
|
-
new_object = HashPersistent::Counting.new
|
89
|
-
new_object.class.should == HashPersistent::Counting
|
90
|
-
store.should_not have_key("#{new_object.key}")
|
91
|
-
store.should_not have_key(new_object.key)
|
92
|
-
store.should have_key("a_namespace::#{new_object.key}")
|
93
|
-
end
|
94
|
-
|
95
|
-
it "should correctly find objects when given a namespace" do
|
96
|
-
HashPersistent::Counting.setup({}, "a_namespace::")
|
97
|
-
key = HashPersistent::Counting.new.key
|
98
|
-
new_object = HashPersistent::Counting.find(key)
|
99
|
-
new_object.should_not be_nil
|
100
|
-
new_object.key.should == key
|
101
|
-
end
|
102
|
-
|
103
|
-
it "should not find objects when mistakenly given a namespaced key" do
|
104
|
-
HashPersistent::Counting.setup({}, "a_namespace::")
|
105
|
-
key = HashPersistent::Counting.new
|
106
|
-
HashPersistent::Counting.find("a_namespace::#{key}").should be_nil
|
107
|
-
end
|
108
|
-
|
109
|
-
it "should correctly delete objects when given a namespace" do
|
110
|
-
store = {}
|
111
|
-
HashPersistent::Counting.setup(store, "a_namespace::")
|
112
|
-
new_object = HashPersistent::Counting.new
|
113
|
-
key = new_object.key
|
114
|
-
new_object.delete
|
115
|
-
|
116
|
-
HashPersistent::Counting.find(key).should be_nil
|
117
|
-
store.should_not have_key("a_namespace::#{key}")
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
class Dummy_CountingDerivedClass < HashPersistent::Counting
|
123
|
-
attr_accessor :dummy
|
124
|
-
|
125
|
-
def initialize(dummy)
|
126
|
-
super()
|
127
|
-
@dummy = dummy
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
context "(derived classes)" do
|
132
|
-
it "should correctly store the full member list of a derived class" do
|
133
|
-
Dummy_CountingDerivedClass.setup({}, "")
|
134
|
-
created_dummy = Dummy_CountingDerivedClass.new(123)
|
135
|
-
created_dummy.dummy.should == 123
|
136
|
-
|
137
|
-
retrieved_dummy = Dummy_CountingDerivedClass.find(created_dummy.key)
|
138
|
-
retrieved_dummy.key.should == created_dummy.key
|
139
|
-
retrieved_dummy.dummy.should == 123
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
context "(updating objects)" do
|
144
|
-
it "should correctly update members of a derived class" do
|
145
|
-
Dummy_CountingDerivedClass.setup({}, "prefix::")
|
146
|
-
created_dummy = Dummy_CountingDerivedClass.new(123)
|
147
|
-
key = created_dummy.key
|
148
|
-
|
149
|
-
duplicated_dummy = created_dummy.dup
|
150
|
-
duplicated_dummy.dummy = 456
|
151
|
-
duplicated_dummy.update
|
152
|
-
|
153
|
-
retrieved_dummy = Dummy_CountingDerivedClass.find(key)
|
154
|
-
retrieved_dummy.dummy.should_not == 123
|
155
|
-
retrieved_dummy.dummy.should == 456
|
156
|
-
|
157
|
-
retrieved_dummy.key.should == key
|
158
|
-
end
|
159
|
-
|
160
|
-
it "should not allow a deleted object to update" do
|
161
|
-
Dummy_CountingDerivedClass.setup({}, "prefix::")
|
162
|
-
created_dummy = Dummy_CountingDerivedClass.new(123)
|
163
|
-
created_dummy.delete
|
164
|
-
created_dummy.dummy = 456
|
165
|
-
lambda{created_dummy.update}.should raise_error
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
context "(restricted hash api)" do
|
170
|
-
it "should use only the restricted hash api provided by moneta" do
|
171
|
-
lambda {
|
172
|
-
Dummy_CountingDerivedClass.setup(Dummy_RestrictedHash.new, "prefix::")
|
173
|
-
created_dummy = Dummy_CountingDerivedClass.new(123)
|
174
|
-
key = created_dummy.key
|
175
|
-
|
176
|
-
duplicated_dummy = created_dummy.dup
|
177
|
-
duplicated_dummy.dummy = 456
|
178
|
-
duplicated_dummy.update
|
179
|
-
|
180
|
-
retrieved_dummy = Dummy_CountingDerivedClass.find(key)
|
181
|
-
retrieved_dummy.delete
|
182
|
-
}.should_not raise_error
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
context "(concurrent access)" do
|
187
|
-
it "should be careful incrementing the auto-generated key" do
|
188
|
-
# TODO. Subclass Hash, adda delay into the []= method (or another one?)
|
189
|
-
# Kick off two threads and create a bunch of objects
|
190
|
-
# Collect keys into arrays, merge, sort and compare for equality with
|
191
|
-
# an array of sequential numbers the same size as the number of objects
|
192
|
-
# we created
|
193
|
-
pending
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|