fake_dynamo 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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