fake_dynamo 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +14 -2
- data/lib/fake_dynamo/key.rb +5 -0
- data/lib/fake_dynamo/server.rb +2 -0
- data/lib/fake_dynamo/table.rb +17 -12
- data/lib/fake_dynamo/version.rb +1 -1
- data/spec/fake_dynamo/table_spec.rb +47 -11
- metadata +8 -8
data/README.md
CHANGED
@@ -6,10 +6,14 @@ local hosted, inmemory dynamodb emulator.
|
|
6
6
|
# Caveats
|
7
7
|
|
8
8
|
* `ConsumedCapacityUnits` value will be 1 always.
|
9
|
-
* The response size is not constrained by 1mb limit. So operation
|
9
|
+
* The response size is not constrained by 1mb limit. So operation
|
10
|
+
like `BatchGetItem` will return all items irrespective of the
|
11
|
+
response size
|
10
12
|
|
11
13
|
# Usage
|
12
14
|
|
15
|
+
requires ruby >= 1.9
|
16
|
+
|
13
17
|
````
|
14
18
|
gem install fake_dynamo
|
15
19
|
|
@@ -28,5 +32,13 @@ AWS.config(:use_ssl => false,
|
|
28
32
|
:secret_access_key => "xxx")
|
29
33
|
````
|
30
34
|
|
35
|
+
__please open a pull request with your configuration if you are using
|
36
|
+
fake_dynamo with clients other than the ones mentioned above__.
|
37
|
+
|
31
38
|
# Storage
|
32
|
-
fake_dynamo stores the `write operations` (request that changes the
|
39
|
+
fake_dynamo stores the `write operations` (request that changes the
|
40
|
+
data) in `/usr/local/var/fake_dynamo/db.fdb` and replays it before
|
41
|
+
starting the server. Because of the way fake_dynamo stores the data,
|
42
|
+
file size tend to grow by time. so fake_dynamo will compact the database
|
43
|
+
during start up if the file size is greater than 100mb. you can
|
44
|
+
manually compact it by passing --compact flag.
|
data/lib/fake_dynamo/key.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module FakeDynamo
|
2
2
|
class Key
|
3
|
+
include Comparable
|
3
4
|
extend Validation
|
4
5
|
|
5
6
|
attr_accessor :primary, :range
|
@@ -67,5 +68,9 @@ module FakeDynamo
|
|
67
68
|
result
|
68
69
|
end
|
69
70
|
|
71
|
+
def <=>(other)
|
72
|
+
[primary, range] <=> [other.primary, other.range]
|
73
|
+
end
|
74
|
+
|
70
75
|
end
|
71
76
|
end
|
data/lib/fake_dynamo/server.rb
CHANGED
data/lib/fake_dynamo/table.rb
CHANGED
@@ -200,16 +200,8 @@ module FakeDynamo
|
|
200
200
|
hash_attribute = Attribute.from_hash(key_schema.hash_key.name, data['HashKeyValue'])
|
201
201
|
matched_items = get_items_by_hash_key(hash_attribute)
|
202
202
|
|
203
|
-
|
204
203
|
forward = data.has_key?('ScanIndexForward') ? data['ScanIndexForward'] : true
|
205
|
-
|
206
|
-
if forward
|
207
|
-
matched_items.sort! { |a, b| a.key.range <=> b.key.range }
|
208
|
-
else
|
209
|
-
matched_items.sort! { |a, b| b.key.range <=> a.key.range }
|
210
|
-
end
|
211
|
-
|
212
|
-
matched_items = drop_till_start(matched_items, data['ExclusiveStartKey'])
|
204
|
+
matched_items = drop_till_start(matched_items, data['ExclusiveStartKey'], forward)
|
213
205
|
|
214
206
|
if data['RangeKeyCondition']
|
215
207
|
conditions = {key_schema.range_key.name => data['RangeKeyCondition']}
|
@@ -237,7 +229,7 @@ module FakeDynamo
|
|
237
229
|
count_and_attributes_to_get_present?(data)
|
238
230
|
validate_limit(data)
|
239
231
|
conditions = data['ScanFilter'] || {}
|
240
|
-
all_items = drop_till_start(items.values, data['ExclusiveStartKey'])
|
232
|
+
all_items = drop_till_start(items.values, data['ExclusiveStartKey'], true)
|
241
233
|
result, last_evaluated_item, scaned_count = filter(all_items, conditions, data['Limit'], false)
|
242
234
|
response = {
|
243
235
|
'Count' => result.size,
|
@@ -267,9 +259,22 @@ module FakeDynamo
|
|
267
259
|
end
|
268
260
|
end
|
269
261
|
|
270
|
-
def drop_till_start(all_items, start_key_hash)
|
262
|
+
def drop_till_start(all_items, start_key_hash, forward)
|
263
|
+
all_items = all_items.sort_by { |item| item.key }
|
264
|
+
|
265
|
+
unless forward
|
266
|
+
all_items = all_items.reverse
|
267
|
+
end
|
268
|
+
|
271
269
|
if start_key_hash
|
272
|
-
|
270
|
+
start_key = Key.from_data(start_key_hash, key_schema)
|
271
|
+
all_items.drop_while do |item|
|
272
|
+
if forward
|
273
|
+
item.key <= start_key
|
274
|
+
else
|
275
|
+
item.key >= start_key
|
276
|
+
end
|
277
|
+
end
|
273
278
|
else
|
274
279
|
all_items
|
275
280
|
end
|
data/lib/fake_dynamo/version.rb
CHANGED
@@ -373,7 +373,8 @@ module FakeDynamo
|
|
373
373
|
t = Table.new(data)
|
374
374
|
t.put_item(item)
|
375
375
|
(1..3).each do |i|
|
376
|
-
(1
|
376
|
+
(15.downto(1)).each do |j|
|
377
|
+
next if j.even?
|
377
378
|
item['Item']['AttributeName1']['S'] = "att#{i}"
|
378
379
|
item['Item']['AttributeName2']['N'] = j.to_s
|
379
380
|
t.put_item(item)
|
@@ -422,14 +423,32 @@ module FakeDynamo
|
|
422
423
|
|
423
424
|
it 'should handle scanindexforward' do
|
424
425
|
result = subject.query(query)
|
425
|
-
result['Items'].first['AttributeName2'].should eq({'N' => '
|
426
|
+
result['Items'].first['AttributeName2'].should eq({'N' => '3'})
|
426
427
|
result = subject.query(query.merge({'ScanIndexForward' => false}))
|
427
|
-
result['Items'].first['AttributeName2'].should eq({'N' => '
|
428
|
+
result['Items'].first['AttributeName2'].should eq({'N' => '15'})
|
429
|
+
|
430
|
+
query['ExclusiveStartKey'] = { 'HashKeyElement' => { 'S' => 'att1' }, 'RangeKeyElement' => { "N" => '7' }}
|
431
|
+
result = subject.query(query)
|
432
|
+
result['Items'][0]['AttributeName1'].should eq({'S' => 'att1'})
|
433
|
+
result['Items'][0]['AttributeName2'].should eq({'N' => '9'})
|
434
|
+
|
435
|
+
result = subject.query(query.merge({'ScanIndexForward' => false}))
|
436
|
+
result['Items'][0]['AttributeName1'].should eq({'S' => 'att1'})
|
437
|
+
result['Items'][0]['AttributeName2'].should eq({'N' => '5'})
|
438
|
+
|
439
|
+
query['ExclusiveStartKey'] = { 'HashKeyElement' => { 'S' => 'att1' }, 'RangeKeyElement' => { "N" => '8' }}
|
440
|
+
result = subject.query(query)
|
441
|
+
result['Items'][0]['AttributeName1'].should eq({'S' => 'att1'})
|
442
|
+
result['Items'][0]['AttributeName2'].should eq({'N' => '9'})
|
443
|
+
|
444
|
+
result = subject.query(query.merge({'ScanIndexForward' => false}))
|
445
|
+
result['Items'][0]['AttributeName1'].should eq({'S' => 'att1'})
|
446
|
+
result['Items'][0]['AttributeName2'].should eq({'N' => '7'})
|
428
447
|
end
|
429
448
|
|
430
449
|
it 'should return lastevaluated key' do
|
431
450
|
result = subject.query(query)
|
432
|
-
result['LastEvaluatedKey'].should == {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"
|
451
|
+
result['LastEvaluatedKey'].should == {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"11"}}
|
433
452
|
result = subject.query(query.merge('Limit' => 100))
|
434
453
|
result['LastEvaluatedKey'].should be_nil
|
435
454
|
|
@@ -439,14 +458,18 @@ module FakeDynamo
|
|
439
458
|
end
|
440
459
|
|
441
460
|
it 'should handle exclusive start key' do
|
442
|
-
result = subject.query(query.merge({'ExclusiveStartKey' => {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"
|
461
|
+
result = subject.query(query.merge({'ExclusiveStartKey' => {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"7"}}}))
|
443
462
|
result['Count'].should eq(4)
|
444
|
-
result['Items'].first['AttributeName2'].should eq({'N' => '
|
463
|
+
result['Items'].first['AttributeName2'].should eq({'N' => '9'})
|
464
|
+
result = subject.query(query.merge({'ExclusiveStartKey' => {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"8"}}}))
|
465
|
+
result['Count'].should eq(4)
|
466
|
+
result['Items'].first['AttributeName2'].should eq({'N' => '9'})
|
445
467
|
result = subject.query(query.merge({'ExclusiveStartKey' => {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"88"}}}))
|
446
468
|
result['Count'].should eq(0)
|
447
469
|
result['Items'].should be_empty
|
448
470
|
end
|
449
471
|
|
472
|
+
|
450
473
|
it 'should return all elements if not rangekeycondition is given' do
|
451
474
|
query.delete('RangeKeyCondition')
|
452
475
|
result = subject.query(query)
|
@@ -455,7 +478,7 @@ module FakeDynamo
|
|
455
478
|
|
456
479
|
it 'should handle between operator' do
|
457
480
|
query['RangeKeyCondition'] = {
|
458
|
-
'AttributeValueList' => [{'N' => '1'}, {'N' => '
|
481
|
+
'AttributeValueList' => [{'N' => '1'}, {'N' => '7'}],
|
459
482
|
'ComparisonOperator' => 'BETWEEN'
|
460
483
|
}
|
461
484
|
result = subject.query(query)
|
@@ -466,7 +489,7 @@ module FakeDynamo
|
|
466
489
|
query['AttributesToGet'] = ['AttributeName1', "AttributeName2"]
|
467
490
|
result = subject.query(query)
|
468
491
|
result['Items'].first.should eq('AttributeName1' => { 'S' => 'att1'},
|
469
|
-
'AttributeName2' => { 'N' => '
|
492
|
+
'AttributeName2' => { 'N' => '3' })
|
470
493
|
end
|
471
494
|
end
|
472
495
|
|
@@ -474,7 +497,8 @@ module FakeDynamo
|
|
474
497
|
subject do
|
475
498
|
t = Table.new(data)
|
476
499
|
(1..3).each do |i|
|
477
|
-
(1
|
500
|
+
(15.downto(1)).each do |j|
|
501
|
+
next if j.even?
|
478
502
|
item['Item']['AttributeName1']['S'] = "att#{i}"
|
479
503
|
item['Item']['AttributeName2']['N'] = j.to_s
|
480
504
|
t.put_item(item)
|
@@ -509,7 +533,7 @@ module FakeDynamo
|
|
509
533
|
|
510
534
|
it 'should handle basic scan' do
|
511
535
|
result = subject.scan(scan)
|
512
|
-
result['Count'].should eq(
|
536
|
+
result['Count'].should eq(24)
|
513
537
|
|
514
538
|
scan['ScanFilter']['AttributeName2']['ComparisonOperator'] = 'EQ'
|
515
539
|
subject.scan(scan)['Count'].should eq(3)
|
@@ -518,7 +542,7 @@ module FakeDynamo
|
|
518
542
|
it 'should return lastevaluated key' do
|
519
543
|
scan['Limit'] = 5
|
520
544
|
result = subject.scan(scan)
|
521
|
-
result['LastEvaluatedKey'].should == {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"
|
545
|
+
result['LastEvaluatedKey'].should == {"HashKeyElement"=>{"S"=>"att1"}, "RangeKeyElement"=>{"N"=>"9"}}
|
522
546
|
result = subject.scan(scan.merge('Limit' => 100))
|
523
547
|
result['LastEvaluatedKey'].should be_nil
|
524
548
|
|
@@ -526,6 +550,18 @@ module FakeDynamo
|
|
526
550
|
result = subject.scan(scan)
|
527
551
|
result['LastEvaluatedKey'].should be_nil
|
528
552
|
end
|
553
|
+
|
554
|
+
it 'should handle ordering' do
|
555
|
+
scan['ExclusiveStartKey'] = { 'HashKeyElement' => { 'S' => 'att2' }, 'RangeKeyElement' => { "N" => '7' }}
|
556
|
+
result = subject.scan(scan)
|
557
|
+
result['Items'][0]['AttributeName1'].should eq({'S' => 'att2'})
|
558
|
+
result['Items'][0]['AttributeName2'].should eq({'N' => '9'})
|
559
|
+
|
560
|
+
scan['ExclusiveStartKey'] = { 'HashKeyElement' => { 'S' => 'att2' }, 'RangeKeyElement' => { "N" => '8' }}
|
561
|
+
result['Items'][0]['AttributeName1'].should eq({'S' => 'att2'})
|
562
|
+
result['Items'][0]['AttributeName2'].should eq({'N' => '9'})
|
563
|
+
end
|
564
|
+
|
529
565
|
end
|
530
566
|
end
|
531
567
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fake_dynamo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05
|
12
|
+
date: 2012-06-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
16
|
-
requirement: &
|
16
|
+
requirement: &70282333825460 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70282333825460
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &70282333824700 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70282333824700
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: json
|
38
|
-
requirement: &
|
38
|
+
requirement: &70282333823880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70282333823880
|
47
47
|
description:
|
48
48
|
email:
|
49
49
|
- ananthakumaran@gmail.com
|