dynamo_record 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +31 -0
  6. data/Rakefile +7 -0
  7. data/dynamo_record.gemspec +28 -0
  8. data/lib/dynamo_record/collection.rb +31 -0
  9. data/lib/dynamo_record/config.rb +15 -0
  10. data/lib/dynamo_record/document.rb +32 -0
  11. data/lib/dynamo_record/fields.rb +31 -0
  12. data/lib/dynamo_record/finders.rb +17 -0
  13. data/lib/dynamo_record/persistence.rb +85 -0
  14. data/lib/dynamo_record/query.rb +29 -0
  15. data/lib/dynamo_record/version.rb +3 -0
  16. data/lib/dynamo_record.rb +25 -0
  17. data/spec/app/models/address.rb +5 -0
  18. data/spec/app/models/person.rb +5 -0
  19. data/spec/dynamo_record/collection_spec.rb +21 -0
  20. data/spec/dynamo_record/config_spec.rb +25 -0
  21. data/spec/dynamo_record/document_spec.rb +19 -0
  22. data/spec/dynamo_record/fields_spec.rb +29 -0
  23. data/spec/dynamo_record/finders_spec.rb +20 -0
  24. data/spec/dynamo_record/persistence_spec.rb +89 -0
  25. data/spec/dynamo_record/query_spec.rb +58 -0
  26. data/spec/dynamo_record_spec.rb +5 -0
  27. data/spec/fixtures/vcr_cassettes/DynamoRecord_Document/initializes_from_database.yml +52 -0
  28. data/spec/fixtures/vcr_cassettes/DynamoRecord_Fields/_find/finds_record.yml +52 -0
  29. data/spec/fixtures/vcr_cassettes/DynamoRecord_Fields/_find/when_record_doesn_t_exists/returns_empty_object.yml +52 -0
  30. data/spec/fixtures/vcr_cassettes/DynamoRecord_Persistence/_create_table/with_index/creates_table.yml +52 -0
  31. data/spec/fixtures/vcr_cassettes/DynamoRecord_Persistence/_create_table/without_index/creates_table.yml +52 -0
  32. data/spec/fixtures/vcr_cassettes/DynamoRecord_Persistence/does_not_overwrite_existing_record.yml +103 -0
  33. data/spec/fixtures/vcr_cassettes/DynamoRecord_Persistence/updates_record.yml +150 -0
  34. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/find_all_records.yml +54 -0
  35. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/find_all_records_with_limie.yml +52 -0
  36. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/find_all_records_with_limit.yml +52 -0
  37. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/paginates_records.yml +52 -0
  38. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/pagination/enumerates_all_pages.yml +199 -0
  39. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/pagination/navigates_to_next_page.yml +101 -0
  40. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_all/pagination/tells_if_there_is_a_next_page.yml +52 -0
  41. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/_query/queries_by_condition.yml +53 -0
  42. data/spec/fixtures/vcr_cassettes/DynamoRecord_Query/querying/_where/queries_by_condition.yml +53 -0
  43. data/spec/spec_helper.rb +18 -0
  44. metadata +211 -0
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord::Config do
4
+
5
+ after do
6
+ DynamoRecord::Config.set_defaults
7
+ end
8
+
9
+ it 'has default options' do
10
+ expect(DynamoRecord::Config.region).to eq('us-east-1')
11
+ end
12
+
13
+ it 'sets config options' do
14
+ DynamoRecord.configure do |config|
15
+ config.access_key_id = 'key'
16
+ config.secret_access_key = 'secret'
17
+ config.region = 'region'
18
+ end
19
+
20
+ expect(DynamoRecord::Config.access_key_id).to eq('key')
21
+ expect(DynamoRecord::Config.secret_access_key).to eq('secret')
22
+ expect(DynamoRecord::Config.region).to eq('region')
23
+ end
24
+
25
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord::Document do
4
+
5
+ it 'initializes a new document' do
6
+ class Person
7
+ include DynamoRecord::Document
8
+ end
9
+ person = Person.new
10
+ expect(person.new_record).to be_truthy
11
+ expect(person.attributes).to be_empty
12
+ end
13
+
14
+ it 'initializes from database', :vcr do
15
+ person = Person.find('f9b351b0-d06d-4fff-b8d4-8af162e2b8ba')
16
+ expect(person.new_record).to be_falsy
17
+ end
18
+
19
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord::Fields do
4
+
5
+ it 'has default id primary key field' do
6
+ person = Person.new
7
+ expect(person.id).to be_nil
8
+ end
9
+
10
+ it 'accepts adding fields' do
11
+ expect(Person.attributes).to eq({name: {type: :string, options: {}}})
12
+ end
13
+
14
+ it 'accepts adding fields with index' do
15
+ class City
16
+ include DynamoRecord::Document
17
+
18
+ field :name, :string, index: true
19
+ end
20
+
21
+ expect(City.attributes).to eq({name: {type: :string, options: {index: true}}})
22
+ end
23
+
24
+ it 'initializes with field values' do
25
+ person = Person.new(name: 'A person')
26
+ expect(person.name).to eq('A person')
27
+ end
28
+
29
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord::Fields, :vcr do
4
+
5
+ describe '#find' do
6
+ it 'finds record' do
7
+ person = Person.find('f9b351b0-d06d-4fff-b8d4-8af162e2b8ba')
8
+ expect(person.id).to eq('f9b351b0-d06d-4fff-b8d4-8af162e2b8ba')
9
+ expect(person.name).to eq('A person')
10
+ end
11
+
12
+ context 'when record doesn\'t exists' do
13
+ it 'returns empty object' do
14
+ person = Person.find('not here')
15
+ expect(person.id).to be_nil
16
+ end
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord::Persistence, :vcr do
4
+
5
+ it 'has table name' do
6
+ expect(Person.table_name).to eq('people')
7
+ end
8
+
9
+ describe 'namespace' do
10
+ before do
11
+ DynamoRecord.configure do |config|
12
+ config.namespace = 'namespace'
13
+ end
14
+ end
15
+
16
+ after do
17
+ DynamoRecord::Config.set_defaults
18
+ end
19
+
20
+ it 'has table name with namespace' do
21
+ expect(Address.table_name).to eq('namespace_addresses')
22
+ end
23
+ end
24
+
25
+ describe '#create_table' do
26
+ context 'with index' do
27
+ it 'creates table' do
28
+ class IndexCity
29
+ include DynamoRecord::Document
30
+
31
+ field :name, :string, index: true
32
+ end
33
+
34
+ IndexCity.create_table
35
+ end
36
+ end
37
+
38
+ context 'without index' do
39
+ it 'creates table' do
40
+ class NoIndexCity
41
+ include DynamoRecord::Document
42
+
43
+ field :name, :string
44
+ end
45
+
46
+ NoIndexCity.create_table
47
+ end
48
+ end
49
+ end
50
+
51
+ it 'saves record' do
52
+ expect(SecureRandom).to receive(:uuid).and_return('a uuid')
53
+ client = spy('client')
54
+ allow(Person).to receive(:client).and_return(client)
55
+
56
+ person = Person.new(name: 'A person')
57
+ person.save
58
+
59
+ expect(client).to have_received(:put_item).
60
+ with({table_name: 'people',
61
+ item: {id: 'a uuid',
62
+ name: 'A person'}})
63
+ expect(person.new_record).to be_falsy
64
+ expect(person.id).to eq('a uuid')
65
+ end
66
+
67
+ it 'does not overwrite existing record' do
68
+ person = Person.new(id: 'f9b351b0-d06d-4fff-b8d4-8af162e2b8ba', name: 'New item')
69
+ expect(person.save).to be_falsy
70
+
71
+ person = Person.find('f9b351b0-d06d-4fff-b8d4-8af162e2b8ba')
72
+ expect(person.name).to_not eq('New item')
73
+ end
74
+
75
+ it 'updates record' do
76
+ DynamoRecord.configure do |config|
77
+ config.access_key_id = 'key'
78
+ config.secret_access_key = 'TfWvbWtJ96DPM+QduJDXVkGKGbwhIyAYpPSnXad1'
79
+ end
80
+
81
+ person = Person.find('f9b351b0-d06d-4fff-b8d4-8af162e2b8ba')
82
+ person.name = 'New name'
83
+ person.save
84
+
85
+ person = Person.find('f9b351b0-d06d-4fff-b8d4-8af162e2b8ba')
86
+ expect(person.name).to eq('New name')
87
+ end
88
+
89
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord::Query, :vcr do
4
+ before do
5
+ DynamoRecord.configure do |config|
6
+ config.access_key_id = 'key'
7
+ config.secret_access_key = 'TfWvbWtJ96DPM+QduJDXVkGKGbwhIyAYpPSnXad1'
8
+ end
9
+ end
10
+
11
+ describe '#all' do
12
+ it 'find all records' do
13
+ people = Person.all
14
+ expect(people.count).to eq(3)
15
+ expect(people.map(&:name)).to eq(['Person 1', 'Person 2', 'Person 3'])
16
+ end
17
+
18
+ it 'find all records with limit' do
19
+ people = Person.all(limit: 1)
20
+ expect(people.count).to eq(1)
21
+ expect(people.map(&:name)).to eq(['Person 1'])
22
+ end
23
+
24
+ describe 'pagination' do
25
+ it 'tells if there is a next page' do
26
+ people = Person.all(limit: 1)
27
+ expect(people.next_page?).to be_truthy
28
+ end
29
+
30
+ it 'navigates to next page' do
31
+ people = Person.all(limit: 1)
32
+ next_page = people.next_page
33
+ expect(next_page.count).to eq(1)
34
+ expect(next_page.map(&:name)).to eq(['Person 2'])
35
+ end
36
+
37
+ it 'enumerates all pages' do
38
+ records = []
39
+ Person.all(limit: 1).each_page do |page|
40
+ records << page
41
+ end
42
+
43
+ expect(records.first.map(&:name)).to eq(['Person 1'])
44
+ expect(records.second.map(&:name)).to eq(['Person 2'])
45
+ expect(records.third.map(&:name)).to eq(['Person 3'])
46
+ end
47
+ end
48
+ end
49
+
50
+ describe 'querying' do
51
+ describe '#where' do
52
+ it 'queries by condition' do
53
+ people = Person.where(name: 'Person 2')
54
+ expect(people.map(&:name)).to eq(['Person 2'])
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe DynamoRecord do
4
+
5
+ end
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://dynamodb.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"TableName":"people","Key":{"id":{"S":"f9b351b0-d06d-4fff-b8d4-8af162e2b8ba"}}}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.0
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.0.17 ruby/2.1.2 x86_64-darwin14.0
16
+ X-Amz-Target:
17
+ - DynamoDB_20120810.GetItem
18
+ X-Amz-Date:
19
+ - 20141231T072158Z
20
+ Host:
21
+ - dynamodb.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - 11a49d1d6421d3540cf7a7c96423eb631d8b8de996d5069f178a631eea8d8e01
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=key/20141231/us-east-1/dynamodb/aws4_request,
26
+ SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=c8f303b4b0b133053cc2efe7f669e0fa82522778902adc796497ac947416887b
28
+ Content-Length:
29
+ - '80'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 200
35
+ message: OK
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - MGQQ63GBV5B4O1I0QMLAAH6N6BVV4KQNSO5AEMVJF66Q9ASUAAJG
39
+ X-Amz-Crc32:
40
+ - '3221367258'
41
+ Content-Type:
42
+ - application/x-amz-json-1.0
43
+ Content-Length:
44
+ - '84'
45
+ Date:
46
+ - Wed, 31 Dec 2014 07:21:58 GMT
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"Item":{"name":{"S":"Person 1"},"id":{"S":"f9b351b0-d06d-4fff-b8d4-8af162e2b8ba"}}}'
50
+ http_version:
51
+ recorded_at: Wed, 31 Dec 2014 07:21:59 GMT
52
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://dynamodb.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"TableName":"people","Key":{"id":{"S":"f9b351b0-d06d-4fff-b8d4-8af162e2b8ba"}}}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.0
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.0.17 ruby/2.1.2 x86_64-darwin14.0
16
+ X-Amz-Target:
17
+ - DynamoDB_20120810.GetItem
18
+ X-Amz-Date:
19
+ - 20141230T053939Z
20
+ Host:
21
+ - dynamodb.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - 11a49d1d6421d3540cf7a7c96423eb631d8b8de996d5069f178a631eea8d8e01
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=key/20141230/us-east-1/dynamodb/aws4_request,
26
+ SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=fcf88dcdbfdfaf7823b478e10e402bc05571dfeffe3e39edc588d4199cc2a318
28
+ Content-Length:
29
+ - '80'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 200
35
+ message: OK
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - 57MJJPT0NUE6TP8N2IR82EBQO3VV4KQNSO5AEMVJF66Q9ASUAAJG
39
+ X-Amz-Crc32:
40
+ - '3713739097'
41
+ Content-Type:
42
+ - application/x-amz-json-1.0
43
+ Content-Length:
44
+ - '84'
45
+ Date:
46
+ - Tue, 30 Dec 2014 05:39:41 GMT
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"Item":{"name":{"S":"A person"},"id":{"S":"f9b351b0-d06d-4fff-b8d4-8af162e2b8ba"}}}'
50
+ http_version:
51
+ recorded_at: Tue, 30 Dec 2014 05:39:41 GMT
52
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://dynamodb.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"TableName":"people","Key":{"id":{"S":"not here"}}}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.0
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.0.17 ruby/2.1.2 x86_64-darwin14.0
16
+ X-Amz-Target:
17
+ - DynamoDB_20120810.GetItem
18
+ X-Amz-Date:
19
+ - 20141230T053941Z
20
+ Host:
21
+ - dynamodb.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - 56ecdde2214232f92e4332de89eca1b431c5f79599b1bcce3692cd8adf0c7248
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=key/20141230/us-east-1/dynamodb/aws4_request,
26
+ SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=70c0bfd738358533fa0fe8b4e01e46481bbb8667fd0d7625dfa51a7282694f51
28
+ Content-Length:
29
+ - '52'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 200
35
+ message: OK
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - DV8GC4GTLL5EG7QPCKGGFM1SO3VV4KQNSO5AEMVJF66Q9ASUAAJG
39
+ X-Amz-Crc32:
40
+ - '2745614147'
41
+ Content-Type:
42
+ - application/x-amz-json-1.0
43
+ Content-Length:
44
+ - '2'
45
+ Date:
46
+ - Tue, 30 Dec 2014 05:39:41 GMT
47
+ body:
48
+ encoding: UTF-8
49
+ string: "{}"
50
+ http_version:
51
+ recorded_at: Tue, 30 Dec 2014 05:39:42 GMT
52
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://dynamodb.us-east-1.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"AttributeDefinitions":[{"AttributeName":"id","AttributeType":"S"},{"AttributeName":"name","AttributeType":"S"}],"TableName":"indexcities","KeySchema":[{"AttributeName":"id","KeyType":"HASH"}],"ProvisionedThroughput":{"ReadCapacityUnits":20,"WriteCapacityUnits":20},"GlobalSecondaryIndexes":[{"IndexName":"name-index","KeySchema":[{"AttributeName":"name","KeyType":"HASH"}],"Projection":{"ProjectionType":"ALL"},"ProvisionedThroughput":{"ReadCapacityUnits":20,"WriteCapacityUnits":20}}]}'
9
+ headers:
10
+ Content-Type:
11
+ - application/x-amz-json-1.0
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.0.17 ruby/2.1.2 x86_64-darwin14.0
16
+ X-Amz-Target:
17
+ - DynamoDB_20120810.CreateTable
18
+ X-Amz-Date:
19
+ - 20141231T083113Z
20
+ Host:
21
+ - dynamodb.us-east-1.amazonaws.com
22
+ X-Amz-Content-Sha256:
23
+ - c5ded24b57df8f29482ee7457c489a5a5baeca7f0a6560930cf8578822323162
24
+ Authorization:
25
+ - AWS4-HMAC-SHA256 Credential=key/20141231/us-east-1/dynamodb/aws4_request,
26
+ SignedHeaders=content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-target,
27
+ Signature=51d5a106760691469c0e2a78a36d27baf4335bf57924c4c06fc4c1ac34400593
28
+ Content-Length:
29
+ - '488'
30
+ Accept:
31
+ - "*/*"
32
+ response:
33
+ status:
34
+ code: 200
35
+ message: OK
36
+ headers:
37
+ X-Amzn-Requestid:
38
+ - GSSH5TN95AUJK6QF3FPVHQEUG7VV4KQNSO5AEMVJF66Q9ASUAAJG
39
+ X-Amz-Crc32:
40
+ - '2558175273'
41
+ Content-Type:
42
+ - application/x-amz-json-1.0
43
+ Content-Length:
44
+ - '715'
45
+ Date:
46
+ - Wed, 31 Dec 2014 08:31:14 GMT
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"TableDescription":{"AttributeDefinitions":[{"AttributeName":"id","AttributeType":"S"},{"AttributeName":"name","AttributeType":"S"}],"CreationDateTime":1.420014674274E9,"GlobalSecondaryIndexes":[{"IndexName":"name-index","IndexSizeBytes":0,"IndexStatus":"CREATING","ItemCount":0,"KeySchema":[{"AttributeName":"name","KeyType":"HASH"}],"Projection":{"ProjectionType":"ALL"},"ProvisionedThroughput":{"NumberOfDecreasesToday":0,"ReadCapacityUnits":20,"WriteCapacityUnits":20}}],"ItemCount":0,"KeySchema":[{"AttributeName":"id","KeyType":"HASH"}],"ProvisionedThroughput":{"NumberOfDecreasesToday":0,"ReadCapacityUnits":20,"WriteCapacityUnits":20},"TableName":"indexcities","TableSizeBytes":0,"TableStatus":"CREATING"}}'
50
+ http_version:
51
+ recorded_at: Wed, 31 Dec 2014 08:31:14 GMT
52
+ recorded_with: VCR 2.9.3