dynamoid 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +18 -0
  3. data/.travis.yml +5 -3
  4. data/CHANGELOG.md +15 -0
  5. data/README.md +113 -63
  6. data/Vagrantfile +2 -2
  7. data/docker-compose.yml +1 -1
  8. data/gemfiles/rails_4_2.gemfile +1 -1
  9. data/gemfiles/rails_5_0.gemfile +1 -1
  10. data/gemfiles/rails_5_1.gemfile +1 -1
  11. data/gemfiles/rails_5_2.gemfile +1 -1
  12. data/lib/dynamoid/adapter.rb +1 -0
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +26 -395
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +234 -0
  15. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +89 -0
  16. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +24 -0
  17. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +57 -0
  18. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +28 -0
  19. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +123 -0
  20. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +85 -0
  21. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +52 -0
  22. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +60 -0
  23. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +1 -0
  24. data/lib/dynamoid/associations/has_many.rb +1 -0
  25. data/lib/dynamoid/associations/has_one.rb +1 -0
  26. data/lib/dynamoid/associations/single_association.rb +1 -0
  27. data/lib/dynamoid/criteria.rb +4 -4
  28. data/lib/dynamoid/criteria/chain.rb +86 -79
  29. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +41 -0
  30. data/lib/dynamoid/criteria/key_fields_detector.rb +61 -0
  31. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +41 -0
  32. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +40 -0
  33. data/lib/dynamoid/document.rb +18 -13
  34. data/lib/dynamoid/dumping.rb +52 -40
  35. data/lib/dynamoid/fields.rb +4 -3
  36. data/lib/dynamoid/finders.rb +3 -3
  37. data/lib/dynamoid/persistence.rb +5 -6
  38. data/lib/dynamoid/primary_key_type_mapping.rb +1 -1
  39. data/lib/dynamoid/tasks.rb +1 -0
  40. data/lib/dynamoid/tasks/database.rake +2 -2
  41. data/lib/dynamoid/type_casting.rb +37 -19
  42. data/lib/dynamoid/undumping.rb +53 -42
  43. data/lib/dynamoid/validations.rb +2 -0
  44. data/lib/dynamoid/version.rb +1 -1
  45. metadata +17 -5
  46. data/lib/dynamoid/adapter_plugin/query.rb +0 -144
  47. data/lib/dynamoid/adapter_plugin/scan.rb +0 -107
