dynamoid 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +53 -0
  3. data/.rubocop_todo.yml +55 -0
  4. data/.travis.yml +5 -27
  5. data/Appraisals +17 -15
  6. data/CHANGELOG.md +26 -3
  7. data/Gemfile +4 -2
  8. data/README.md +95 -77
  9. data/Rakefile +17 -17
  10. data/Vagrantfile +5 -3
  11. data/dynamoid.gemspec +39 -45
  12. data/gemfiles/rails_4_2.gemfile +7 -5
  13. data/gemfiles/rails_5_0.gemfile +6 -4
  14. data/gemfiles/rails_5_1.gemfile +6 -4
  15. data/gemfiles/rails_5_2.gemfile +6 -4
  16. data/lib/dynamoid.rb +11 -4
  17. data/lib/dynamoid/adapter.rb +21 -27
  18. data/lib/dynamoid/adapter_plugin/{aws_sdk_v2.rb → aws_sdk_v3.rb} +118 -113
  19. data/lib/dynamoid/application_time_zone.rb +27 -0
  20. data/lib/dynamoid/associations.rb +3 -6
  21. data/lib/dynamoid/associations/association.rb +3 -6
  22. data/lib/dynamoid/associations/belongs_to.rb +4 -5
  23. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +2 -3
  24. data/lib/dynamoid/associations/has_many.rb +2 -3
  25. data/lib/dynamoid/associations/has_one.rb +2 -3
  26. data/lib/dynamoid/associations/many_association.rb +8 -9
  27. data/lib/dynamoid/associations/single_association.rb +3 -3
  28. data/lib/dynamoid/components.rb +2 -2
  29. data/lib/dynamoid/config.rb +9 -5
  30. data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +4 -2
  31. data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +3 -1
  32. data/lib/dynamoid/config/options.rb +4 -4
  33. data/lib/dynamoid/criteria.rb +3 -5
  34. data/lib/dynamoid/criteria/chain.rb +42 -49
  35. data/lib/dynamoid/dirty.rb +5 -4
  36. data/lib/dynamoid/document.rb +142 -36
  37. data/lib/dynamoid/dumping.rb +167 -0
  38. data/lib/dynamoid/dynamodb_time_zone.rb +16 -0
  39. data/lib/dynamoid/errors.rb +7 -6
  40. data/lib/dynamoid/fields.rb +24 -23
  41. data/lib/dynamoid/finders.rb +101 -59
  42. data/lib/dynamoid/identity_map.rb +5 -11
  43. data/lib/dynamoid/indexes.rb +45 -46
  44. data/lib/dynamoid/middleware/identity_map.rb +2 -0
  45. data/lib/dynamoid/persistence.rb +67 -307
  46. data/lib/dynamoid/primary_key_type_mapping.rb +34 -0
  47. data/lib/dynamoid/railtie.rb +3 -1
  48. data/lib/dynamoid/tasks/database.rake +11 -11
  49. data/lib/dynamoid/tasks/database.rb +4 -3
  50. data/lib/dynamoid/type_casting.rb +193 -0
  51. data/lib/dynamoid/undumping.rb +188 -0
  52. data/lib/dynamoid/validations.rb +4 -7
  53. data/lib/dynamoid/version.rb +3 -1
  54. metadata +59 -53
  55. data/gemfiles/rails_4_0.gemfile +0 -9
  56. data/gemfiles/rails_4_1.gemfile +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 542fc1c696af080fcd65a7428ec553584d3fe48d
4
- data.tar.gz: 93cca063db742ea205238443dcf98ab8e03ba627
3
+ metadata.gz: 9cb9dd0de78473763541261beed167d81d2431b1
4
+ data.tar.gz: 057eea674112a29e69a55794a2815f2af8b14a7d
5
5
  SHA512:
