dynamoid 1.3.4 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +3 -0
  4. data/.travis.yml +37 -7
  5. data/Appraisals +11 -0
  6. data/CHANGELOG.md +115 -2
  7. data/Gemfile +2 -0
  8. data/LICENSE.txt +18 -16
  9. data/README.md +253 -34
  10. data/Rakefile +0 -24
  11. data/Vagrantfile +1 -1
  12. data/docker-compose.yml +7 -0
  13. data/dynamoid.gemspec +4 -4
  14. data/gemfiles/rails_4_0.gemfile +3 -3
  15. data/gemfiles/rails_4_1.gemfile +3 -3
  16. data/gemfiles/rails_4_2.gemfile +3 -3
  17. data/gemfiles/rails_5_0.gemfile +2 -1
  18. data/gemfiles/rails_5_1.gemfile +8 -0
  19. data/gemfiles/rails_5_2.gemfile +8 -0
  20. data/lib/dynamoid.rb +31 -31
  21. data/lib/dynamoid/adapter.rb +14 -10
  22. data/lib/dynamoid/adapter_plugin/aws_sdk_v2.rb +188 -100
  23. data/lib/dynamoid/associations.rb +21 -12
  24. data/lib/dynamoid/associations/association.rb +19 -3
  25. data/lib/dynamoid/associations/belongs_to.rb +26 -16
  26. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +0 -16
  27. data/lib/dynamoid/associations/has_many.rb +2 -17
  28. data/lib/dynamoid/associations/has_one.rb +0 -14
  29. data/lib/dynamoid/associations/many_association.rb +19 -6
  30. data/lib/dynamoid/associations/single_association.rb +25 -7
  31. data/lib/dynamoid/config.rb +37 -18
  32. data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +11 -0
  33. data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +25 -0
  34. data/lib/dynamoid/config/options.rb +1 -1
  35. data/lib/dynamoid/criteria/chain.rb +48 -32
  36. data/lib/dynamoid/dirty.rb +23 -4
  37. data/lib/dynamoid/document.rb +88 -5
  38. data/lib/dynamoid/errors.rb +4 -1
  39. data/lib/dynamoid/fields.rb +6 -6
  40. data/lib/dynamoid/finders.rb +42 -12
  41. data/lib/dynamoid/identity_map.rb +0 -1
  42. data/lib/dynamoid/indexes.rb +41 -54
  43. data/lib/dynamoid/persistence.rb +151 -40
  44. data/lib/dynamoid/railtie.rb +1 -1
  45. data/lib/dynamoid/validations.rb +4 -3
  46. data/lib/dynamoid/version.rb +1 -1
  47. metadata +18 -29
  48. data/gemfiles/rails_4_0.gemfile.lock +0 -150
  49. data/gemfiles/rails_4_1.gemfile.lock +0 -154
  50. data/gemfiles/rails_4_2.gemfile.lock +0 -175
  51. data/gemfiles/rails_5_0.gemfile.lock +0 -180
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 563c512230d5a7f2485b6cf226eff77accfa2a3e
4
- data.tar.gz: e2f88e774032ddf472bc81a2b9e2a5844b61933a
3
+ metadata.gz: 542fc1c696af080fcd65a7428ec553584d3fe48d
4
+ data.tar.gz: 93cca063db742ea205238443dcf98ab8e03ba627
5
5
  SHA512:
6
- metadata.gz: 4a9dba2ea759a74d83f27ef984e3ca963230ddebde577084e89324eb69dd08c65c4c19690891e4086bc9cacadb17eaed5c460c03944dcc9db1eb109044ceef91
7
- data.tar.gz: 314ffb14350619c950bd6615d46ccb3ca32383116f1a1dfe0aa39891694575e89d3376051bfac53b4e4a0f584257c0315a334365e5715727bcc767a31c2daa5e
6
+ metadata.gz: 438f005ef464d1f90f8e6ba57b3c1b2969e183828e2ad35dcb9fd5db00dea9bc394684ad1787c4c61c6ad23f8a879a933b8a18be3254fc730bc74c1d39b574e9
7
+ data.tar.gz: 905e8db4276d80e280fd95fb56902da845a0aa0eb496c4251d67deff3200436a83fada31f23af341fe490e4576956c9db7e5f9137d58ac53f68e7505addae7d1
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore CHANGED
@@ -69,3 +69,6 @@ Gemfile.lock
69
69
 
70
70
  # For vagrant
71
71
  .vagrant