@@ -1,144 +0,0 @@
1
- module Dynamoid
2
- module AdapterPlugin
3
- class Query
4
- OPTIONS_KEYS = [
5
- :limit, :hash_key, :hash_value, :range_key, :consistent_read, :scan_index_forward,
6
- :select, :index_name, :batch_size, :exclusive_start_key, :record_limit, :scan_limit
7
- ]
8
-
9
- attr_reader :client, :table, :options, :conditions
10
-
11
- def initialize(client, table, opts = {})
12
- @client = client
13
- @table = table
14
-
15
- opts = opts.symbolize_keys
16
- @options = opts.slice(*OPTIONS_KEYS)
17
- @conditions = opts.except(*OPTIONS_KEYS)
18
- end
19
-
20
- def call
21
- request = build_request
22
-
23
- Enumerator.new do |yielder|
24
- record_count = 0
25
- scan_count = 0
26
-
27
- backoff = Dynamoid.config.backoff ? Dynamoid.config.build_backoff : nil
28
-
29
- loop do
30
- # Adjust the limit down if the remaining record and/or scan limit are
31
- # lower to obey limits. We can assume the difference won't be
32
- # negative due to break statements below but choose smaller limit
33
- # which is why we have 2 separate if statements.
34
- # NOTE: Adjusting based on record_limit can cause many HTTP requests
35
- # being made. We may want to change this behavior, but it affects
36
- # filtering on data with potentially large gaps.
37
- # Example:
38
- # User.where('created_at.gte' => 1.day.ago).record_limit(1000)
39
- # Records 1-999 User's that fit criteria
40
- # Records 1000-2000 Users's that do not fit criteria
41
- # Record 2001 fits criteria
42
- # The underlying implementation will have 1 page for records 1-999
43
- # then will request with limit 1 for records 1000-2000 (making 1000
44
- # requests of limit 1) until hit record 2001.
45
- if request[:limit] && record_limit && record_limit - record_count < request[:limit]
46
- request[:limit] = record_limit - record_count
47
- end
48
- if request[:limit] && scan_limit && scan_limit - scan_count < request[:limit]
49
- request[:limit] = scan_limit - scan_count
50
- end
51
-
52
- response = client.query(request)
53
-
54
- yielder << response
55
-
56
- record_count += response.count
57
- break if record_limit && record_count >= record_limit
58
-
59
- scan_count += response.scanned_count
60
- break if scan_limit && scan_count >= scan_limit
61
-
62
- if response.last_evaluated_key
63
- request[:exclusive_start_key] = response.last_evaluated_key
64
- else
65
- break
66
- end
67
-
68
- backoff.call if backoff
69
- end
70
- end
71
- end
72
-
73
- private
74
-
75
- def build_request
76
- request = options.slice(
77
- :consistent_read,
78
- :scan_index_forward,
79
- :select,
80
- :index_name,
81
- :exclusive_start_key
82
- ).compact
83
-
84
- # Deal with various limits and batching
85
- batch_size = options[:batch_size]
86
- limit = [record_limit, scan_limit, batch_size].compact.min
87
-
88
- request[:limit] = limit if limit
89
- request[:table_name] = table.name
90
- request[:key_conditions] = key_conditions
91
- request[:query_filter] = query_filter
92
-
93
- request
94
- end
95
-
96
- def record_limit
97
- options[:record_limit]
98
- end
99
-
100
- def scan_limit
101
- options[:scan_limit]
102
- end
103
-
104
- def hash_key_name
105
- (options[:hash_key] || table.hash_key)
106
- end
107
-
108
- def range_key_name
109
- (options[:range_key] || table.range_key)
110
- end
111
-
112
- def key_conditions
113
- result = {
114
- hash_key_name => {
115
- comparison_operator: AwsSdkV3::EQ,
116
- attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::EQ, options[:hash_value].freeze)
117
- }
118
- }
119
-
120
- conditions.slice(*AwsSdkV3::RANGE_MAP.keys).each do |k, _v|
121
- op = AwsSdkV3::RANGE_MAP[k]
122
-
123
- result[range_key_name] = {
124
- comparison_operator: op,
125
- attribute_value_list: AwsSdkV3.attribute_value_list(op, conditions[k].freeze)
126
- }
127
- end
128
-
129
- result
130
- end
131
-
132
- def query_filter
133
- conditions.except(*AwsSdkV3::RANGE_MAP.keys).reduce({}) do |result, (attr, cond)|
134
- condition = {
135
- comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
136
- attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
137
- }
138
- result[attr] = condition
139
- result
140
- end
141
- end
142
- end
143
- end
144
- end
@@ -1,107 +0,0 @@
1
- module Dynamoid
2
- module AdapterPlugin
3
- class Scan
4
- attr_reader :client, :table, :conditions, :options
5
-
6
- def initialize(client, table, conditions = {}, options = {})
7
- @client = client
8
- @table = table
9
- @conditions = conditions
10
- @options = options
11
- end
12
-
13
- def call
14
- request = build_request
15
-
16
- Enumerator.new do |yielder|
17
- record_count = 0
18
- scan_count = 0
19
-
20
- backoff = Dynamoid.config.backoff ? Dynamoid.config.build_backoff : nil
21
-
22
- loop do
23
- # Adjust the limit down if the remaining record and/or scan limit are
24
- # lower to obey limits. We can assume the difference won't be
25
- # negative due to break statements below but choose smaller limit
26
- # which is why we have 2 separate if statements.
27
- # NOTE: Adjusting based on record_limit can cause many HTTP requests
28
- # being made. We may want to change this behavior, but it affects
29
- # filtering on data with potentially large gaps.
30
- # Example:
31
- # User.where('created_at.gte' => 1.day.ago).record_limit(1000)
32
- # Records 1-999 User's that fit criteria
33
- # Records 1000-2000 Users's that do not fit criteria
34
- # Record 2001 fits criteria
35
- # The underlying implementation will have 1 page for records 1-999
36
- # then will request with limit 1 for records 1000-2000 (making 1000
37
- # requests of limit 1) until hit record 2001.
38
- if request[:limit] && record_limit && record_limit - record_count < request[:limit]
39
- request[:limit] = record_limit - record_count
40
- end
41
- if request[:limit] && scan_limit && scan_limit - scan_count < request[:limit]
42
- request[:limit] = scan_limit - scan_count
43
- end
44
-
45
- response = client.scan(request)
46
-
47
- yielder << response
48
-
49
- record_count += response.count
50
- break if record_limit && record_count >= record_limit
51
-
52
- scan_count += response.scanned_count
53
- break if scan_limit && scan_count >= scan_limit
54
-
55
- # Keep pulling if we haven't finished paging in all data
56
- if response.last_evaluated_key
57
- request[:exclusive_start_key] = response.last_evaluated_key
58
- else
59
- break
60
- end
61
-
62
- backoff.call if backoff
63
- end
64
- end
65
- end
66
-
67
- private
68
-
69
- def build_request
70
- request = options.slice(
71
- :consistent_read,
72
- :exclusive_start_key,
73
- :select
74
- ).compact
75
-
76
- # Deal with various limits and batching
77
- batch_size = options[:batch_size]
78
- limit = [record_limit, scan_limit, batch_size].compact.min
79
-
80
- request[:limit] = limit if limit
81
- request[:table_name] = table.name
82
- request[:scan_filter] = scan_filter
83
-
84
- request
85
- end
86
-
87
- def record_limit
88
- options[:record_limit]
89
- end
90
-
91
- def scan_limit
92
- options[:scan_limit]
93
- end
94
-
95
- def scan_filter
96
- conditions.reduce({}) do |result, (attr, cond)|
97
- condition = {
98
- comparison_operator: AwsSdkV3::FIELD_MAP[cond.keys[0]],
99
- attribute_value_list: AwsSdkV3.attribute_value_list(AwsSdkV3::FIELD_MAP[cond.keys[0]], cond.values[0].freeze)
100
- }
101
- result[attr] = condition
102
- result
103
- end
104
- end
105
- end
106
- end
107
- end