6
- metadata.gz: 438f005ef464d1f90f8e6ba57b3c1b2969e183828e2ad35dcb9fd5db00dea9bc394684ad1787c4c61c6ad23f8a879a933b8a18be3254fc730bc74c1d39b574e9
7
- data.tar.gz: 905e8db4276d80e280fd95fb56902da845a0aa0eb496c4251d67deff3200436a83fada31f23af341fe490e4576956c9db7e5f9137d58ac53f68e7505addae7d1
6
+ metadata.gz: 2b231fdf9a13c129634234b3e01b587bbe52594f4063f28e9b682f55c2941db8c74ffc0d1da72c79911eff228443032c062c8f4430960872f969bdcd5583e2fe
7
+ data.tar.gz: 692db4e23c132dd5fb4266349c552f0e2561396f1443306380c1a3ae6d775b6a3a289720f3a26619f54332c08a0b0af07c6b9f977a289effb90d01678212fd9b
@@ -0,0 +1,53 @@
1
+ # We chose not to make these changes
2
+ inherit_from: .rubocop_todo.yml
3
+
4
+ # It's the lowest supported Ruby version
5
+ AllCops:
6
+ TargetRubyVersion: 2.3
7
+
8
+ # It's a matter of taste
9
+ Layout/AlignParameters:
10
+ EnforcedStyle: with_fixed_indentation
11
+ Style/GuardClause:
12
+ Enabled: false
13
+ Style/FormatStringToken:
14
+ Enabled: false
15
+ Style/DoubleNegation:
16
+ Enabled: false
17
+
18
+ # We aren't so brave to tackle all these issues right now
19
+ Metrics/LineLength:
20
+ Enabled: false
21
+ Metrics/BlockLength:
22
+ Enabled: false
23
+ Metrics/MethodLength:
24
+ Enabled: false
25
+ Metrics/CyclomaticComplexity:
26
+ Enabled: false
27
+ Metrics/AbcSize:
28
+ Enabled: false
29
+ Metrics/ModuleLength:
30
+ Enabled: false
31
+ Metrics/BlockNesting:
32
+ Enabled: false
33
+ Metrics/PerceivedComplexity:
34
+ Enabled: false
35
+ Metrics/ClassLength:
36
+ Enabled: false
37
+
38
+ # Minor annoying issues
39
+ Lint/UselessAssignment:
40
+ Enabled: false
41
+ Lint/AmbiguousBlockAssociation:
42
+ Enabled: false
43
+ Lint/AssignmentInCondition:
44
+ Enabled: false
45
+ Style/Documentation:
46
+ Enabled: false
47
+ Style/DateTime:
48
+ Enabled: false
49
+ Style/MissingRespondToMissing:
50
+ Enabled: false
51
+ Naming/PredicateName:
52
+ Enabled: false
53
+
@@ -0,0 +1,55 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-06-18 22:26:49 +0300 using RuboCop version 0.57.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Lint/HandleExceptions:
11
+ Exclude:
12
+ - 'lib/dynamoid/document.rb'
13
+
14
+ # Offense count: 1
15
+ # Cop supports --auto-correct.
16
+ Security/YAMLLoad:
17
+ Exclude:
18
+ - 'lib/dynamoid/persistence.rb'
19
+
20
+ # Offense count: 1
21
+ # Cop supports --auto-correct.
22
+ # Configuration parameters: EnforcedStyle.
23
+ # SupportedStyles: braces, no_braces, context_dependent
24
+ Style/BracesAroundHashParameters:
25
+ Exclude:
26
+ - 'spec/dynamoid/adapter_plugin/aws_sdk_v3_spec.rb'
27
+
28
+ # Offense count: 2
29
+ # Cop supports --auto-correct.
30
+ # Configuration parameters: EnforcedStyle.
31
+ # SupportedStyles: module_function, extend_self
32
+ Style/ModuleFunction:
33
+ Exclude:
34
+ - 'lib/dynamoid.rb'
35
+ - 'lib/dynamoid/config.rb'
36
+
37
+ # Offense count: 1
38
+ Style/OptionalArguments:
39
+ Exclude:
40
+ - 'lib/dynamoid/document.rb'
41
+
42
+ # Offense count: 1
43
+ # Cop supports --auto-correct.
44
+ # Configuration parameters: AllowAsExpressionSeparator.
45
+ Style/Semicolon:
46
+ Exclude:
47
+ - 'spec/dynamoid/adapter_plugin/aws_sdk_v3_spec.rb'
48
+
49
+ # Offense count: 1
50
+ # Cop supports --auto-correct.
51
+ # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
52
+ # Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
53
+ Style/TrivialAccessors:
54
+ Exclude:
55
+ - 'lib/dynamoid/adapter_plugin/aws_sdk_v3.rb'
@@ -2,38 +2,16 @@ sudo: required
2
2
 
3
3
  language: ruby
4
4
  rvm:
5
- - ruby-2.0.0-p648
6
- - ruby-2.1.10
7
- - ruby-2.2.7
8
- - ruby-2.3.4
9
- - ruby-2.4.1
10
- - jruby-9.1.9.0
5
+ - ruby-2.3.7
6
+ - ruby-2.4.4
7
+ - ruby-2.5.1
8
+ - jruby-9.1.17.0
11
9
  gemfile:
12
10
  gemfile:
13
- - gemfiles/rails_4_0.gemfile
14
- - gemfiles/rails_4_1.gemfile
15
11
  - gemfiles/rails_4_2.gemfile
16
12
  - gemfiles/rails_5_0.gemfile
17
13
  - gemfiles/rails_5_1.gemfile
18
14
  - gemfiles/rails_5_2.gemfile
19
- matrix:
20
- exclude:
21
- - rvm: ruby-2.0.0-p648
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
27
- - rvm: ruby-2.1.10
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
33
- - rvm: ruby-2.4.1
34
- gemfile: gemfiles/rails_4_0.gemfile
35
- - rvm: ruby-2.4.1
36
- gemfile: gemfiles/rails_4_1.gemfile
37
15
 
