hammerspace 0.1.2

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.
@@ -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.2'
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