hammerspace-fork 0.1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ require 'forwardable'
2
+
3
+ module Hammerspace
4
+
5
+ # "Frontend" class
6
+ #
7
+ # All hammerspace functionality is exposed through this class's interface.
8
+ # Responsible for setting up the backend and delegating methods to the
9
+ # backend. Also handles default values. This functionality is designed to be
10
+ # consistent across backends; backends cannot be override this functionality.
11
+ class Hash
12
+ extend Forwardable
13
+
14
+ attr_reader :path
15
+ attr_reader :options
16
+ attr_reader :backend
17
+ attr_reader :default_proc
18
+
19
+ def_delegators :backend, *Enumerable.instance_methods
20
+ def_delegators :backend, *HashMethods.instance_methods
21
+ def_delegators :backend, :close, :uid
22
+
23
+ DEFAULT_OPTIONS = {
24
+ :backend => Hammerspace::Backend::Sparkey
25
+ }
26
+
27
+ def initialize(path, options={}, *args, &block)
28
+ raise ArgumentError, "wrong number of arguments" if args.size > 1
29
+
30
+ @path = path
31
+ @options = DEFAULT_OPTIONS.merge(options)
32
+ @backend = @options[:backend].new(self, @path, @options)
33
+
34
+ if block_given?
35
+ self.default_proc=(block)
36
+ raise ArgumentError, "wrong number of arguments" if args.size == 1
37
+ else
38
+ self.default=args.first
39
+ end
40
+ end
41
+
42
+ def default(*args)
43
+ if @default_proc && args.size
44
+ @default_proc.call(self, args.first)
45
+ else
46
+ @default
47
+ end
48
+ end
49
+
50
+ def default=(value)
51
+ @default_proc = nil
52
+ @default = value
53
+ end
54
+
55
+ def default_proc=(value)
56
+ @default = nil
57
+ @default_proc = value
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,234 @@
1
+ module Hammerspace
2
+
3
+ # Basic implementations of most methods supported by Ruby's hash
4
+ #
5
+ # Analogous to Enumerable. Mixed into Hammerspace::Backend::Base. A backend
6
+ # need only implement four of these methods: [], []=, delete, and each. (A
7
+ # backend should also implement close and uid, but these are not hash
8
+ # methods; they are hammerspace-specific.) However, a backend may choose to
9
+ # override some of the default implementations if the backend is able to
10
+ # implement the methods more efficiently.
11
+ module HashMethods
12
+
13
+ def ==(hash)
14
+ return false if size != hash.size
15
+ each do |key, value|
16
+ return false unless hash.has_key?(key)
17
+ return false unless hash[key] == value
18
+ end
19
+ true
20
+ end
21
+
22
+ def [](key)
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def []=(key, value)
27
+ raise NotImplementedError
28
+ end
29
+
30
+ def assoc(key)
31
+ find { |k,v| k == key }
32
+ end
33
+
34
+ def clear
35
+ each { |key, value| delete(key) }
36
+ close # flush immediately
37
+ frontend
38
+ end
39
+
40
+ def delete(key)
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def delete_if(&block)
45
+ if block_given?
46
+ reject!(&block)
47
+ frontend
48
+ else
49
+ reject!
50
+ end
51
+ end
52
+
53
+ def each(&block)
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def each_key
58
+ if block_given?
59
+ each { |key, value| yield key }
60
+ else
61
+ Enumerator.new { |y| each { |key, value| y << key } }
62
+ end
63
+ end
64
+
65
+ def each_value
66
+ if block_given?
67
+ each { |key, value| yield value }
68
+ else
69
+ Enumerator.new { |y| each { |key, value| y << value } }
70
+ end
71
+ end
72
+
73
+ def empty?
74
+ size == 0
75
+ end
76
+
77
+ def eql?(hash)
78
+ return false if size != hash.size
79
+ each do |key, value|
80
+ return false unless hash.has_key?(key)
81
+ return false unless hash[key].eql?(value)
82
+ end
83
+ true
84
+ end
85
+
86
+ def fetch(key, *args)
87
+ raise ArgumentError, "wrong number of arguments" if args.size > 1
88
+
89
+ return self[key] if has_key?(key)
90
+
91
+ if block_given?
92
+ yield key
93
+ elsif args.size == 1
94
+ args.first
95
+ else
96
+ raise KeyError, "key not found: \"#{key}\""
97
+ end
98
+ end
99
+
100
+ def flatten(*args)
101
+ # Note: the optional level argument is supported for compatibility, but
102
+ # it will never have an effect because only string values are
103
+ # supported.
104
+ raise ArgumentError, "wrong number of arguments" if args.size > 1
105
+
106
+ each_with_object([]) do |args, array|
107
+ array << args.first
108
+ array << args.last
109
+ end
110
+ end
111
+
112
+ def has_key?(key)
113
+ !!find { |k,v| k.eql?(key) }
114
+ end
115
+
116
+ def has_value?(value)
117
+ !!find { |k,v| v == value }
118
+ end
119
+
120
+ def keep_if(&block)
121
+ if block_given?
122
+ select!(&block)
123
+ frontend
124
+ else
125
+ select!
126
+ end
127
+ end
128
+
129
+ def key(key)
130
+ has_key?(key) ? self[key] : nil
131
+ end
132
+
133
+ def keys
134
+ each.map { |key, value| key }
135
+ end
136
+
137
+ def merge!(hash)
138
+ hash.each do |key, value|
139
+ if block_given?
140
+ self[key] = yield key, self[key], value
141
+ else
142
+ self[key] = value
143
+ end
144
+ end
145
+ frontend
146
+ end
147
+
148
+ def rassoc(value)
149
+ find { |k,v| v == value }
150
+ end
151
+
152
+ def reject!
153
+ if block_given?
154
+ any_deleted = false
155
+ each do |key, value|
156
+ if yield key, value
157
+ any_deleted = true
158
+ delete(key)
159
+ end
160
+ end
161
+ any_deleted ? frontend : nil
162
+ else
163
+ Enumerator.new do |y|
164
+ each { |key, value| delete(key) if y.yield(key, value) }
165
+ end
166
+ end
167
+ end
168
+
169
+ def replace(hash)
170
+ clear
171
+ merge!(hash)
172
+ end
173
+
174
+ def select!
175
+ if block_given?
176
+ any_deleted = false
177
+ each do |key, value|
178
+ unless yield key, value
179
+ any_deleted = true
180
+ delete(key)
181
+ end
182
+ end
183
+ any_deleted ? frontend : nil
184
+ else
185
+ Enumerator.new do |y|
186
+ each { |key, value| delete(key) unless y.yield(key, value) }
187
+ end
188
+ end
189
+ end
190
+
191
+ def shift
192
+ items = take(1)
193
+ if items.empty?
194
+ frontend.default
195
+ else
196
+ pair = items.first
197
+ delete(pair.first)
198
+ pair
199
+ end
200
+ end
201
+
202
+ def size
203
+ count = 0
204
+ each { |key, value| count += 1 }
205
+ count
206
+ end
207
+
208
+ def to_hash
209
+ each_with_object({}) { |args, hash| hash[args.first] = args.last }
210
+ end
211
+
212
+ def values
213
+ each.map { |key, value| value }
214
+ end
215
+
216
+ def values_at(*args)
217
+ args.map { |key| self[key] }
218
+ end
219
+
220
+ alias_method :store, :[]=
221
+ alias_method :each_pair, :each
222
+ alias_method :key?, :has_key?
223
+
224
+ # alias_method seems to conflict with Enumerable's version of these methods
225
+ def include?(key); has_key?(key); end
226
+ def member?(key); has_key?(key); end
227
+
228
+ alias_method :value?, :has_value?
229
+ alias_method :update, :merge!
230
+ alias_method :initialize_copy, :replace
231
+ alias_method :length, :size
232
+
233
+ end
234
+ end
@@ -0,0 +1,3 @@
1
+ module Hammerspace
2
+ VERSION = '0.1.5.1'
3
+ end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ $:.push File.expand_path('../../lib', __FILE__)
3
+ $:.push File.expand_path('../../spec', __FILE__)
4
+
5
+ require 'trollop'
6
+ require 'hammerspace'
7
+ require 'support/write_concurrency_test'
8
+ require 'fileutils'
9
+
10
+ include WriteConcurrencyTest
11
+
12
+ opts = Trollop::options do
13
+ opt :path, 'Path to hammerspace root', :default => '/tmp'
14
+ opt :backend, 'Hammerspace backend to use', :default => 'Sparkey'
15
+ opt :concurrency, 'Number of writer processes to fork', :default => 10
16
+ opt :iterations, 'Number of times each process should write', :default => 10
17
+ opt :size, 'Number of items to write on each iteration', :default => 10
18
+ end
19
+
20
+ path = File.join(opts[:path], 'write_concurrency_test')
21
+
22
+ begin
23
+ run_write_concurrency_test(
24
+ path,
25
+ {
26
+ :backend => Hammerspace::Backend.const_get(opts[:backend])
27
+ },
28
+ opts[:concurrency],
29
+ opts[:iterations],
30
+ opts[:size])
31
+ ensure
32
+ FileUtils.rm_rf(path)
33
+ end
34
+
35
+ puts "OK"
36
+
@@ -0,0 +1,1487 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hammerspace do
4
+
5
+ [Hammerspace::Backend::Sparkey].each do |backend|
6
+ describe backend do
7
+
8
+ let(:path) { HAMMERSPACE_ROOT }
9
+ let(:options) { { :backend => backend } }
10
+
11
+ before do
12
+ FileUtils.rm_rf(path, :secure => true)
13
+ end
14
+
15
+ after(:all) do
16
+ FileUtils.rm_rf(path, :secure => true)
17
+ end
18
+
19
+ it "gets after set" do
20
+ hash = Hammerspace.new(path, options)
21
+ hash['foo'] = 'bar'
22
+ hash['foo'].should == 'bar'
23
+ hash.close
24
+ end
25
+
26
+ it "gets before set" do
27
+ hash = Hammerspace.new(path, options)
28
+ hash['foo'].should be_nil
29
+ hash.close
30
+ end
31
+
32
+ it "supports interleaved gets and sets" do
33
+ hash = Hammerspace.new(path, options)
34
+ hash['foo'] = 'bar'
35
+ hash['foo'].should == 'bar'
36
+ hash['foo'] = 'newvalue'
37
+ hash['foo'].should == 'newvalue'
38
+ hash.close
39
+ end
40
+
41
+ it "persists values after reopen" do
42
+ hash = Hammerspace.new(path, options)
43
+ hash['foo'] = 'bar'
44
+ hash.close
45
+
46
+ hash = Hammerspace.new(path, options)
47
+ hash['foo'].should == 'bar'
48
+ hash.close
49
+ end
50
+
51
+ it "allows updating after reopen" do
52
+ hash = Hammerspace.new(path, options)
53
+ hash['foo'] = 'bar'
54
+ hash.close
55
+
56
+ hash = Hammerspace.new(path, options)
57
+ hash['foo'] = 'newvalue'
58
+ hash['foo'].should == 'newvalue'
59
+ hash.close
60
+ end
61
+
62
+ it "supports multiple readers" do
63
+ hash = Hammerspace.new(path, options)
64
+ hash['foo'] = 'bar'
65
+ hash.close
66
+
67
+ reader1 = Hammerspace.new(path, options)
68
+ reader1['foo'].should == 'bar'
69
+
70
+ reader2 = Hammerspace.new(path, options)
71
+ reader2['foo'].should == 'bar'
72
+
73
+ reader1.close
74
+ reader2.close
75
+ end
76
+
77
+ it "isolates readers" do
78
+ hash = Hammerspace.new(path, options)
79
+ hash['foo'] = 'bar'
80
+ hash.close
81
+
82
+ reader1 = Hammerspace.new(path, options)
83
+ reader1['foo'].should == 'bar'
84
+
85
+ hash = Hammerspace.new(path, options)
86
+ hash['foo'] = 'newvalue'
87
+ hash.close
88
+
89
+ reader1['foo'].should == 'bar' # still 'bar'
90
+
91
+ reader2 = Hammerspace.new(path, options)
92
+ reader2['foo'].should == 'newvalue'
93
+
94
+ reader1.close
95
+ reader2.close
96
+ end
97
+
98
+ it "supports multiple writers" do
99
+ writer1 = Hammerspace.new(path, options)
100
+ writer1['foo'] = 'one'
101
+
102
+ writer2 = Hammerspace.new(path, options)
103
+ writer2['foo'] = 'two'
104
+ writer2['bar'] = 'two' # test works even without locking if this isn't here?
105
+
106
+ writer2.close
107
+ writer1.close # last write wins
108
+
109
+ hash = Hammerspace.new(path, options)
110
+ hash['foo'].should == 'one'
111
+ hash['bar'].should be_nil
112
+ hash.close
113
+ end
114
+
115
+ it "supports multiple appenders" do
116
+ hash = Hammerspace.new(path, options)
117
+ hash['foo'] = 'bar'
118
+ hash.close
119
+
120
+ writer1 = Hammerspace.new(path, options)
121
+ writer1['foo'] = 'one'
122
+
123
+ writer2 = Hammerspace.new(path, options)
124
+ writer2['foo'] = 'two'
125
+ writer2['bar'] = 'two' # test works even without locking if this isn't here?
126
+
127
+ writer2.close
128
+ writer1.close # last write wins
129
+
130
+ hash = Hammerspace.new(path, options)
131
+ hash['foo'].should == 'one'
132
+ hash['bar'].should be_nil
133
+ hash.close
134
+ end
135
+
136
+ it "handles high write concurrency" do
137
+ run_write_concurrency_test(path, options)
138
+ end
139
+
140
+ describe "#==" do
141
+
142
+ it "returns false if different sizes" do
143
+ h1 = Hammerspace.new(File.join(path, '1'), options)
144
+ h1['a'] = 'A'
145
+ h1['b'] = 'B'
146
+
147
+ h2 = Hammerspace.new(File.join(path, '2'), options)
148
+ h2['a'] = 'A'
149
+
150
+ h1.should_not == h2
151
+
152
+ h1.close
153
+ h2.close
154
+ end
155
+
156
+ it "does not consider default values" do
157
+ h1 = Hammerspace.new(File.join(path, '1'), options)
158
+ h1['a'] = 'A'
159
+ h1['b'] = 'B'
160
+
161
+ h2 = Hammerspace.new(File.join(path, '2'), options, 'B')
162
+ h2['a'] = 'A'
163
+
164
+ h1.should_not == h2
165
+
166
+ h1.close
167
+ h2.close
168
+ end
169
+
170
+ it "returns false if different keys" do
171
+ h1 = Hammerspace.new(File.join(path, '1'), options)
172
+ h1['a'] = 'A'
173
+ h1['b'] = 'B'
174
+
175
+ h2 = Hammerspace.new(File.join(path, '2'), options)
176
+ h2['a'] = 'A'
177
+ h2['B'] = 'B'
178
+
179
+ h1.should_not == h2
180
+
181
+ h1.close
182
+ h2.close
183
+ end
184
+
185
+ it "returns false if different values" do
186
+ h1 = Hammerspace.new(File.join(path, '1'), options)
187
+ h1['a'] = 'A'
188
+ h1['b'] = 'B'
189
+
190
+ h2 = Hammerspace.new(File.join(path, '2'), options)
191
+ h2['a'] = 'A'
192
+ h2['b'] = 'b'
193
+
194
+ h1.should_not == h2
195
+
196
+ h1.close
197
+ h2.close
198
+ end
199
+
200
+ it "returns true if same keys and values" do
201
+ h1 = Hammerspace.new(File.join(path, '1'), options)
202
+ h1['a'] = 'A'
203
+ h1['b'] = 'B'
204
+
205
+ h2 = Hammerspace.new(File.join(path, '2'), options)
206
+ h2['a'] = 'A'
207
+ h2['b'] = 'B'
208
+
209
+ h1.should == h2
210
+
211
+ h1.close
212
+ h2.close
213
+ end
214
+
215
+ it "works with hashes" do
216
+ hash = Hammerspace.new(File.join(path, '1'), options)
217
+ hash['a'] = 'A'
218
+ hash['b'] = 'B'
219
+ hash.should == {'a' => 'A', 'b' => 'B'}
220
+ hash.close
221
+ end
222
+
223
+ end
224
+
225
+ describe "#[]" do
226
+
227
+ it "returns value if key exists" do
228
+ hash = Hammerspace.new(path, options)
229
+ hash.default = 'default'
230
+ hash['foo'] = 'bar'
231
+ hash['foo'].should == 'bar'
232
+ hash.close
233
+ end
234
+
235
+ it "returns default value if key does not exist" do
236
+ hash = Hammerspace.new(path, options)
237
+ hash.default = 'default'
238
+ hash['foo'].should == 'default'
239
+ hash.close
240
+ end
241
+
242
+ it "supports storing the default value" do
243
+ hash = Hammerspace.new(path, options) { |h,k| h[k] = 'Go fish' }
244
+ hash['foo'].should == 'Go fish'
245
+ hash.has_key?('foo').should be_true
246
+ hash.close
247
+ end
248
+
249
+ end
250
+
251
+ describe "#[]=" do
252
+
253
+ it "handles key mutation" do
254
+ hash = Hammerspace.new(path, options)
255
+ key = 'foo'
256
+ hash[key] = 'bar'
257
+ key = 'key'
258
+ hash['foo'].should == 'bar'
259
+ hash['key'].should be_nil
260
+ hash.close
261
+ end
262
+
263
+ end
264
+
265
+ describe "#assoc" do
266
+
267
+ it "returns key value pair when key is present" do
268
+ hash = Hammerspace.new(path, options)
269
+ hash['foo'] = 'bar'
270
+ hash.assoc('foo').should == ['foo', 'bar']
271
+ hash.close
272
+ end
273
+
274
+ it "returns nil when key is not present" do
275
+ hash = Hammerspace.new(path, options)
276
+ hash['foo'] = 'bar'
277
+ hash.assoc('otherkey').should be_nil
278
+ hash.close
279
+ end
280
+
281
+ it "returns nil when empty" do
282
+ hash = Hammerspace.new(path, options)
283
+ hash.assoc('foo').should be_nil
284
+ hash.close
285
+ end
286
+
287
+ end
288
+
289
+ describe "#clear" do
290
+
291
+ it "removes all keys and values" do
292
+ hash = Hammerspace.new(path, options)
293
+ hash['foo'] = 'bar'
294
+ hash.close
295
+
296
+ hash = Hammerspace.new(path, options)
297
+ hash.clear
298
+ hash['foo'].should be_nil
299
+ hash.size.should == 0
300
+ hash.close
301
+ end
302
+
303
+ it "removes unflushed keys and values" do
304
+ hash = Hammerspace.new(path, options)
305
+ hash['foo'] = 'bar'
306
+ hash.clear
307
+ hash['foo'].should be_nil
308
+ hash.size.should == 0
309
+ hash.close
310
+ end
311
+
312
+ it "returns the hash" do
313
+ hash = Hammerspace.new(path, options)
314
+ hash.clear.should == hash
315
+ hash.close
316
+ end
317
+
318
+ end
319
+
320
+ describe "#delete" do
321
+
322
+ it "deletes" do
323
+ hash = Hammerspace.new(path, options)
324
+ hash['foo'] = 'bar'
325
+ hash.delete('foo')
326
+ hash.has_key?('foo').should be_false
327
+ hash['foo'].should be_nil
328
+ hash.close
329
+ end
330
+
331
+ end
332
+
333
+ describe "#delete_if" do
334
+
335
+ let(:hash) do
336
+ h = Hammerspace.new(path, options)
337
+ h['a'] = 'A'
338
+ h['b'] = 'B'
339
+ h
340
+ end
341
+
342
+ context "with block" do
343
+
344
+ it "deletes when true" do
345
+ hash.delete_if { |key,value| key == 'a' }
346
+ hash.has_key?('a').should be_false
347
+ hash['a'].should be_nil
348
+ hash.has_key?('b').should be_true
349
+ hash['b'].should == 'B'
350
+ hash.close
351
+ end
352
+
353
+ it "returns the hash" do
354
+ hash.delete_if { |key,value| true }.should == hash
355
+ hash.close
356
+ end
357
+
358
+ end
359
+
360
+ context "with enumerator" do
361
+
362
+ it "deletes when true" do
363
+ hash.delete_if.each { |key,value| key == 'a' }
364
+ hash.has_key?('a').should be_false
365
+ hash['a'].should be_nil
366
+ hash.has_key?('b').should be_true
367
+ hash['b'].should == 'B'
368
+ hash.close
369
+ end
370
+
371
+ end
372
+
373
+ end
374
+
375
+ describe "#each" do
376
+
377
+ let(:keys) { [] }
378
+ let(:values) { [] }
379
+ let(:hash) do
380
+ h = Hammerspace.new(path, options)
381
+ h['a'] = 'A'
382
+ h['b'] = 'B'
383
+ h
384
+ end
385
+
386
+ context "with block" do
387
+
388
+ it "allows iteration" do
389
+ hash.each do |key,value|
390
+ keys << key
391
+ values << value
392
+ end
393
+ hash.close
394
+
395
+ keys.should == ['a', 'b']
396
+ values.should == ['A', 'B']
397
+ end
398
+
399
+ it "allows iteration when empty" do
400
+ iterations = 0
401
+
402
+ hash = Hammerspace.new(path, options)
403
+ hash.each { |key,value| iterations += 1 }
404
+ hash.close
405
+
406
+ iterations.should == 0
407
+ end
408
+
409
+ it "allows updating during iteration" do
410
+ hash.each do |key,value|
411
+ keys << key
412
+ values << value
413
+ hash[key] = 'C'
414
+ end
415
+
416
+ keys.should == ['a', 'b']
417
+ values.should == ['A', 'B']
418
+
419
+ hash['a'].should == 'C'
420
+ hash['b'].should == 'C'
421
+
422
+ hash.close
423
+ end
424
+
425
+ it "allows updating and reading during iteration" do
426
+ hash.each do |key,value|
427
+ keys << key
428
+ values << value
429
+ hash[key] = 'C'
430
+ hash[key].should == 'C'
431
+ end
432
+
433
+ keys.should == ['a', 'b']
434
+ values.should == ['A', 'B']
435
+
436
+ hash['a'].should == 'C'
437
+ hash['b'].should == 'C'
438
+
439
+ hash.close
440
+ end
441
+
442
+ it "isolates iterators during iteration" do
443
+ hash.each do |key,value|
444
+ hash['b'] = 'C'
445
+ keys << key
446
+ values << value
447
+ end
448
+
449
+ keys.should == ['a', 'b']
450
+ values.should == ['A', 'B']
451
+
452
+ hash['b'].should == 'C'
453
+
454
+ hash.close
455
+ end
456
+
457
+ end
458
+
459
+ context "with enumerator" do
460
+
461
+ it "allows iteration" do
462
+ hash.each.each do |key,value|
463
+ keys << key
464
+ values << value
465
+ end
466
+ hash.close
467
+
468
+ keys.should == ['a', 'b']
469
+ values.should == ['A', 'B']
470
+ end
471
+
472
+ it "allows iteration when empty" do
473
+ iterations = 0
474
+
475
+ hash = Hammerspace.new(path, options)
476
+ hash.each.each { |key,value| iterations += 1 }
477
+ hash.close
478
+
479
+ iterations.should == 0
480
+ end
481
+
482
+ it "allows updating during iteration" do
483
+ hash.each.each do |key,value|
484
+ keys << key
485
+ values << value
486
+ hash[key] = 'C'
487
+ end
488
+
489
+ keys.should == ['a', 'b']
490
+ values.should == ['A', 'B']
491
+
492
+ hash['a'].should == 'C'
493
+ hash['b'].should == 'C'
494
+
495
+ hash.close
496
+ end
497
+
498
+ it "allows updating and reading during iteration" do
499
+ hash.each.each do |key,value|
500
+ keys << key
501
+ values << value
502
+ hash[key] = 'C'
503
+ hash[key].should == 'C'
504
+ end
505
+
506
+ keys.should == ['a', 'b']
507
+ values.should == ['A', 'B']
508
+
509
+ hash['a'].should == 'C'
510
+ hash['b'].should == 'C'
511
+
512
+ hash.close
513
+ end
514
+
515
+ it "isolates iterators during iteration" do
516
+ hash.each.each do |key,value|
517
+ hash['b'] = 'C'
518
+ keys << key
519
+ values << value
520
+ end
521
+
522
+ keys.should == ['a', 'b']
523
+ values.should == ['A', 'B']
524
+
525
+ hash['b'].should == 'C'
526
+
527
+ hash.close
528
+ end
529
+
530
+ it "returns the hash" do
531
+ hash.each { |key,value| 'foo' }.should == hash
532
+ end
533
+
534
+ end
535
+
536
+ end
537
+
538
+ describe "#each_key" do
539
+
540
+ let(:keys) { [] }
541
+ let(:hash) do
542
+ h = Hammerspace.new(path, options)
543
+ h['a'] = 'A'
544
+ h['b'] = 'B'
545
+ h
546
+ end
547
+
548
+ context "with block" do
549
+
550
+ it "allows iteration" do
551
+ hash.each_key do |key|
552
+ keys << key
553
+ end
554
+ hash.close
555
+
556
+ keys.should == ['a', 'b']
557
+ end
558
+
559
+ it "allows iteration when empty" do
560
+ iterations = 0
561
+
562
+ hash = Hammerspace.new(path, options)
563
+ hash.each_key { |key| iterations += 1 }
564
+ hash.close
565
+
566
+ iterations.should == 0
567
+ end
568
+
569
+ it "allows updating during iteration" do
570
+ hash.each_key do |key|
571
+ keys << key
572
+ hash[key] = 'C'
573
+ end
574
+
575
+ keys.should == ['a', 'b']
576
+
577
+ hash['a'].should == 'C'
578
+ hash['b'].should == 'C'
579
+
580
+ hash.close
581
+ end
582
+
583
+ it "allows updating and reading during iteration" do
584
+ hash.each_key do |key|
585
+ keys << key
586
+ hash[key] = 'C'
587
+ hash[key].should == 'C'
588
+ end
589
+
590
+ keys.should == ['a', 'b']
591
+
592
+ hash['a'].should == 'C'
593
+ hash['b'].should == 'C'
594
+
595
+ hash.close
596
+ end
597
+
598
+ it "isolates iterators during iteration" do
599
+ hash.each_key do |key|
600
+ hash['b'] = 'C'
601
+ keys << key
602
+ end
603
+
604
+ keys.should == ['a', 'b']
605
+
606
+ hash['b'].should == 'C'
607
+
608
+ hash.close
609
+ end
610
+
611
+ it "returns the hash" do
612
+ hash.each_key { |key| 'foo' }.should == hash
613
+ end
614
+
615
+ end
616
+
617
+ context "with enumerator" do
618
+
619
+ it "allows iteration" do
620
+ hash.each_key.each do |key|
621
+ keys << key
622
+ end
623
+ hash.close
624
+
625
+ keys.should == ['a', 'b']
626
+ end
627
+
628
+ it "allows iteration when empty" do
629
+ iterations = 0
630
+
631
+ hash = Hammerspace.new(path, options)
632
+ hash.each_key.each { |key,value| iterations += 1 }
633
+ hash.close
634
+
635
+ iterations.should == 0
636
+ end
637
+
638
+ it "allows updating during iteration" do
639
+ hash.each_key.each do |key|
640
+ keys << key
641
+ hash[key] = 'C'
642
+ end
643
+
644
+ keys.should == ['a', 'b']
645
+
646
+ hash['a'].should == 'C'
647
+ hash['b'].should == 'C'
648
+
649
+ hash.close
650
+ end
651
+
652
+ it "allows updating and reading during iteration" do
653
+ hash.each_key.each do |key|
654
+ keys << key
655
+ hash[key] = 'C'
656
+ hash[key].should == 'C'
657
+ end
658
+
659
+ keys.should == ['a', 'b']
660
+
661
+ hash['a'].should == 'C'
662
+ hash['b'].should == 'C'
663
+
664
+ hash.close
665
+ end
666
+
667
+ it "isolates iterators during iteration" do
668
+ hash.each_key.each do |key|
669
+ hash['b'] = 'C'
670
+ keys << key
671
+ end
672
+
673
+ keys.should == ['a', 'b']
674
+
675
+ hash['b'].should == 'C'
676
+
677
+ hash.close
678
+ end
679
+
680
+ end
681
+
682
+ end
683
+
684
+ describe "#each_value" do
685
+
686
+ let(:values) { [] }
687
+ let(:hash) do
688
+ h = Hammerspace.new(path, options)
689
+ h['a'] = 'A'
690
+ h['b'] = 'B'
691
+ h
692
+ end
693
+
694
+ context "with block" do
695
+
696
+ it "allows iteration" do
697
+ hash.each_value do |value|
698
+ values << value
699
+ end
700
+ hash.close
701
+
702
+ values.should == ['A', 'B']
703
+ end
704
+
705
+ it "allows iteration when empty" do
706
+ iterations = 0
707
+
708
+ hash = Hammerspace.new(path, options)
709
+ hash.each_value { |value| iterations += 1 }
710
+ hash.close
711
+
712
+ iterations.should == 0
713
+ end
714
+
715
+ it "allows updating during iteration" do
716
+ hash.each_value do |value|
717
+ values << value
718
+ hash['a'] = 'C'
719
+ end
720
+
721
+ values.should == ['A', 'B']
722
+
723
+ hash['a'].should == 'C'
724
+
725
+ hash.close
726
+ end
727
+
728
+ it "allows updating and reading during iteration" do
729
+ hash.each_value do |value|
730
+ values << value
731
+ hash['a'] = 'C'
732
+ hash['a'].should == 'C'
733
+ end
734
+
735
+ values.should == ['A', 'B']
736
+
737
+ hash['a'].should == 'C'
738
+
739
+ hash.close
740
+ end
741
+
742
+ it "isolates iterators during iteration" do
743
+ hash.each_value do |value|
744
+ hash['b'] = 'C'
745
+ values << value
746
+ end
747
+
748
+ values.should == ['A', 'B']
749
+
750
+ hash['b'].should == 'C'
751
+
752
+ hash.close
753
+ end
754
+
755
+ it "returns the hash" do
756
+ hash.each_value { |value| 'foo' }.should == hash
757
+ end
758
+
759
+ end
760
+
761
+ context "with enumerator" do
762
+
763
+ it "allows iteration" do
764
+ hash.each_value.each do |value|
765
+ values << value
766
+ end
767
+ hash.close
768
+
769
+ values.should == ['A', 'B']
770
+ end
771
+
772
+ it "allows iteration when empty" do
773
+ iterations = 0
774
+
775
+ hash = Hammerspace.new(path, options)
776
+ hash.each_value.each { |value| iterations += 1 }
777
+ hash.close
778
+
779
+ iterations.should == 0
780
+ end
781
+
782
+ it "allows updating during iteration" do
783
+ hash.each_value.each do |value|
784
+ values << value
785
+ hash['a'] = 'C'
786
+ end
787
+
788
+ values.should == ['A', 'B']
789
+
790
+ hash['a'].should == 'C'
791
+
792
+ hash.close
793
+ end
794
+
795
+ it "allows updating and reading during iteration" do
796
+ hash.each_value.each do |value|
797
+ values << value
798
+ hash['a'] = 'C'
799
+ hash['a'].should == 'C'
800
+ end
801
+
802
+ values.should == ['A', 'B']
803
+
804
+ hash['a'].should == 'C'
805
+
806
+ hash.close
807
+ end
808
+
809
+ it "isolates iterators during iteration" do
810
+ hash.each_value.each do |value|
811
+ hash['b'] = 'C'
812
+ values << value
813
+ end
814
+
815
+ values.should == ['A', 'B']
816
+
817
+ hash['b'].should == 'C'
818
+
819
+ hash.close
820
+ end
821
+
822
+ end
823
+
824
+ end
825
+
826
+ describe "#empty?" do
827
+
828
+ it "returns true when empty" do
829
+ hash = Hammerspace.new(path, options)
830
+ hash.empty?.should be_true
831
+ hash.close
832
+ end
833
+
834
+ it "returns false when not empty" do
835
+ hash = Hammerspace.new(path, options)
836
+ hash['foo'] = 'bar'
837
+ hash.empty?.should be_false
838
+ hash.close
839
+ end
840
+
841
+ end
842
+
843
+ describe "#eql?" do
844
+
845
+ it "returns false if different sizes" do
846
+ h1 = Hammerspace.new(File.join(path, '1'), options)
847
+ h1['a'] = 'A'
848
+ h1['b'] = 'B'
849
+
850
+ h2 = Hammerspace.new(File.join(path, '2'), options)
851
+ h2['a'] = 'A'
852
+
853
+ h1.should_not eql(h2)
854
+
855
+ h1.close
856
+ h2.close
857
+ end
858
+
859
+ it "does not consider default values" do
860
+ h1 = Hammerspace.new(File.join(path, '1'), options)
861
+ h1['a'] = 'A'
862
+ h1['b'] = 'B'
863
+
864
+ h2 = Hammerspace.new(File.join(path, '2'), options, 'B')
865
+ h2['a'] = 'A'
866
+
867
+ h1.should_not eql(h2)
868
+
869
+ h1.close
870
+ h2.close
871
+ end
872
+
873
+ it "returns false if different keys" do
874
+ h1 = Hammerspace.new(File.join(path, '1'), options)
875
+ h1['a'] = 'A'
876
+ h1['b'] = 'B'
877
+
878
+ h2 = Hammerspace.new(File.join(path, '2'), options)
879
+ h2['a'] = 'A'
880
+ h2['B'] = 'B'
881
+
882
+ h1.should_not eql(h2)
883
+
884
+ h1.close
885
+ h2.close
886
+ end
887
+
888
+ it "returns false if different values" do
889
+ h1 = Hammerspace.new(File.join(path, '1'), options)
890
+ h1['a'] = 'A'
891
+ h1['b'] = 'B'
892
+
893
+ h2 = Hammerspace.new(File.join(path, '2'), options)
894
+ h2['a'] = 'A'
895
+ h2['b'] = 'b'
896
+
897
+ h1.should_not eql(h2)
898
+
899
+ h1.close
900
+ h2.close
901
+ end
902
+
903
+ it "returns true if same keys and values" do
904
+ h1 = Hammerspace.new(File.join(path, '1'), options)
905
+ h1['a'] = 'A'
906
+ h1['b'] = 'B'
907
+
908
+ h2 = Hammerspace.new(File.join(path, '2'), options)
909
+ h2['a'] = 'A'
910
+ h2['b'] = 'B'
911
+
912
+ h1.should eql(h2)
913
+
914
+ h1.close
915
+ h2.close
916
+ end
917
+
918
+ it "works with hashes" do
919
+ hash = Hammerspace.new(File.join(path, '1'), options)
920
+ hash['a'] = 'A'
921
+ hash['b'] = 'B'
922
+ hash.should eql({'a' => 'A', 'b' => 'B'})
923
+ hash.close
924
+ end
925
+
926
+ end
927
+
928
+ describe "#fetch" do
929
+
930
+ it "returns value if key exists" do
931
+ hash = Hammerspace.new(path, options)
932
+ hash['foo'] = 'bar'
933
+ hash.fetch('foo').should == 'bar'
934
+ end
935
+
936
+ it "calls block to determine value if key does not exist" do
937
+ hash = Hammerspace.new(path, options)
938
+ hash.fetch('foo') { |key| "block#{key}" }.should == "blockfoo"
939
+ hash.close
940
+ end
941
+
942
+ it "returns default value if key does not exist" do
943
+ hash = Hammerspace.new(path, options)
944
+ hash.fetch('foo', 'default').should == 'default'
945
+ hash.close
946
+ end
947
+
948
+ it "calls block to determine value if key does not exist and both second argument and block are passed" do
949
+ hash = Hammerspace.new(path, options)
950
+ hash.fetch('foo', 'default') { |key| "block#{key}" }.should == "blockfoo"
951
+ hash.close
952
+ end
953
+
954
+ it "raises KeyError if key does not exist" do
955
+ hash = Hammerspace.new(path, options)
956
+ expect {
957
+ hash.fetch('foo')
958
+ }.to raise_error(KeyError)
959
+ hash.close
960
+ end
961
+
962
+ it "raises ArgumentError if a third argument is passed" do
963
+ hash = Hammerspace.new(path, options)
964
+ expect {
965
+ hash.fetch('foo', 'default', 'bogus')
966
+ }.to raise_error(ArgumentError)
967
+ hash.close
968
+ end
969
+
970
+ end
971
+
972
+ describe "#flatten" do
973
+
974
+ it "returns an array of key value pairs" do
975
+ hash = Hammerspace.new(path, options)
976
+ hash['a'] = 'A'
977
+ hash['b'] = 'B'
978
+ hash.flatten.should == ['a', 'A', 'b', 'B']
979
+ hash.close
980
+ end
981
+
982
+ it "returns an empty array when empty" do
983
+ hash = Hammerspace.new(path, options)
984
+ hash.flatten.should == []
985
+ hash.close
986
+ end
987
+
988
+ it "accepts an optional level argument" do
989
+ hash = Hammerspace.new(path, options)
990
+ hash['a'] = 'A'
991
+ hash['b'] = 'B'
992
+ hash.flatten(2).should == ['a', 'A', 'b', 'B']
993
+ hash.close
994
+ end
995
+
996
+ it "raises ArgumentError if a second argument is passed" do
997
+ hash = Hammerspace.new(path, options)
998
+ expect {
999
+ hash.flatten(1, 'bogus')
1000
+ }.to raise_error(ArgumentError)
1001
+ hash.close
1002
+ end
1003
+
1004
+ end
1005
+
1006
+ describe "#has_key?" do
1007
+
1008
+ it "returns true when key is present" do
1009
+ hash = Hammerspace.new(path, options)
1010
+ hash['foo'] = 'bar'
1011
+ hash.has_key?('foo').should be_true
1012
+ hash.close
1013
+ end
1014
+
1015
+ it "returns false when key is not present" do
1016
+ hash = Hammerspace.new(path, options)
1017
+ hash['foo'] = 'bar'
1018
+ hash.has_key?('otherkey').should be_false
1019
+ hash.close
1020
+ end
1021
+
1022
+ it "returns false when empty" do
1023
+ hash = Hammerspace.new(path, options)
1024
+ hash.has_key?('foo').should be_false
1025
+ hash.close
1026
+ end
1027
+
1028
+ end
1029
+
1030
+ describe "#has_value?" do
1031
+
1032
+ it "returns true when value is present" do
1033
+ hash = Hammerspace.new(path, options)
1034
+ hash['foo'] = 'bar'
1035
+ hash.has_value?('bar').should be_true
1036
+ hash.close
1037
+ end
1038
+
1039
+ it "returns false when value is not present" do
1040
+ hash = Hammerspace.new(path, options)
1041
+ hash['foo'] = 'bar'
1042
+ hash.has_value?('othervalue').should be_false
1043
+ hash.close
1044
+ end
1045
+
1046
+ it "returns false when empty" do
1047
+ hash = Hammerspace.new(path, options)
1048
+ hash.has_value?('foo').should be_false
1049
+ hash.close
1050
+ end
1051
+
1052
+ end
1053
+
1054
+ describe "#keep_if" do
1055
+
1056
+ let(:hash) do
1057
+ h = Hammerspace.new(path, options)
1058
+ h['a'] = 'A'
1059
+ h['b'] = 'B'
1060
+ h
1061
+ end
1062
+
1063
+ context "with block" do
1064
+
1065
+ it "keeps when true" do
1066
+ hash.keep_if { |key,value| key == 'b' }
1067
+ hash.has_key?('a').should be_false
1068
+ hash['a'].should be_nil
1069
+ hash.has_key?('b').should be_true
1070
+ hash['b'].should == 'B'
1071
+ hash.close
1072
+ end
1073
+
1074
+ it "returns the hash" do
1075
+ hash.keep_if { |key,value| true }.should == hash
1076
+ hash.close
1077
+ end
1078
+
1079
+ end
1080
+
1081
+ context "with enumerator" do
1082
+
1083
+ it "keeps when true" do
1084
+ hash.keep_if.each { |key,value| key == 'b' }
1085
+ hash.has_key?('a').should be_false
1086
+ hash['a'].should be_nil
1087
+ hash.has_key?('b').should be_true
1088
+ hash['b'].should == 'B'
1089
+ hash.close
1090
+ end
1091
+
1092
+ end
1093
+
1094
+ end
1095
+
1096
+ describe "#key" do
1097
+
1098
+ it "returns value if key exists" do
1099
+ hash = Hammerspace.new(path, options)
1100
+ hash['foo'] = 'bar'
1101
+ hash.key('foo').should == 'bar'
1102
+ end
1103
+
1104
+ it "returns nil if key does not exist" do
1105
+ hash = Hammerspace.new(path, options)
1106
+ hash.key('foo').should be_nil
1107
+ end
1108
+
1109
+ end
1110
+
1111
+ describe "#keys" do
1112
+
1113
+ it "returns keys" do
1114
+ hash = Hammerspace.new(path, options)
1115
+ hash['a'] = 'A'
1116
+ hash['b'] = 'B'
1117
+ hash.keys.should == ['a', 'b']
1118
+ hash.close
1119
+ end
1120
+
1121
+ it "returns empty array when empty" do
1122
+ hash = Hammerspace.new(path, options)
1123
+ hash.keys.should == []
1124
+ hash.close
1125
+ end
1126
+
1127
+ end
1128
+
1129
+ describe "#merge!" do
1130
+
1131
+ it "adds new values" do
1132
+ hash = Hammerspace.new(path, options)
1133
+ hash.merge!({'foo' => 'bar'})
1134
+ hash['foo'].should == 'bar'
1135
+ hash.close
1136
+ end
1137
+
1138
+ it "updates existing values" do
1139
+ hash = Hammerspace.new(path, options)
1140
+ hash['foo'] = 'bar'
1141
+ hash.merge!({'foo' => 'newvalue'})
1142
+ hash['foo'].should == 'newvalue'
1143
+ hash.close
1144
+ end
1145
+
1146
+ it "calls block to determine value on duplicates" do
1147
+ hash = Hammerspace.new(path, options)
1148
+ hash['foo'] = 'bar'
1149
+ hash.merge!({'foo' => 'newvalue'}) { |key, v1, v2| v1 + v2 }
1150
+ hash['foo'].should == 'barnewvalue'
1151
+ hash.close
1152
+ end
1153
+
1154
+ it "returns the hash" do
1155
+ hash = Hammerspace.new(path, options)
1156
+ hash.merge!({}).should == hash
1157
+ hash.close
1158
+ end
1159
+
1160
+ end
1161
+
1162
+ describe "#rassoc" do
1163
+
1164
+ it "returns key value pair when value is present" do
1165
+ hash = Hammerspace.new(path, options)
1166
+ hash['foo'] = 'bar'
1167
+ hash.rassoc('bar').should == ['foo', 'bar']
1168
+ hash.close
1169
+ end
1170
+
1171
+ it "returns first key value pair when value is present multiple times" do
1172
+ hash = Hammerspace.new(path, options)
1173
+ hash['foo'] = 'bar'
1174
+ hash['otherkey'] = 'bar'
1175
+ hash.rassoc('bar').should == ['foo', 'bar']
1176
+ hash.close
1177
+ end
1178
+
1179
+ it "returns nil when value is not present" do
1180
+ hash = Hammerspace.new(path, options)
1181
+ hash['foo'] = 'bar'
1182
+ hash.rassoc('otherkey').should be_nil
1183
+ hash.close
1184
+ end
1185
+
1186
+ it "returns nil when empty" do
1187
+ hash = Hammerspace.new(path, options)
1188
+ hash.rassoc('foo').should be_nil
1189
+ hash.close
1190
+ end
1191
+
1192
+ end
1193
+
1194
+ describe "#reject!" do
1195
+
1196
+ let(:hash) do
1197
+ h = Hammerspace.new(path, options)
1198
+ h['a'] = 'A'
1199
+ h['b'] = 'B'
1200
+ h
1201
+ end
1202
+
1203
+ context "with block" do
1204
+
1205
+ it "deletes when true" do
1206
+ hash.reject! { |key,value| key == 'a' }
1207
+ hash.has_key?('a').should be_false
1208
+ hash['a'].should be_nil
1209
+ hash.has_key?('b').should be_true
1210
+ hash['b'].should == 'B'
1211
+ hash.close
1212
+ end
1213
+
1214
+ it "returns the hash if items deleted" do
1215
+ hash.reject! { |key,value| true }.should == hash
1216
+ hash.close
1217
+ end
1218
+
1219
+ it "returns nil if no items deleted" do
1220
+ hash.reject! { |key,value| false }.should be_nil
1221
+ hash.close
1222
+ end
1223
+
1224
+ end
1225
+
1226
+ context "with enumerator" do
1227
+
1228
+ it "deletes when true" do
1229
+ hash.reject!.each { |key,value| key == 'a' }
1230
+ hash.has_key?('a').should be_false
1231
+ hash['a'].should be_nil
1232
+ hash.has_key?('b').should be_true
1233
+ hash['b'].should == 'B'
1234
+ hash.close
1235
+ end
1236
+
1237
+ end
1238
+
1239
+ end
1240
+
1241
+ describe "#replace" do
1242
+
1243
+ it "removes values" do
1244
+ hash = Hammerspace.new(path, options)
1245
+ hash['a'] = 'A'
1246
+ hash.close
1247
+
1248
+ hash.replace({'b' => 'B'})
1249
+ hash['a'].should be_nil
1250
+ hash['b'].should == 'B'
1251
+ hash.close
1252
+ end
1253
+
1254
+ it "updates existing values" do
1255
+ hash = Hammerspace.new(path, options)
1256
+ hash['foo'] = 'bar'
1257
+ hash.close
1258
+
1259
+ hash.replace({'foo' => 'newvalue'})
1260
+ hash['foo'].should == 'newvalue'
1261
+ hash.close
1262
+ end
1263
+
1264
+ it "returns the hash" do
1265
+ hash = Hammerspace.new(path, options)
1266
+ hash.replace({}).should == hash
1267
+ hash.close
1268
+ end
1269
+
1270
+ end
1271
+
1272
+ describe "#select!" do
1273
+
1274
+ let(:hash) do
1275
+ h = Hammerspace.new(path, options)
1276
+ h['a'] = 'A'
1277
+ h['b'] = 'B'
1278
+ h
1279
+ end
1280
+
1281
+ context "with block" do
1282
+
1283
+ it "keeps when true" do
1284
+ hash.select! { |key,value| key == 'b' }
1285
+ hash.has_key?('a').should be_false
1286
+ hash['a'].should be_nil
1287
+ hash.has_key?('b').should be_true
1288
+ hash['b'].should == 'B'
1289
+ hash.close
1290
+ end
1291
+
1292
+ it "returns the hash if items deleted" do
1293
+ hash.select! { |key,value| false }.should == hash
1294
+ hash.close
1295
+ end
1296
+
1297
+ it "returns nil if no items deleted" do
1298
+ hash.select! { |key,value| true }.should be_nil
1299
+ hash.close
1300
+ end
1301
+
1302
+ end
1303
+
1304
+ context "with enumerator" do
1305
+
1306
+ it "keeps when true" do
1307
+ hash.select!.each { |key,value| key == 'b' }
1308
+ hash.has_key?('a').should be_false
1309
+ hash['a'].should be_nil
1310
+ hash.has_key?('b').should be_true
1311
+ hash['b'].should == 'B'
1312
+ hash.close
1313
+ end
1314
+
1315
+ end
1316
+
1317
+ end
1318
+
1319
+ describe "#shift" do
1320
+
1321
+ it "removes and returns the first key value pair" do
1322
+ hash = Hammerspace.new(path, options)
1323
+ hash['a'] = 'A'
1324
+ hash['b'] = 'B'
1325
+ hash.shift.should == ['a', 'A']
1326
+ hash.keys.should == ['b']
1327
+ hash.values.should == ['B']
1328
+ hash.close
1329
+ end
1330
+
1331
+ it "returns the default value if empty" do
1332
+ hash = Hammerspace.new(path, options, 'default')
1333
+ hash.shift.should == 'default'
1334
+ hash.close
1335
+ end
1336
+
1337
+ end
1338
+
1339
+ describe "#size" do
1340
+
1341
+ it "returns size" do
1342
+ hash = Hammerspace.new(path, options)
1343
+ hash['a'] = 'A'
1344
+ hash['b'] = 'B'
1345
+ hash.size.should == 2
1346
+ hash.close
1347
+ end
1348
+
1349
+ it "returns 0 when empty" do
1350
+ hash = Hammerspace.new(path, options)
1351
+ hash.size.should == 0
1352
+ hash.close
1353
+ end
1354
+
1355
+ end
1356
+
1357
+ describe "#to_a" do
1358
+
1359
+ it "returns an array of key value pairs" do
1360
+ hash = Hammerspace.new(path, options)
1361
+ hash['a'] = 'A'
1362
+ hash['b'] = 'B'
1363
+ hash.to_a.should == [['a', 'A'], ['b', 'B']]
1364
+ hash.close
1365
+ end
1366
+
1367
+ it "returns an empty array when empty" do
1368
+ hash = Hammerspace.new(path, options)
1369
+ hash.to_a.should == []
1370
+ hash.close
1371
+ end
1372
+
1373
+ end
1374
+
1375
+ describe "#to_hash" do
1376
+
1377
+ it "returns a hash" do
1378
+ hash = Hammerspace.new(path, options)
1379
+ hash['a'] = 'A'
1380
+ hash['b'] = 'B'
1381
+ hash.to_hash.should == {'a' => 'A', 'b' => 'B'}
1382
+ hash.close
1383
+ end
1384
+
1385
+ it "returns an empty hash when empty" do
1386
+ hash = Hammerspace.new(path, options)
1387
+ hash.to_hash.should == {}
1388
+ hash.close
1389
+ end
1390
+
1391
+ end
1392
+
1393
+ describe "#uid" do
1394
+
1395
+ it "returns uid" do
1396
+ hash = Hammerspace.new(path, options)
1397
+ hash['foo'] = 'bar'
1398
+ hash.close
1399
+
1400
+ hash = Hammerspace.new(path, options)
1401
+ hash.uid.should_not be_nil
1402
+ hash.close
1403
+ end
1404
+
1405
+ it "returns nil when empty" do
1406
+ hash = Hammerspace.new(path, options)
1407
+ hash.uid.should be_nil
1408
+ hash.close
1409
+ end
1410
+
1411
+ it "returns same uid throughout isolated read" do
1412
+ writer = Hammerspace.new(path, options)
1413
+ writer['foo'] = 'bar'
1414
+ writer.close
1415
+
1416
+ reader = Hammerspace.new(path, options)
1417
+ uid = reader.uid
1418
+
1419
+ writer = Hammerspace.new(path, options)
1420
+ writer['foo'] = 'newvalue'
1421
+ writer.close
1422
+
1423
+ reader.uid.should == uid
1424
+ reader.close
1425
+ end
1426
+
1427
+ it "returns different uid after close/reopen" do
1428
+ writer = Hammerspace.new(path, options)
1429
+ writer['foo'] = 'bar'
1430
+ writer.close
1431
+
1432
+ reader = Hammerspace.new(path, options)
1433
+ uid = reader.uid
1434
+
1435
+ writer = Hammerspace.new(path, options)
1436
+ writer['foo'] = 'newvalue'
1437
+ writer.close
1438
+
1439
+ reader.uid.should == uid
1440
+ reader.close
1441
+ reader.uid.should_not == uid
1442
+ reader.close
1443
+ end
1444
+
1445
+ end
1446
+
1447
+ describe "#values" do
1448
+
1449
+ it "returns values" do
1450
+ hash = Hammerspace.new(path, options)
1451
+ hash['a'] = 'A'
1452
+ hash['b'] = 'B'
1453
+ hash.values.should == ['A', 'B']
1454
+ hash.close
1455
+ end
1456
+
1457
+ it "returns empty array when empty" do
1458
+ hash = Hammerspace.new(path, options)
1459
+ hash.values.should == []
1460
+ hash.close
1461
+ end
1462
+
1463
+ end
1464
+
1465
+ describe "#values_at" do
1466
+
1467
+ it "returns values" do
1468
+ hash = Hammerspace.new(path, options)
1469
+ hash['a'] = 'A'
1470
+ hash['b'] = 'B'
1471
+ hash.values_at('b', 'a').should == ['B', 'A']
1472
+ hash.close
1473
+ end
1474
+
1475
+ it "returns default values when keys do not exist" do
1476
+ hash = Hammerspace.new(path, options)
1477
+ hash.default = 'default'
1478
+ hash['a'] = 'A'
1479
+ hash.values_at('a', 'b').should == ['A', 'default']
1480
+ hash.close
1481
+ end
1482
+
1483
+ end
1484
+
1485
+ end
1486
+ end
1487
+ end