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 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 like `BatchGetItem` will return all items irrespective of the response size
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 data) in `/usr/local/var/fake_dynamo/db.fdb` and replays it before starting the server.
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.
@@ -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
@@ -4,6 +4,8 @@ module FakeDynamo
4
4
  class Server < Sinatra::Base
5
5
 
6
6
  set :show_exceptions, false
7
+ set :environment, :production
8
+ set :lock, true
7
9
 
8
10
  post '/' do
9
11
  status = 200
@@ -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
- all_items.drop_while { |i| i.key.as_key_hash != start_key_hash }.drop(1)
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
@@ -1,3 +1,3 @@
1
1
  module FakeDynamo
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -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..10).each do |j|
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' => '2'})
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' => '10'})
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"=>"6"}}
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"=>"6"}}}))
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' => '7'})
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' => '4'}],
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' => '2' })
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..10).each do |j|
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(30)
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"=>"5"}}
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.6
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-10 00:00:00.000000000 Z
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: &70284409972980 !ruby/object:Gem::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: *70284409972980
24
+ version_requirements: *70282333825460
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70284409992000 !ruby/object:Gem::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: *70284409992000
35
+ version_requirements: *70282333824700
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: json
38
- requirement: &70284409991540 !ruby/object:Gem::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: *70284409991540
46
+ version_requirements: *70282333823880
47
47
  description:
48
48
  email:
49
49
  - ananthakumaran@gmail.com