72
+
73
+ # For Appraisals
74
+ gemfiles/*.gemfile.lock
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
+ sudo: required
2
+
1
3
  language: ruby
2
4
  rvm:
3
5
  - ruby-2.0.0-p648
@@ -6,26 +8,54 @@ rvm:
6
8
  - ruby-2.3.4
7
9
  - ruby-2.4.1
8
10
  - jruby-9.1.9.0
11
+ gemfile:
9
12
  gemfile:
10
13
  - gemfiles/rails_4_0.gemfile
11
14
  - gemfiles/rails_4_1.gemfile
12
15
  - gemfiles/rails_4_2.gemfile
13
16
  - gemfiles/rails_5_0.gemfile
17
+ - gemfiles/rails_5_1.gemfile
18
+ - gemfiles/rails_5_2.gemfile
14
19
  matrix:
15
20
  exclude:
16
21
  - rvm: ruby-2.0.0-p648
17
22
  gemfile: gemfiles/rails_5_0.gemfile
23
+ - rvm: ruby-2.0.0-p648
24
+ gemfile: gemfiles/rails_5_1.gemfile
25
+ - rvm: ruby-2.0.0-p648
26
+ gemfile: gemfiles/rails_5_2.gemfile
18
27
  - rvm: ruby-2.1.10
19
28
  gemfile: gemfiles/rails_5_0.gemfile
29
+ - rvm: ruby-2.1.10
30
+ gemfile: gemfiles/rails_5_1.gemfile
31
+ - rvm: ruby-2.1.10
32
+ gemfile: gemfiles/rails_5_2.gemfile
20
33
  - rvm: ruby-2.4.1
21
34
  gemfile: gemfiles/rails_4_0.gemfile
22
35
  - rvm: ruby-2.4.1
23
36
  gemfile: gemfiles/rails_4_1.gemfile
24
- before_install: gem install bundler -v 1.15.4
25
- install:
26
- - wget http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.zip --quiet -O spec/dynamodb_temp.zip
27
- - unzip -qq spec/dynamodb_temp.zip -d spec/DynamoDBLocal-latest
28
- - rm spec/dynamodb_temp.zip
29
- script:
37
+
38
+ ### BUILD LIFECYCLE STEPS ###
39
+
40
+ before_install:
41
+ # Debugging: Print out the current docker-compose version.
42
+ - docker-compose --version
43
+
44
+ # If one of your containers does not build for
45
+ # whatever reason it's best to report that now before your tests start
46
+ # otherwise it can be really tricky to debug why tests are failing sometimes.
47
+ - docker ps
48
+
49
+ after_install:
50
+ - gem install bundler -v 1.15.4
30
51
  - bundle install
31
- - bundle exec rake unattended_spec
52
+
53
+ before_script:
54
+ # Start Docker Compose as a daemon
55
+ - docker-compose up -d
56
+
57
+ script:
58
+ - bundle exec rake spec
59
+
60
+ after_script:
61
+ - docker-compose down
data/Appraisals CHANGED
@@ -1,15 +1,26 @@
1
1
  appraise "rails-4-0" do
2
2
  gem "rails", "~> 4.0.0"
3
+ gem "nokogiri", "~> 1.6.8" # can be removed once we drop support for Ruby 2.0.0
3
4
  end
4
5
 
5
6
  appraise "rails-4-1" do
6
7
  gem "rails", "~> 4.1.0"
8
+ gem "nokogiri", "~> 1.6.8" # can be removed once we drop support for Ruby 2.0.0
7
9
  end
8
10
 
9
11
  appraise "rails-4-2" do
10
12
  gem "rails", "~> 4.2.0"
13
+ gem "nokogiri", "~> 1.6.8" # can be removed once we drop support for Ruby 2.0.0
11
14
  end
12
15
 
13
16
  appraise "rails-5-0" do
14
17
  gem "rails", "~> 5.0.0"
15
18
  end
19
+
20
+ appraise "rails-5-1" do
21
+ gem "rails", "~> 5.1.0"
22
+ end
23
+
24
+ appraise "rails-5-2" do
25
+ gem "rails", "~> 5.2.0"
26
+ end
data/CHANGELOG.md CHANGED
@@ -1,8 +1,121 @@
1
1
  # HEAD
2
2
 
3
+ ## Breaking
4
+
5
+ * N/A
6
+
7
+ ## Improvements
8
+
9
+ * N/A
10
+
11
+ ## Fixes
12
+
13
+ * N/A
14
+
15
+ # 2.2.0
16
+
17
+ ## Breaking
18
+
19
+ * N/A
20
+
21
+ ## Improvements
22
+
23
+ * Feature: [#256](https://github.com/Dynamoid/Dynamoid/pull/256) Support Rails 5.2 (@andrykonchin)
24
+
25
+ ## Fixes
26
+
27
+ * Bug: [#255](https://github.com/Dynamoid/Dynamoid/pull/255) Fix Vagrant RVM configuration and upgrade to Ruby 2.4.1 (@richardhsu)
28
+
29
+ # 2.1.0
30
+
31
+ ## Breaking
32
+
33
+ * N/A
34
+
35
+ ## Improvements
36
+
37
+ * Feature: [#221](https://github.com/Dynamoid/Dynamoid/pull/221) Add field declaration option `of` to specify the type of `set` elements (@pratik60)
38
+ * Feature: [#223](https://github.com/Dynamoid/Dynamoid/pull/223) Add field declaration option `store_as_string` to store `datetime` as ISO-8601 formatted strings (@william101)
39
+ * Feature: [#228](https://github.com/Dynamoid/Dynamoid/pull/228) Add field declaration option `store_as_string` to store `date` as ISO-8601 formatted strings (@andrykonchin)
40
+ * Feature: [#229](https://github.com/Dynamoid/Dynamoid/pull/229) Support hash argument for `start` chain method (@mnussbaumer)
41
+ * Feature: [#236](https://github.com/Dynamoid/Dynamoid/pull/236) Change log level from `info` to `debug` for benchmark logging (@kicktheken)
42
+ * Feature: [#239](https://github.com/Dynamoid/Dynamoid/pull/239) Add methods for low-level updating: `.update`, `.update_fields` and `.upsert` (@andrykonchin)
43
+ * Feature: [#243](https://github.com/Dynamoid/Dynamoid/pull/243) Support `ne` condition operator (@andrykonchin)
44
+ * Feature: [#246](https://github.com/Dynamoid/Dynamoid/pull/246) Added support of backoff in batch operations (@andrykonchin)
45
+ * added global config options `backoff` and `backoff_strategies` to configure backoff
46
+ * added `constant` and `exponential` built-in backoff strategies
47
+ * `.find_all` and `.import` support new backoff options
48
+
49
+ ## Fixes
50
+
51
+ * Bug: [#216](https://github.com/Dynamoid/Dynamoid/pull/216) Fix global index detection in queries with conditions other than equal (@andrykonchin)
52
+ * Bug: [#224](https://github.com/Dynamoid/Dynamoid/pull/224) Fix how `contains` operator works with `set` and `array` field types (@andrykonchin)
53
+ * Bug: [#225](https://github.com/Dynamoid/Dynamoid/pull/225) Fix equal conditions for `array` fields (@andrykonchin)
54
+ * Bug: [#229](https://github.com/Dynamoid/Dynamoid/pull/229) Repair support `start` chain method on Scan operation (@mnussbaumer)
55
+ * Bug: [#238](https://github.com/Dynamoid/Dynamoid/pull/238) Fix default value of `models_dir` config option (@baloran)
56
+ * Bug: [#244](https://github.com/Dynamoid/Dynamoid/pull/244) Allow to pass empty strings and sets to `.import` (@andrykonchin)
57
+ * Bug: [#246](https://github.com/Dynamoid/Dynamoid/pull/246) Batch operations (`batch_write_item` and `batch_read_item`) handle unprocessed items themselves (@andrykonchin)
58
+ * Bug: [#250](https://github.com/Dynamoid/Dynamoid/pull/250) Update outdated warning message about inefficient query and missing indices (@andrykonchin)
59
+ * Bug: [252](https://github.com/Dynamoid/Dynamoid/pull/252) Don't loose nanoseconds when store DateTime as float number
60
+
61
+ # 2.0.0
62
+
63
+ ## Breaking
64
+
65
+ Breaking changes in this release generally bring Dynamoid behavior closer to the Rails-way.
66
+
67
+ * Change: [#186](https://github.com/Dynamoid/Dynamoid/pull/186) Consistent behavior for `Model.where({}).all` (@andrykonchin)
68
+ * <= 1.3.x behaviour -
69
+ * load lazily if user specified batch size
70
+ * load all collection into memory otherwise
71
+ * New behaviour -
72
+ * always return lazy evaluated collection
73
+ * It means Model.where({}).all returns Enumerator instead of Array.
74
+ * If you need Array interface you have to convert collection to Array manually with to_a method call
75
+ * Change: [#195](https://github.com/Dynamoid/Dynamoid/pull/195) Failed `#find` returns error (@andrykonchin)
76
+ * <= 1.3.x behaviour - find returns nil or smaller array.
77
+ * New behaviour - it raises RecordNotFound if one or more records can not be found for the requested ids
78
+ * Change: [#196](https://github.com/Dynamoid/Dynamoid/pull/196) Return value of `#save` (@andrykonchin)
79
+ * <= 1.3.x behaviour - save returns self if model is saved successfully
80
+ * New behaviour - it returns true
81
+
82
+ ## Improvements
83
+
84
+ * Feature: [#185](https://github.com/Dynamoid/Dynamoid/pull/185) `where`, finders and friends take into account STI (single table inheritance) now (@andrykonchin)
85
+ * query will return items of the model class and all subclasses
86
+ * Feature: [#190](https://github.com/Dynamoid/Dynamoid/pull/190) Allow passing options to range when defining attributes of the document (@richardhsu)
87
+ * Allows for serialized fields and passing the serializer option.
88
+ * Feature: [#198](https://github.com/Dynamoid/Dynamoid/pull/198) Enhanced `#create` and `#create!` to allow multiple document creation like `#import` (@andrykonchin)
89
+ * `User.create([{name: 'Josh'}, {name: 'Nick'}])`
90
+ * Feature: [#199](https://github.com/Dynamoid/Dynamoid/pull/199) Added `Document.import` method (@andrykonchin)
91
+ * Feature: [#205](https://github.com/Dynamoid/Dynamoid/pull/205) Use batch deletion via `batch_write_item` for `delete_all` (@andrykonchin)
92
+ * Rename: [#205](https://github.com/Dynamoid/Dynamoid/pull/205) `Chain#destroy_all` as `Chain#delete_all`, to better match Rails conventions when no callbacks are run (@andrykonchin)
93
+ * kept the old name as an alias, for backwards compatibility
94
+ * Feature: [#207](https://github.com/Dynamoid/Dynamoid/pull/207) Added slicing by 25 requests in #batch_write_item (@andrykonchin)
95
+ * Feature: [#211](https://github.com/Dynamoid/Dynamoid/pull/211) Improved Vagrant setup for testing (@richardhsu)
96
+ * Feature: [#212](https://github.com/Dynamoid/Dynamoid/pull/212) Add foreign_key option (@andrykonchin)
97
+ * Feature: [#213](https://github.com/Dynamoid/Dynamoid/pull/213) Support Boolean raw type (@andrykonchin)
98
+ * Improved Documentation (@pboling, @andrykonchin)
99
+
100
+ ## Fixes
101
+
102
+ * Bug: [#191](https://github.com/Dynamoid/Dynamoid/pull/191), [#192](https://github.com/Dynamoid/Dynamoid/pull/192) Support lambdas as fix for value types were not able to be used as default values (@andrykonchin)(@richardhsu)
103
+ * Bug: [#202](https://github.com/Dynamoid/Dynamoid/pull/202) Fix several issues with associations (@andrykonchin)
104
+ * setting `nil` value raises an exception
105
+ * document doesn't keep assigned model and loads it from the storage
106
+ * delete call doesn't update cached ids of associated models
107
+ * fix clearing old `has_many` association while add model to new `has_many` association
108
+ * Bug: [#204](https://github.com/Dynamoid/Dynamoid/pull/204) Fixed issue where `Document.where(:"id.in" => [])` would do `Query` operation instead of `Scan` (@andrykonchin)
109
+ * Fixed `Chain#key_present?`
110
+ * Bug: [#205](https://github.com/Dynamoid/Dynamoid/pull/205) Fixed `delete_all` (@andrykonchin)
111
+ * Fixed exception when makes scan and sort key is declared in model
112
+ * Fixed exception when makes scan and any condition is specified in where clause (like Document.where().delete_all)
113
+ * Fixed exception when makes query and sort key isn't declared in model
114
+ * Bug: [#207](https://github.com/Dynamoid/Dynamoid/pull/207) Fixed `#delete` method for case `adapter.delete(table_name, [1, 2, 3], range_key: 1)` (@andrykonchin)
115
+
3
116
  # 1.3.4
4
117
 
5
- Improving
118
+ ## Improvements
6
119
 
7
120
  * Added `Chain#last` method (@andrykonchin)
8
121
  * Added `date` field type (@andrykonchin)
@@ -19,7 +132,7 @@ Improving
19
132
  * Support querying Global/Local Secondary Indices in `where` clause (@richardhsu)
20
133
  * Only query on GSI if projects all attributes in `where` clause (@richardhsu)
21
134
 
22
- Fixes
135
+ ## Fixes
23
136
 
24
137
  * Fix incorrect applying of default field value (#36 and #117, @andrykonchin)
25
138
  * Fix sync table creation/deletion (#160, @mirokuxy)
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in dynamoid.gemspec
4
4
  gemspec
5
+
6
+ gem "pry-byebug", platforms: :ruby
data/LICENSE.txt CHANGED
@@ -1,20 +1,22 @@
1
+ MIT License
2
+
1
3
  Copyright (c) 2012 Josh Symonds
4
+ Copyright (c) 2013 - 2018 Dynamoid, https://github.com/Dynamoid
2
5
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
10
12
 
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
13
15
 
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Dynamoid
2
2
 
3
+ You are viewing the README for version 2 of Dynamoid. See the [CHANGELOG](https://github.com/Dynamoid/Dynamoid/blob/master/CHANGELOG.md#200) for details on breaking changes since 1.3.x.
4
+
5
+ For version 1.3.x use the [1-3-stable branch](https://github.com/Dynamoid/Dynamoid/blob/1-3-stable/README.md).
6
+
3
7
  Dynamoid is an ORM for Amazon's DynamoDB for Ruby applications. It
4
8
  provides similar functionality to ActiveRecord and improves on
5
9
  Amazon's existing
@@ -10,21 +14,31 @@ DynamoDB is not like other document-based databases you might know, and is very
10
14
 
11
15
  But if you want a fast, scalable, simple, easy-to-use database (and a Gem that supports it) then look no further!
12
16
 
13
- ## Call For Maintainers
14
17
 
15
- Please inquire within.
16
- https://github.com/Dynamoid/Dynamoid/issues/125
18
+ | Project | Dynamoid |
19
+ |------------------------ | ----------------- |
20
+ | gem name | dynamoid |
21
+ | license | MIT |
22
+ | download rank | [![Total Downloads](https://img.shields.io/gem/rt/Dynamoid.svg)](https://rubygems.org/gems/dynamoid) |
23
+ | version | [![Gem Version](https://badge.fury.io/rb/dynamoid.svg)](https://rubygems.org/gems/dynamoid) |
24
+ | dependencies | [![Dependency Status](https://gemnasium.com/badges/github.com/Dynamoid/Dynamoid.svg)](https://gemnasium.com/github.com/Dynamoid/Dynamoid) [![Depfu](https://badges.depfu.com/badges/6661c063c8e77a5008344fc7283a50aa/status.svg)](https://depfu.com)|
25
+ | code quality | [![Code Climate](https://codeclimate.com/github/Dynamoid/Dynamoid.svg)](https://codeclimate.com/github/Dynamoid/Dynamoid) |
26
+ | continuous integration | [![Build Status](https://travis-ci.org/Dynamoid/Dynamoid.svg?branch=master)](https://travis-ci.org/Dynamoid/Dynamoid) |
27
+ | test coverage | [![Coverage Status](https://coveralls.io/repos/github/Dynamoid/Dynamoid/badge.svg?branch=master)](https://coveralls.io/github/Dynamoid/Dynamoid?branch=master) |
28
+ | triage helpers | [![CodeTriage Helpers](https://www.codetriage.com/dynamoid/dynamoid/badges/users.svg)](https://www.codetriage.com/dynamoid/dynamoid) |
29
+ | homepage | [https://github.com/Dynamoid/Dynamoid](https://github.com/Dynamoid/Dynamoid) |
30
+ | documentation | [http://rdoc.info/github/Dynamoid/Dynamoid/frames](http://rdoc.info/github/Dynamoid/Dynamoid/frames) |
17
31
 
18
32
  ## Installation
19
33
 
20
34
  Installing Dynamoid is pretty simple. First include the Gem in your Gemfile:
21
35
 
22
36
  ```ruby
23
- gem 'dynamoid', '~> 1'
37
+ gem 'dynamoid', '~> 2'
24
38
  ```
25
39
  ## Prerequisities
26
40
 
27
- Dynamoid depends on the aws-sdk, and this is tested on the current version of aws-sdk (~> 2), rails (~> 4).
41
+ Dynamoid depends on the aws-sdk, and this is tested on the current version of aws-sdk (~> 2), rails (>= 4).
28
42
  Hence the configuration as needed for aws to work will be dealt with by aws setup.
29
43
 
30
44
  Here are the steps to setup aws-sdk.
@@ -35,7 +49,18 @@ gem 'aws-sdk', '~>2'
35
49
 
36
50
  (or) include the aws-sdk in your Gemfile.
37
51
 
38
- **NOTE:** Dynamoid-1.0 doesn't support aws-sdk Version 1 (Use Dynamoid Major Version 0 for aws-sdk 1)
52
+ ### AWS SDK Version Compatibility
53
+
54
+ Make sure you are using the version for the right AWS SDK.
55
+
56
+ | Dynamoid version | AWS SDK Version |
57
+ | ---------------- | --------------- |
58
+ | 0.x | 1.x |
59
+ | 1.x | 2.x |
60
+ | 2.x | 2.x |
61
+ | 3.x (unreleased) | 3.x |
62
+
63
+ ### AWS Configuration
39
64
 
40
65
  Configure AWS access:
41
66
  [Reference](https://github.com/aws/aws-sdk-ruby)
@@ -56,6 +81,7 @@ Create config/initializers/aws.rb as follows:
56
81
  Alternatively, if you don't want Aws connection settings to be overwritten for you entire project, you can specify connection settings for Dynamoid only, by setting those in the `Dynamoid.configure` clause:
57
82
 
58
83
  ```ruby
84
+ require 'dynamoid'
59
85
  Dynamoid.configure do |config|
60
86
  config.access_key = 'REPLACE_WITH_ACCESS_KEY_ID'
61
87
  config.secret_key = 'REPLACE_WITH_SECRET_ACCESS_KEY'
@@ -69,6 +95,7 @@ For a full list of the DDB regions, you can go
69
95
  Then you need to initialize Dynamoid config to get it going. Put code similar to this somewhere (a Rails initializer would be a great place for this if you're using Rails):
70
96
 
71
97
  ```ruby
98
+ require 'dynamoid'
72
99
  Dynamoid.configure do |config|
73
100
  config.namespace = "dynamoid_app_development" # To namespace tables created by Dynamoid from other tables you might have. Set to nil to avoid namespacing.
74
101
  config.endpoint = 'http://localhost:3000' # [Optional]. If provided, it communicates with the DB listening at the endpoint. This is useful for testing with [Amazon Local DB] (http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html).
@@ -76,7 +103,7 @@ Then you need to initialize Dynamoid config to get it going. Put code similar to
76
103
 
77
104
  ```
78
105
 
79
- ### Compatibility Matrix
106
+ ### Ruby & Rails Compatibility Matrix
80
107
 
81
108
  | Ruby / Active Record | 4.0.x | 4.1.x | 4.2.x | 5.0.x |
82
109
  |:---------------------:|:-----:|:-----:|:-----:|:-----:|
@@ -124,6 +151,61 @@ By default, fields are assumed to be of type ```:string```. Other built-in types
124
151
  If built-in types do not suit you, you can use a custom field type represented by an arbitrary class, provided that the class supports a compatible serialization interface.
125
152
  The primary use case for using a custom field type is to represent your business logic with high-level types, while ensuring portability or backward-compatibility of the serialized representation.
126
153
 
154
+ #### Note on boolean type
155
+
156
+ The boolean fields are stored as `"t", "f"` strings by default. DynamoDB
157
+ supports boolean type natively. So if you want to use native boolean
158
+ type or already have table with native boolean attribute you can easily
159
+ achieve this with `store_as_native_boolean` option:
160
+
161
+ ```ruby
162
+ class Document
163
+ include DynamoId::Document
164
+
165
+ field :active, :boolean, store_as_native_boolean: true
166
+ end
167
+ ```
168
+
169
+ #### Note on date type
170
+
171
+ By default date fields are persisted as days count since 1 January 1970 like UNIX time. If you prefer dates to be stored as ISO-8601 formatted strings instead then set `store_as_string` to `true`
172
+
173
+ ```ruby
174
+ class Document
175
+ include DynamoId::Document
176
+
177
+ field :sent_at, :datetime, store_as_string: true
178
+ end
179
+ ```
180
+
181
+ #### Note on datetime type
182
+
183
+ By default datetime fields are persisted as UNIX timestamps with milisecond precission in DynamoDB. If you prefer datetimes to be stored as ISO-8601 formatted strings instead then set `store_as_string` to `true`
184
+
185
+ ```ruby
186
+ class Document
187
+ include DynamoId::Document
188
+
189
+ field :sent_at, :datetime, store_as_string: true
190
+ end
191
+ ```
192
+
193
+ ### Note on set type
194
+
195
+ There is `of` option to declare the type of set elements. You can use
196
+ `:integer` value only
197
+
198
+ ```ruby
199
+ class Document
200
+ include DynamoId::Document
201
+
202
+ field :tags, :set, of: :integer
203
+ end
204
+ ```
205
+
206
+
207
+ #### Magic Columns
208
+
127
209
  You get magic columns of id (string), created_at (datetime), and updated_at (datetime) for free.
128
210
 
129
211
  ```ruby
@@ -140,6 +222,8 @@ class User
140
222
  end
141
223
  ```
142
224
 
225
+ #### Default Values
226
+
143
227
  You can optionally set a default value on a field using either a plain value or a lambda:
144
228
 
145
229
  ```ruby
@@ -147,6 +231,8 @@ You can optionally set a default value on a field using either a plain value or
147
231
  field :joined_at, :datetime, {default: ->(){Time.now}}
148
232
  ```
149
233
 
234
+ #### Custom Types
235
+
150
236
  To use a custom type for a field, suppose you have a `Money` type.
151
237
 
152
238
  ```ruby
@@ -159,7 +245,7 @@ To use a custom type for a field, suppose you have a `Money` type.
159
245
 
160
246
  def self.dynamoid_load(serialized_str)
161
247
  # parse serialized representation and return a Money instance
162
- Money.new(...)
248
+ Money.new(1.23)
163
249
  end
164
250
  end
165
251
 
@@ -181,7 +267,7 @@ add a level of indirection for serializing.) Example:
181
267
 
182
268
  class MoneyAdapter
183
269
  def self.dynamoid_load(money_serialized_str)
184
- Money.new(...)
270
+ Money.new(1.23)
185
271
  end
186
272
 
187
273
  def self.dynamoid_dump(money_obj)
@@ -201,8 +287,8 @@ This is especially important if you want to use your custom field as a numeric r
201
287
  number-oriented queries. By default custom fields are persisted as a string attribute, but
202
288
  your custom class can override this with a `.dynamoid_field_type` class method, which would
203
289
  return either `:string` or `:number`.
204
- (DynamoDB supports some other attribute types, but Dynamoid does not yet.)
205
290
 
291
+ DynamoDB may support some other attribute types that are not yet supported by Dynamoid.
206
292
 
207
293
  ### Associations
208
294
 
@@ -214,12 +300,13 @@ The only supported associations (so far) are ```has_many```, ```has_one```, ```h
214
300
  class User
215
301
  include Dynamoid::Document
216
302
 
217
- ...
303
+ # ...
218
304
 
219
305
  has_many :addresses
220
306
  has_many :students, :class => User
221
307
  belongs_to :teacher, :class_name => :user
222
308
  belongs_to :group
309
+ belongs_to :group, :foreign_key => :group_id
223
310
  has_one :role
224
311
  has_and_belongs_to_many :friends, :inverse_of => :friending_users
225
312
 
@@ -228,7 +315,7 @@ end
228
315
  class Address
229
316
  include Dynamoid::Document
230
317
 
231
- ...
318
+ # ...
232
319
 
233
320
  belongs_to :user # Automatically links up with the user model
234
321
 
@@ -245,7 +332,7 @@ Dynamoid bakes in ActiveModel validations, just like ActiveRecord does.
245
332
  class User
246
333
  include Dynamoid::Document
247
334
 
248
- ...
335
+ # ...
249
336
 
250
337
  validates_presence_of :name
251
338
  validates_format_of :email, :with => /@/
@@ -268,7 +355,7 @@ Dynamoid also employs ActiveModel callbacks. Right now, callbacks are defined on
268
355
  class User
269
356
  include Dynamoid::Document
270
357
 
271
- ...
358
+ # ...
272
359
 
273
360
  before_save :set_default_password
274
361
  after_create :notify_friends
@@ -325,6 +412,19 @@ address.city = 'Chicago'
325
412
  address.save
326
413
  ```
327
414
 
415
+ To create multiple documents at once:
416
+
417
+ ```ruby
418
+ User.create([{name: 'Josh'}, {name: 'Nick'}])
419
+ ```
420
+
421
+ There is an efficient and low-level way to create multiple documents
422
+ (without validation and callbacks running):
423
+
424
+ ```ruby
425
+ users = User.import([{name: 'Josh'}, {name: 'Nick'}])
426
+ ```
427
+
328
428
  ### Querying
329
429
 
330
430
  Querying can be done in one of three ways:
@@ -354,11 +454,13 @@ There are three types of limits that you can query with:
354
454
  Using these in various combinations results in the underlying requests to be made in the smallest size possible and
355
455
  the query returns once `record_limit` or `scan_limit` is satisfied. It will attempt to batch whenever possible.
356
456
 
357
- You can thus limit the number of evaluated records, or select a record from which to start, to support pagination:
457
+ You can thus limit the number of evaluated records, or select a record from which to start in order to support pagination.
358
458
 
359
459
  ```ruby
360
460
  Address.record_limit(5).start(address) # Only 5 addresses starting at `address`
361
461
  ```
462
+ Where `address` is an instance of the model or a hash `{the_model_hash_key: 'value', the_model_range_key: 'value'}`:
463
+ Keep in mind that if you are passing a hash to `.start()` you need to explicitly define all required keys in it including range keys, depending on table or secondary indexes signatures, otherwise you'll get an `Aws::DynamoDB::Errors::ValidationException` either for `Exclusive Start Key must have same size as table's key schema` or `The provided starting key is invalid`
362
464
 
363
465
  If you are potentially running over a large data set and this is especially true when using certain filters, you may
364
466
  want to consider limiting the number of scanned records (the number of records DynamoDB infrastructure looks through
@@ -386,21 +488,21 @@ You are able to optimize query with condition for sort key. Following operators
386
488
 
387
489
  ```ruby
388
490
  Address.where(latitude: 10212)
389
- Address.where('latitude.gt': 10212)
390
- Address.where('latitude.lt': 10212)
391
- Address.where('latitude.gte': 10212)
392
- Address.where('latitude.lte': 10212)
393
- Address.where('city.begins_with': 'Lon')
394
- Address.where('latitude.between': [10212, 20000])
491
+ Address.where('latitude.gt' => 10212)
492
+ Address.where('latitude.lt' => 10212)
493
+ Address.where('latitude.gte' => 10212)
494
+ Address.where('latitude.lte' => 10212)
495
+ Address.where('city.begins_with' => 'Lon')
496
+ Address.where('latitude.between' => [10212, 20000])
395
497
  ```
396
498
 
397
499
  You are able to filter results on the DynamoDB side and specify conditions for non-key fields.
398
500
  Following operators are available: `in`, `contains`, `not_contains`:
399
501
 
400
502
  ```ruby
401
- Address.where('city.in': ['London', 'Edenburg', 'Birmingham'])
402
- Address.where('city.contains': [on])
403
- Address.where('city.not_contains': [ing])
503
+ Address.where('city.in' => ['London', 'Edenburg', 'Birmingham'])
504
+ Address.where('city.contains' => [on])
505
+ Address.where('city.not_contains' => [ing])
404
506
  ```
405
507
 
406
508
  ### Consistent Reads
@@ -423,8 +525,77 @@ User.where("created_at.lt" => DateTime.now - 1.day).all
423
525
 
424
526
  It also supports .gte and .lte. Turning those into symbols and allowing a Rails SQL-style string syntax is in the works. You can only have one range argument per query, because of DynamoDB's inherent limitations, so use it sensibly!
425
527
 
528
+
529
+ ### Updating
530
+
531
+ In order to update document you can use high level methods
532
+ `#update_attributes`, `#update_attribute` and `.update`.
533
+ They run validation and collbacks.
534
+
535
+ ```ruby
536
+ Address.find(id).update_attributes(city: 'Chicago')
537
+ Address.find(id).update_attribute(city, 'Chicago')
538
+ Address.update(id, city: 'Chicago')
539
+ Address.update(id, { city: 'Chicago' }, if: { deliverable: true })
540
+ ```
541
+
542
+ There are also some low level methods `#update`, `.update_fields` and
543
+ `.upsert`. They don't run validation and callbacks (except `#update` - it
544
+ runs `update` callbacks). All of them support conditional updates.
545
+ `#upsert` will create new document if document with specified `id`
546
+ doesn't exist.
547
+
548
+ ```ruby
549
+ Adderess.find(id).update do |i|
550
+ i.set city: 'Chicago'
551
+ i.add latitude: 100
552
+ i.delete set_of_numbers: 10
553
+ end
554
+ Adderess.find(id).update(if: { deliverable: true }) do |i|
555
+ i.set city: 'Chicago'
556
+ end
557
+ Address.update_fields(id, city: 'Chicago')
558
+ Address.update_fields(id, { city: 'Chicago' }, if: { deliverable: true })
559
+ Address.upsert(id, city: 'Chicago')
560
+ Address.upsert(id, { city: 'Chicago' }, if: { deliverable: true })
561
+ ```
562
+
563
+ ### Deleting
564
+
565
+ In order to delete some items `delete_all` method should be used.
566
+ Any callback wont be called. Items delete in efficient way in batch.
567
+
568
+ ```ruby
569
+ Address.where(city: "London").delete_all
570
+ ```
571
+
426
572
  ### Global Secondary Indexes
427
573
 
574
+ You can define index with `global_secondary_index`:
575
+
576
+ ```ruby
577
+ class User
578
+ include Dynamoid::Document
579
+
580
+ field :name
581
+ field :age, :number
582
+
583
+ global_secondary_index hash_key: :age
584
+ end
585
+ ```
586
+
587
+ There are following options:
588
+ * `hash_key` - is used as hash key of an index,
589
+ * `range_key` - is used as range key of an index,
590
+ * `projected_attributes` - list of fields to store in an index or has a predefiled value `:keys_only`, `:all`; `:keys_only` is a default,
591
+ * `name` - an index will be created with this name when a table is created; by default name is generated and contains table name and keys names,
592
+ * `read_capacity` - is used when table creates and used as an index capacity; by default equals `Dynamoid::Config.read_capacity`,
593
+ * `write_capacity` - is used when table creates and used as an index capacity; by default equals `Dynamoid::Config.write_capacity`
594
+
595
+ The only mandatory option is `name`.
596
+
597
+ To use index in `Document.where` implicitly you need to project all the fields with option `projected_attributes: :all`.
598
+
428
599
  There are two ways to query Global Secondary Indexes (GSI).
429
600
 
430
601
  #### Explicit
@@ -485,7 +656,7 @@ the table since a query against GSI then a query on base table is still likely f
485
656
 
486
657
  ## Configuration
487
658
 
488
- There are listed all the configuration options:
659
+ Listed below are all configuration options.
489
660
 
490
661
  * `adapter` - usefull only for the gem developers to switch to a new adapter. Default and the only available value is `aws_sdk_v2`
491
662
  * `namespace` - prefix for table names, default is `dynamoid_#{application_name}_#{environment}` for Rails application and `dynamoid` otherwise
@@ -504,9 +675,13 @@ There are listed all the configuration options:
504
675
  * `sync_retry_max_times` - when Dynamoid creates or deletes table synchronously it checks for completion specified times. Default is 60 (times). It's a bit over 2 minutes by default
505
676
  * `sync_retry_wait_seconds` - time to wait between retries. Default is 2 (seconds)
506
677
  * `convert_big_decimal` - if `true` then Dynamoid converts numbers stored in `Hash` in `raw` field to float. Default is `false`
507
- * `models_dir` - `dynamoid:create_tables` rake task loads DynamoDb models from this directory. Default is `app/models`. In Rails application you should set `./app/models` value
678
+ * `models_dir` - `dynamoid:create_tables` rake task loads DynamoDb models from this directory. Default is `./app/models`.
508
679
  * `application_timezone` - Dynamoid converts all `datetime` fields to specified time zone when loads data from the storage.
509
680
  Acceptable values - `utc`, `local` (to use system time zone) and time zone name e.g. `Eastern Time (US & Canada)`. Default is `local`
681
+ * `store_datetime_as_string` - if `true` then Dynamoid stores :datetime fields in ISO 8601 string format. Default is `false`
682
+ * `store_date_as_string` - if `true` then Dynamoid stores :date fields in ISO 8601 string format. Default is `false`
683
+ * `backoff` - is a hash: key is a backoff strategy (symbol), value is parameters for the strategy. Is used in batch operations. Default id `nil`
684
+ * `backoff_strategies`: is a hash and contains all available strategies. Default is { constant: ..., exponential: ...}
510
685
 
511
686
 
512
687
  ## Concurrency
@@ -515,11 +690,11 @@ Dynamoid supports basic, ActiveRecord-like optimistic locking on save operations
515
690
 
516
691
  ```ruby
517
692
  class MyTable
518
- ...
693
+ # ...
519
694
 
520
695
  field :lock_version, :integer
521
696
 
522
- ...
697
+ # ...
523
698
  end
524
699
  ```
525
700
 
@@ -527,6 +702,52 @@ In this example, all saves to `MyTable` will raise an `Dynamoid::Errors::StaleOb
527
702
 
528
703
  Calls to `update` and `update!` also increment the `lock_version`, however they do not check the existing value. This guarantees that a update operation will raise an exception in a concurrent save operation, however a save operation will never cause an update to fail. Thus, `update` is useful & safe only for doing atomic operations (e.g. increment a value, add/remove from a set, etc), but should not be used in a read-modify-write pattern.
529
704
 
705
+
706
+ ### Backoff strategies
707
+
708
+
709
+ You can use several methods that run efficiently in batch mode like `.find_all` and `.import`.
710
+
711
+ The backoff strategy will be used when, for any reason, some items could not be processed as part of a batch mode command.
712
+ Operations will be re-run to process these items.
713
+
714
+ Exponential backoff is the recommended way to handle throughput limits exceeding and throttling on the table.
715
+
716
+ There are two built-in strategies - constant delay and truncated binary exponential backoff.
717
+ By default no backoff is used but you can specify one of the built-in ones:
718
+
719
+ ```ruby
720
+ Dynamoid.configure do |config|
721
+ config.backoff = { constant: 2.second }
722
+ end
723
+
724
+ Dynamoid.configure do |config|
725
+ config.backoff = { exponential: { base_backoff: 0.2.seconds, ceiling: 10 } }
726
+ end
727
+
728
+ ```
729
+
730
+ You can just specify strategy without any arguments to use default presets:
731
+
732
+ ```ruby
733
+ Dynamoid.configure do |config|
734
+ config.backoff = :constant
735
+ end
736
+ ```
737
+
738
+ You can use your own strategy in following way:
739
+
740
+ ```ruby
741
+ Dynamoid.configure do |config|
742
+ config.backoff_strategies[:custom] = lambda do |n|
743
+ -> { sleep rand(n) }
744
+ end
745
+
746
+ config.backoff = { custom: 10 }
747
+ end
748
+ ```
749
+
750
+
530
751
  ## Rake Tasks
531
752
 
532
753
  * `rake dynamoid:create_tables`
@@ -593,14 +814,15 @@ Also, without contributors the project wouldn't be nearly as awesome. So many th
593
814
  * [Pascal Corpet](https://github.com/pcorpet)
594
815
  * [Brian Glusman](https://github.com/bglusman) *
595
816
  * [Peter Boling](https://github.com/pboling) *
817
+ * [Andrew Konchin](https://github.com/andrykonchin) *
596
818
 
597
819
  \* Current Maintainers
598
820
 
599
821
  ## Running the tests
600
822
 
601
- Running the tests is fairly simple. You should have an instance of DynamoDB running locally. Follow this steps to be able to run the tests:
823
+ Running the tests is fairly simple. You should have an instance of DynamoDB running locally. Follow these steps to setup your test environment.
602
824
 
603
- * First download and unpack the latest version of DynamoDB.
825
+ * First download and unpack the latest version of DynamoDB. We have a script that will do this for you if you use homebrew on a Mac.
604
826
 
605
827
  ```shell
606
828
  bin/setup
@@ -624,7 +846,7 @@ Running the tests is fairly simple. You should have an instance of DynamoDB runn
624
846
  bin/stop_dynamodblocal
625
847
  ```
626
848
 
627
- If you want to run all the specs that travis runs, use `bundle exec wwtd`, but first you will need to setup all the rubies, for each of `%w( 2.0.0-p648 2.1.10 2.2.6 2.3.3 2.4.1 jruby-9.1.8.0 )`. WHen you run `bundle exec wwtd` it will take care of starting and stopping the local dynamodb instance.
849
+ If you want to run all the specs that travis runs, use `bundle exec wwtd`, but first you will need to setup all the rubies, for each of `%w( 2.0.0-p648 2.1.10 2.2.6 2.3.3 2.4.1 jruby-9.1.8.0 )`. When you run `bundle exec wwtd` it will take care of starting and stopping the local dynamodb instance.
628
850
 
629
851
  ```shell
630
852
  rvm use 2.0.0-p648
@@ -633,9 +855,6 @@ gem install bundler
633
855
  bundle install
634
856
  ```
635
857
 
636
- [![Build Status](https://travis-ci.org/Dynamoid/Dynamoid.svg)](https://travis-ci.org/Dynamoid/Dynamoid)
637
- [![Coverage Status](https://coveralls.io/repos/Dynamoid/Dynamoid/badge.svg?branch=master&service=github)](https://coveralls.io/github/Dynamoid/Dynamoid?branch=master)
638
-
639
858
  ## Copyright
640
859
 
641
860
  Copyright (c) 2012 Josh Symonds.