38
16
  ### BUILD LIFECYCLE STEPS ###
39
17
 
@@ -47,7 +25,7 @@ before_install:
47
25
  - docker ps
48
26
 
49
27
  after_install:
50
- - gem install bundler -v 1.15.4
28
+ - gem install bundler -v 1.16.2
51
29
  - bundle install
52
30
 
53
31
  before_script:
data/Appraisals CHANGED
@@ -1,26 +1,28 @@
1
- appraise "rails-4-0" do
2
- gem "rails", "~> 4.0.0"
3
- gem "nokogiri", "~> 1.6.8" # can be removed once we drop support for Ruby 2.0.0
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'rails-4-0' do
4
+ gem 'rails', '~> 4.0.0'
5
+ gem 'nokogiri', '~> 1.6.8' # can be removed once we drop support for Ruby 2.0.0
4
6
  end
5
7
 
6
- appraise "rails-4-1" do
7
- gem "rails", "~> 4.1.0"
8
- gem "nokogiri", "~> 1.6.8" # can be removed once we drop support for Ruby 2.0.0
8
+ appraise 'rails-4-1' do
9
+ gem 'rails', '~> 4.1.0'
10
+ gem 'nokogiri', '~> 1.6.8' # can be removed once we drop support for Ruby 2.0.0
9
11
  end
10
12
 
11
- appraise "rails-4-2" do
12
- gem "rails", "~> 4.2.0"
13
- gem "nokogiri", "~> 1.6.8" # can be removed once we drop support for Ruby 2.0.0
13
+ appraise 'rails-4-2' do
14
+ gem 'rails', '~> 4.2.0'
15
+ gem 'nokogiri', '~> 1.6.8' # can be removed once we drop support for Ruby 2.0.0
14
16
  end
15
17
 
16
- appraise "rails-5-0" do
17
- gem "rails", "~> 5.0.0"
18
+ appraise 'rails-5-0' do
19
+ gem 'rails', '~> 5.0.0'
18
20
  end
19
21
 
20
- appraise "rails-5-1" do
21
- gem "rails", "~> 5.1.0"
22
+ appraise 'rails-5-1' do
23
+ gem 'rails', '~> 5.1.0'
22
24
  end
23
25
 
24
- appraise "rails-5-2" do
25
- gem "rails", "~> 5.2.0"
26
+ appraise 'rails-5-2' do
27
+ gem 'rails', '~> 5.2.0'
26
28
  end
@@ -2,15 +2,38 @@
2
2
 
3
3
  ## Breaking
4
4
 
5
- * N/A
5
+ ## Improvements
6
+
7
+ ## Fixes
8
+
9
+
10
+ # 3.0.0
11
+
12
+ ## Breaking
13
+
14
+ * Maintenance: [#267](https://github.com/Dynamoid/dynamoid/pull/267) Upgrade AWS SDK to V3
15
+ * Maintenance: [#268](https://github.com/Dynamoid/dynamoid/pull/268) Drop support of old Ruby versions. Support Ruby since 2.3 version
16
+ * Maintenance: [#268](https://github.com/Dynamoid/dynamoid/pull/268) Drop support of old Rails versions. Support Rails since 4.2 version
17
+ * Improvement: [#278](https://github.com/Dynamoid/dynamoid/pull/278) Add type casting for finders (`find`, `find_by_id` and `find_all`)
18
+ * Improvement: [#279](https://github.com/Dynamoid/dynamoid/pull/279) Change default value of `application_timezone` config option from `:local` to `:utc`
19
+ * Feature: [#288](https://github.com/Dynamoid/dynamoid/pull/288) Add `store_boolean_as_native` config option and set it to `true` by default. So all boolean fields are stored not as string `'t'` and `'f'` but as native boolean values now
20
+ * Feature: [#289](https://github.com/Dynamoid/dynamoid/pull/289) Add `dynamodb_timezone` config option and set it to `:utc` by default. So now all `date` and `datetime` fields stored in string format will be converted to UTC time zone by default
6
21
 
7
22
  ## Improvements
8
23
 
9
- * N/A
24
+ * Improvement: [#261](https://github.com/Dynamoid/Dynamoid/pull/261) Improve documentation (@walkersumida)
25
+ * Improvement: [#264](https://github.com/Dynamoid/Dynamoid/pull/264) Improve documentation (@xbx)
26
+ * Improvement: [#278](https://github.com/Dynamoid/Dynamoid/pull/278) Add Rails-like type casting
27
+ * Maintenance: [#281](https://github.com/Dynamoid/Dynamoid/pull/281) Deprecate dynamic finders, `find_all`, `find_by_id`, `find_by_composite_key`, `find_all_by_composite_key` and `find_all_by_secondary_index`
28
+ * Improvement: [#285](https://github.com/Dynamoid/Dynamoid/pull/285) Set timestamps (`created_at` and `updated_at`) in `upsert`, `update_fields`, `import` and `update` methods
29
+ * Improvement: [#286](https://github.com/Dynamoid/Dynamoid/pull/286) Disable scan warning when intentionally loading all items from a collection (@knovoselic)
10
30
 
11
31
  ## Fixes
12
32
 
13
- * N/A
33
+ * Bug: [#275](https://github.com/Dynamoid/Dynamoid/pull/275) Fix custom type serialization/deserialization
34
+ * Bug: [#283](https://github.com/Dynamoid/Dynamoid/pull/283) Fix using string formats for partition and sort keys of `date`/`datetime` type
35
+ * Bug: [#283](https://github.com/Dynamoid/Dynamoid/pull/283) Fix type declaration of custom type fields. Returned by `.dynamoid_field_type` value is treated as Dynamoid's type now
36
+ * Bug: [#287](https://github.com/Dynamoid/Dynamoid/pull/287) Fix logging disabling (@ghiculescu)
14
37
 
15
38
  # 2.2.0
16
39
 
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in dynamoid.gemspec
4
6
  gemspec
5
7
 
6
- gem "pry-byebug", platforms: :ruby
8
+ gem 'pry-byebug', platforms: :ruby
data/README.md CHANGED
@@ -23,11 +23,11 @@ But if you want a fast, scalable, simple, easy-to-use database (and a Gem that s
23
23
  | version | [![Gem Version](https://badge.fury.io/rb/dynamoid.svg)](https://rubygems.org/gems/dynamoid) |
24
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
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) |
26
+ | continuous integration | [![Build Status](https://travis-ci.org/Dynamoid/dynamoid.svg?branch=master)](https://travis-ci.org/Dynamoid/dynamoid) |
27
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
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) |
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) |
31
31
 
32
32
  ## Installation
33
33
 
@@ -38,17 +38,9 @@ gem 'dynamoid', '~> 2'
38
38
  ```
39
39
  ## Prerequisities
40
40
 
41
- 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 (~> 3), rails (>= 4).
42
42
  Hence the configuration as needed for aws to work will be dealt with by aws setup.
43
43
 
44
- Here are the steps to setup aws-sdk.
45
-
46
- ```ruby
47
- gem 'aws-sdk', '~>2'
48
- ```
49
-
50
- (or) include the aws-sdk in your Gemfile.
51
-
52
44
  ### AWS SDK Version Compatibility
53
45
 
54
46
  Make sure you are using the version for the right AWS SDK.
@@ -58,7 +50,7 @@ Make sure you are using the version for the right AWS SDK.
58
50
  | 0.x | 1.x |
59
51
  | 1.x | 2.x |
60
52
  | 2.x | 2.x |
61
- | 3.x (unreleased) | 3.x |
53
+ | 3.x | 3.x |
62
54
 
63
55
  ### AWS Configuration
64
56
 
@@ -67,7 +59,7 @@ Configure AWS access:
67
59
 
68
60
  For example, to configure AWS access:
69
61
 
70
- Create config/initializers/aws.rb as follows:
62
+ Create `config/initializers/aws.rb` as follows:
71
63
 
72
64
  ```ruby
73
65
 
@@ -97,28 +89,27 @@ Then you need to initialize Dynamoid config to get it going. Put code similar to
97
89
  ```ruby
98
90
  require 'dynamoid'
99
91
  Dynamoid.configure do |config|
100
- config.namespace = "dynamoid_app_development" # To namespace tables created by Dynamoid from other tables you might have. Set to nil to avoid namespacing.
92
+ config.namespace = 'dynamoid_app_development' # To namespace tables created by Dynamoid from other tables you might have. Set to nil to avoid namespacing.
101
93
  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).
102
94
  end
103
-
104
95
  ```
105
96
 
106
- ### Ruby & Rails Compatibility Matrix
97
+ ### Ruby & Rails Compatibility
107
98
 
108
- | Ruby / Active Record | 4.0.x | 4.1.x | 4.2.x | 5.0.x |
99
+ Dynamoid supports Ruby >= 2.3 and Rails >= 4.2.
100
+
101
+ Its compatibility is tested in following way:
102
+
103
+ | Ruby / Active Record | 4.2.x | 5.0.x | 5.1.x | 5.2.x |
109
104
  |:---------------------:|:-----:|:-----:|:-----:|:-----:|
110
- | 2.0.0 | ✓ | ✓ | ✓ | |
111
- | 2.1.x | ✓ | ✓ | ✓ | |
112
- | 2.2.0-2.2.1 | ✓ | ✓ | ✓ | |
113
- | 2.2.2+ | ✓ | ✓ | ✓ | ✓ |
114
- | 2.3.x | ✓ | ✓ | ✓ | ✓ |
115
- | 2.3.x | ✓ | ✓ | ✓ | ✓ |
116
- | 2.4.x | | | ✓ | ✓ |
117
- | jruby-9.X | ✓ | ✓ | ✓ | ✓ |
105
+ | 2.3.7 | ✓ | ✓ | ✓ ||
106
+ | 2.4.4 | ✓ | ✓ | ✓ ||
107
+ | 2.5.1 | ✓ | ✓ | ✓ ||
108
+ | jruby-9.1.17.0 | ✓ | ✓ | ✓ | ✓ |
118
109
 
119
110
  ## Setup
120
111
 
121
- You *must* include ```Dynamoid::Document``` in every Dynamoid model.
112
+ You *must* include `Dynamoid::Document` in every Dynamoid model.
122
113
 
123
114
  ```ruby
124
115
  class User
@@ -135,34 +126,34 @@ Dynamoid has some sensible defaults for you when you create a new table, includi
135
126
  class User
136
127
  include Dynamoid::Document
137
128
 
138
- table :name => :awesome_users, :key => :user_id, :read_capacity => 5, :write_capacity => 5
129
+ table name: :awesome_users, key: :user_id, read_capacity: 5, write_capacity: 5
139
130
  end
140
131
  ```
141
132
 
142
- These fields will not change an existing table: so specifying a new read_capacity and write_capacity here only works correctly for entirely new tables. Similarly, while Dynamoid will look for a table named `awesome_users` in your namespace, it won't change any existing tables to use that name; and if it does find a table with the correct name, it won't change its hash key, which it expects will be user_id. If this table doesn't exist yet, however, Dynamoid will create it with these options.
133
+ These fields will not change an existing table: so specifying a new read_capacity and write_capacity here only works correctly for entirely new tables. Similarly, while Dynamoid will look for a table named `awesome_users` in your namespace, it won't change any existing tables to use that name; and if it does find a table with the correct name, it won't change its hash key, which it expects will be `user_id`. If this table doesn't exist yet, however, Dynamoid will create it with these options.
143
134
 
144
135
  ### Fields
145
136
 
146
137
  You'll have to define all the fields on the model and the data type of each field. Every field on the object must be included here; if you miss any they'll be completely bypassed during DynamoDB's initialization and will not appear on the model objects.
147
138
 
148
- By default, fields are assumed to be of type ```:string```. Other built-in types are
149
- ```:integer```, ```:number```, ```:set```, ```:array```, ```:datetime```, ```date```, ```:boolean```, ```:raw``` and ```:serialized```.
150
- ```raw``` type means you can store Ruby Array, Hash, String and numbers.
139
+ By default, fields are assumed to be of type `:string`. Other built-in types are
140
+ `:integer`, `:number`, `:set`, `:array`, `:datetime`, `date`, `:boolean`, `:raw` and `:serialized`.
141
+ `raw` type means you can store Ruby Array, Hash, String and numbers.
151
142
  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.
152
143
  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.
153
144
 
154
145
  #### Note on boolean type
155
146
 
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:
147
+ The boolean fields are stored as DynamoDB boolean values by default.
148
+ Dynamoid can store boolean values as strings as well - `'t'` and `'f'`.
149
+ So if you want to change default format of boolean field you can easily
150
+ achieve this with `store_as_native_boolean` field option:
160
151
 
161
152
  ```ruby
162
153
  class Document
163
154
  include DynamoId::Document
164
155
 
165
- field :active, :boolean, store_as_native_boolean: true
156
+ field :active, :boolean, store_as_native_boolean: false
166
157
  end
167
158
  ```
168
159
 
@@ -174,13 +165,13 @@ By default date fields are persisted as days count since 1 January 1970 like UNI
174
165
  class Document
175
166
  include DynamoId::Document
176
167
 
177
- field :sent_at, :datetime, store_as_string: true
168
+ field :sent_on, :date, store_as_string: true
178
169
  end
179
170
  ```
180
171
 
181
172
  #### Note on datetime type
182
173
 
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`
174
+ By default datetime fields are persisted as UNIX timestamps with millisecond precision in DynamoDB. If you prefer datetimes to be stored as ISO-8601 formatted strings instead then set `store_as_string` to `true`
184
175
 
185
176
  ```ruby
186
177
  class Document
@@ -190,7 +181,15 @@ class Document
190
181
  end
191
182
  ```
192
183
 
193
- ### Note on set type
184
+ WARNING: Fields in numeric format are stored with nanoseconds as a fraction part and precision could be lost.
185
+ That's why `datetime` field in numeric format shouldn't be used as a range key.
186
+
187
+ You have two options if you need to use a `datetime` field as a range key:
188
+ * string format
189
+ * store `datetime` values without milliseconds e.g. cut them
190
+ manually with `change` method - `Time.now.change(usec: 0)`
191
+
192
+ #### Note on set type
194
193
 
195
194
  There is `of` option to declare the type of set elements. You can use
196
195
  `:integer` value only
@@ -206,7 +205,7 @@ end
206
205
 
207
206
  #### Magic Columns
208
207
 
209
- You get magic columns of id (string), created_at (datetime), and updated_at (datetime) for free.
208
+ You get magic columns of `id` (`string`), `created_at` (`datetime`), and `updated_at` (`datetime`) for free.
210
209
 
211
210
  ```ruby
212
211
  class User
@@ -227,8 +226,8 @@ end
227
226
  You can optionally set a default value on a field using either a plain value or a lambda:
228
227
 
229
228
  ```ruby
230
- field :actions_taken, :integer, {default: 0}
231
- field :joined_at, :datetime, {default: ->(){Time.now}}
229
+ field :actions_taken, :integer, default: 0
230
+ field :joined_at, :datetime, default: -> { Time.now }
232
231
  ```
233
232
 
234
233
  #### Custom Types
@@ -240,7 +239,7 @@ To use a custom type for a field, suppose you have a `Money` type.
240
239
  # ... your business logic ...
241
240
 
242
241
  def dynamoid_dump
243
- "serialized representation as a string"
242
+ 'serialized representation as a string'
244
243
  end
245
244
 
246
245
  def self.dynamoid_load(serialized_str)
@@ -284,17 +283,32 @@ add a level of indirection for serializing.) Example:
284
283
 
285
284
  Lastly, you can control the data type of your custom-class-backed field at the DynamoDB level.
286
285
  This is especially important if you want to use your custom field as a numeric range or for
287
- number-oriented queries. By default custom fields are persisted as a string attribute, but
286
+ number-oriented queries. By default custom fields are persisted as a string attribute, but
288
287
  your custom class can override this with a `.dynamoid_field_type` class method, which would
289
288
  return either `:string` or `:number`.
290
289
 
291
290
  DynamoDB may support some other attribute types that are not yet supported by Dynamoid.
292
291
 
292
+ ### Sort key
293
+
294
+ Along with partition key table may have a sort key. In order to declare it in a model
295
+ `range` class method should be used:
296
+
297
+ ```ruby
298
+ class Post
299
+ include Dynamoid::Document
300
+
301
+ range :posted_at, :datetime
302
+ end
303
+ ```
304
+
305
+ Second argument, type, is optional. Default type is `string`.
306
+
293
307
  ### Associations
294
308
 
295
309
  Just like in ActiveRecord (or your other favorite ORM), Dynamoid uses associations to create links between models.
296
310
 
297
- The only supported associations (so far) are ```has_many```, ```has_one```, ```has_and_belongs_to_many```, and ```belongs_to```. Associations are very simple to create: just specify the type, the name, and then any options you'd like to pass to the association. If there's an inverse association either inferred or specified directly, Dynamoid will update both objects to point at each other.
311
+ The only supported associations (so far) are `has_many`, `has_one`, `has_and_belongs_to_many`, and `belongs_to`. Associations are very simple to create: just specify the type, the name, and then any options you'd like to pass to the association. If there's an inverse association either inferred or specified directly, Dynamoid will update both objects to point at each other.
298
312
 
299
313
  ```ruby
300
314
  class User
@@ -303,12 +317,12 @@ class User
303
317
  # ...
304
318
 
305
319
  has_many :addresses
306
- has_many :students, :class => User
307
- belongs_to :teacher, :class_name => :user
320
+ has_many :students, class: User
321
+ belongs_to :teacher, class_name: :user
308
322
  belongs_to :group
309
- belongs_to :group, :foreign_key => :group_id
323
+ belongs_to :group, foreign_key: :group_id
310
324
  has_one :role
311
- has_and_belongs_to_many :friends, :inverse_of => :friending_users
325
+ has_and_belongs_to_many :friends, inverse_of: :friending_users
312
326
 
313
327
  end
314
328
 
@@ -322,7 +336,7 @@ class Address
322
336
  end
323
337
  ```
324
338
 
325
- Contrary to what you'd expect, association information is always contained on the object specifying the association, even if it seems like the association has a foreign key. This is a side effect of DynamoDB's structure: it's very difficult to find foreign keys without an index. Usually you won't find this to be a problem, but it does mean that association methods that build new models will not work correctly -- for example, ```user.addresses.new``` returns an address that is not associated to the user. We'll be correcting this ~soon~ maybe someday, if we get a pull request.
339
+ Contrary to what you'd expect, association information is always contained on the object specifying the association, even if it seems like the association has a foreign key. This is a side effect of DynamoDB's structure: it's very difficult to find foreign keys without an index. Usually you won't find this to be a problem, but it does mean that association methods that build new models will not work correctly -- for example, `user.addresses.new` returns an address that is not associated to the user. We'll be correcting this ~soon~ maybe someday, if we get a pull request.
326
340
 
327
341
  ### Validations
328
342
 
@@ -335,7 +349,7 @@ class User
335
349
  # ...
336
350
 
337
351
  validates_presence_of :name
338
- validates_format_of :email, :with => /@/
352
+ validates_format_of :email, with: /@/
339
353
  end
340
354
  ```
341
355
 
@@ -383,7 +397,6 @@ cat = Cat.create(name: 'Morgan')
383
397
  animal = Animal.find(cat.id)
384
398
  animal.class
385
399
  #=> Cat
386
-
387
400
  ```
388
401
 
389
402
  ## Usage
@@ -393,7 +406,7 @@ animal.class
393
406
  Dynamoid's syntax is generally very similar to ActiveRecord's. Making new objects is simple:
394
407
 
395
408
  ```ruby
396
- u = User.new(:name => 'Josh')
409
+ u = User.new(name: 'Josh')
397
410
  u.email = 'josh@joshsymonds.com'
398
411
  u.save
399
412
  ```
@@ -401,7 +414,7 @@ u.save
401
414
  Save forces persistence to the datastore: a unique ID is also assigned, but it is a string and not an auto-incrementing number.
402
415
 
403
416
  ```ruby
404
- u.id # => "3a9f7216-4726-4aea-9fbc-8554ae9292cb"
417
+ u.id # => '3a9f7216-4726-4aea-9fbc-8554ae9292cb'
405
418
  ```
406
419
 
407
420
  To use associations, you use association methods very similar to ActiveRecord's:
@@ -431,14 +444,14 @@ Querying can be done in one of three ways:
431
444
 
432
445
  ```ruby
433
446
  Address.find(address.id) # Find directly by ID.
434
- Address.where(:city => 'Chicago').all # Find by any number of matching criteria... though presently only "where" is supported.
447
+ Address.where(city: 'Chicago').all # Find by any number of matching criteria... though presently only "where" is supported.
435
448
  Address.find_by_city('Chicago') # The same as above, but using ActiveRecord's older syntax.
436
449
  ```
437
450
 
438
451
  And you can also query on associations:
439
452
 
440
453
  ```ruby
441
- u.addresses.where(:city => 'Chicago').all
454
+ u.addresses.where(city: 'Chicago').all
442
455
  ```
443
456
 
444
457
  But keep in mind Dynamoid -- and document-based storage systems in general -- are not drop-in replacements for existing relational databases. The above query does not efficiently perform a conditional join, but instead finds all the user's addresses and naively filters them in Ruby. For large associations this is a performance hit compared to relational database engines.
@@ -475,7 +488,7 @@ For large queries that return many rows, Dynamoid can use AWS' support for reque
475
488
  ```ruby
476
489
  # Do some maintenance on the entire table without flooding DynamoDB
477
490
  Address.all(batch_size: 100).each { |address| address.do_some_work; sleep(0.01) }
478
- Address.record_limit(10_000).batch(100). each { … } # Batch specified as part of a chain
491
+ Address.record_limit(10_000).batch(100).each { … } # Batch specified as part of a chain
479
492
  ```
480
493
 
481
494
  The implication of batches is that the underlying requests are done in the batch sizes to make the request and responses
@@ -488,21 +501,21 @@ You are able to optimize query with condition for sort key. Following operators
488
501
 
489
502
  ```ruby
490
503
  Address.where(latitude: 10212)
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])
504
+ Address.where('latitude.gt': 10212)
505
+ Address.where('latitude.lt': 10212)
506
+ Address.where('latitude.gte': 10212)
507
+ Address.where('latitude.lte': 10212)
508
+ Address.where('city.begins_with': 'Lon')
509
+ Address.where('latitude.between': [10212, 20000])
497
510
  ```
498
511
 
499
512
  You are able to filter results on the DynamoDB side and specify conditions for non-key fields.
500
513
  Following operators are available: `in`, `contains`, `not_contains`:
501
514
 
502
515
  ```ruby
503
- Address.where('city.in' => ['London', 'Edenburg', 'Birmingham'])
504
- Address.where('city.contains' => [on])
505
- Address.where('city.not_contains' => [ing])
516
+ Address.where('city.in': ['London', 'Edenburg', 'Birmingham'])
517
+ Address.where('city.contains': ['on'])
518
+ Address.where('city.not_contains': ['ing'])
506
519
  ```
507
520
 
508
521
  ### Consistent Reads
@@ -510,8 +523,8 @@ Address.where('city.not_contains' => [ing])
510
523
  Querying supports consistent reading. By default, DynamoDB reads are eventually consistent: if you do a write and then a read immediately afterwards, the results of the previous write may not be reflected. If you need to do a consistent read (that is, you need to read the results of a write immediately) you can do so, but keep in mind that consistent reads are twice as expensive as regular reads for DynamoDB.
511
524
 
512
525
  ```ruby
513
- Address.find(address.id, :consistent_read => true) # Find an address, ensure the read is consistent.
514
- Address.where(:city => 'Chicago').consistent.all # Find all addresses where the city is Chicago, with a consistent read.
526
+ Address.find(address.id, consistent_read: true) # Find an address, ensure the read is consistent.
527
+ Address.where(city: 'Chicago').consistent.all # Find all addresses where the city is Chicago, with a consistent read.
515
528
  ```
516
529
 
517
530
  ### Range Finding
@@ -519,11 +532,11 @@ Address.where(:city => 'Chicago').consistent.all # Find all addresses where t
519
532
  If you have a range index, Dynamoid provides a number of additional other convenience methods to make your life a little easier:
520
533
 
521
534
  ```ruby
522
- User.where("created_at.gt" => DateTime.now - 1.day).all
523
- User.where("created_at.lt" => DateTime.now - 1.day).all
535
+ User.where("created_at.gt": DateTime.now - 1.day).all
536
+ User.where("created_at.lt": DateTime.now - 1.day).all
524
537
  ```
525
538
 
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!
539
+ 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!
527
540
 
528
541
 
529
542
  ### Updating
@@ -534,7 +547,7 @@ They run validation and collbacks.
534
547
 
535
548
  ```ruby
536
549
  Address.find(id).update_attributes(city: 'Chicago')
537
- Address.find(id).update_attribute(city, 'Chicago')
550
+ Address.find(id).update_attribute(:city, 'Chicago')
538
551
  Address.update(id, city: 'Chicago')
539
552
  Address.update(id, { city: 'Chicago' }, if: { deliverable: true })
540
553
  ```
@@ -566,7 +579,7 @@ In order to delete some items `delete_all` method should be used.
566
579
  Any callback wont be called. Items delete in efficient way in batch.
567
580
 
568
581
  ```ruby
569
- Address.where(city: "London").delete_all
582
+ Address.where(city: 'London').delete_all
570
583
  ```
571
584
 
572
585
  ### Global Secondary Indexes
@@ -613,7 +626,7 @@ find_all_by_secondary_index(
613
626
  },
614
627
  # false is the same as DESC in SQL (newest timestamp first)
615
628
  # true is the same as ASC in SQL (oldest timestamp first)
616
- :scan_index_forward => false # or true
629
+ scan_index_forward: false # or true
617
630
  )
618
631
  ```
619
632
 
@@ -658,7 +671,7 @@ the table since a query against GSI then a query on base table is still likely f
658
671
 
659
672
  Listed below are all configuration options.
660
673
 
661
- * `adapter` - usefull only for the gem developers to switch to a new adapter. Default and the only available value is `aws_sdk_v2`
674
+ * `adapter` - usefull only for the gem developers to switch to a new adapter. Default and the only available value is `aws_sdk_v3`
662
675
  * `namespace` - prefix for table names, default is `dynamoid_#{application_name}_#{environment}` for Rails application and `dynamoid` otherwise
663
676
  * `logger` - by default it's a `Rails.logger` in Rails application and `stdout` otherwise. You can disable logging by setting `nil` or `false` values. Set `true` value to use defaults
664
677
  * `access_key` - DynamoDb custom credentials for AWS, override global AWS credentials if they present
@@ -677,9 +690,14 @@ Listed below are all configuration options.
677
690
  * `convert_big_decimal` - if `true` then Dynamoid converts numbers stored in `Hash` in `raw` field to float. Default is `false`
678
691
  * `models_dir` - `dynamoid:create_tables` rake task loads DynamoDb models from this directory. Default is `./app/models`.
679
692
  * `application_timezone` - Dynamoid converts all `datetime` fields to specified time zone when loads data from the storage.
680
- Acceptable values - `utc`, `local` (to use system time zone) and time zone name e.g. `Eastern Time (US & Canada)`. Default is `local`
693
+ Acceptable values - `:utc`, `:local` (to use system time zone) and time zone name e.g. `Eastern Time (US & Canada)`. Default is `utc`
694
+ * `dynamodb_timezone` - When a datetime field is stored in string format Dynamoid converts it to specified time zone when saves a value to the storage.
695
+ Acceptable values - `:utc`, `:local` (to use system time zone) and time zone name e.g. `Eastern Time (US & Canada)`. Default is `utc`
681
696
  * `store_datetime_as_string` - if `true` then Dynamoid stores :datetime fields in ISO 8601 string format. Default is `false`
682
697
  * `store_date_as_string` - if `true` then Dynamoid stores :date fields in ISO 8601 string format. Default is `false`
698
+ * `store_boolean_as_native` - if `true` Dynamoid stores boolean fields as native DynamoDB
699
+ boolean values. Otherwise boolean fields are stored as string values
700
+ `'t'` and `'f'`. Default is true
683
701
  * `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
702
  * `backoff_strategies`: is a hash and contains all available strategies. Default is { constant: ..., exponential: ...}
685
703