aws-record 1.0.3 → 1.1.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.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 707c0dda4d338ec8c39441124f050922181ca6a5
|
4
|
+
data.tar.gz: 945f67f08fa5230b7c3f7dac7d95138a1017ee9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f58be26c4c9c1215a293fbbcc1484ca4993d305edeac7e16182df127a9fe80620135024039190eb9894d3ed5f58554a02cf93cd091b73c9b133c7c739d4a90ba
|
7
|
+
data.tar.gz: 056e886c4d5146019525d9aa5eb25e2f4e195dfee69abf9105be266210ca6e20daee3f0b5a3be66ab390d30d5eb7506e83ac0cd0842e62242fd602ce3f0d5ce0
|
data/lib/aws-record.rb
CHANGED
@@ -28,6 +28,7 @@ module Aws
|
|
28
28
|
autoload :ModelAttributes, 'aws-record/record/model_attributes'
|
29
29
|
autoload :Query, 'aws-record/record/query'
|
30
30
|
autoload :SecondaryIndexes, 'aws-record/record/secondary_indexes'
|
31
|
+
autoload :TableConfig, 'aws-record/record/table_config'
|
31
32
|
autoload :TableMigration, 'aws-record/record/table_migration'
|
32
33
|
autoload :VERSION, 'aws-record/record/version'
|
33
34
|
|
@@ -89,14 +89,17 @@ module Aws
|
|
89
89
|
private
|
90
90
|
def _migration_format_indexes(indexes)
|
91
91
|
return nil if indexes.empty?
|
92
|
-
indexes.collect do |name, opts|
|
92
|
+
mfi = indexes.collect do |name, opts|
|
93
93
|
h = { index_name: name }
|
94
94
|
h[:key_schema] = _si_key_schema(opts)
|
95
|
-
opts.delete(:hash_key)
|
96
|
-
opts.delete(:range_key)
|
95
|
+
hk = opts.delete(:hash_key)
|
96
|
+
rk = opts.delete(:range_key)
|
97
97
|
h = h.merge(opts)
|
98
|
+
opts[:hash_key] = hk if hk
|
99
|
+
opts[:range_key] = rk if rk
|
98
100
|
h
|
99
101
|
end
|
102
|
+
mfi
|
100
103
|
end
|
101
104
|
|
102
105
|
def _si_key_schema(opts)
|
@@ -0,0 +1,514 @@
|
|
1
|
+
# Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not
|
4
|
+
# use this file except in compliance with the License. A copy of the License is
|
5
|
+
# located at
|
6
|
+
#
|
7
|
+
# http://aws.amazon.com/apache2.0/
|
8
|
+
#
|
9
|
+
# or in the "license" file accompanying this file. This file is distributed on
|
10
|
+
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
11
|
+
# or implied. See the License for the specific language governing permissions
|
12
|
+
# and limitations under the License.
|
13
|
+
|
14
|
+
module Aws
|
15
|
+
module Record
|
16
|
+
|
17
|
+
# +Aws::Record::TableConfig+ provides a DSL for describing and modifying
|
18
|
+
# the remote configuration of your DynamoDB tables. A table configuration
|
19
|
+
# object can perform intelligent comparisons and incremental migrations
|
20
|
+
# versus the current remote configuration, if the table exists, or do a full
|
21
|
+
# create if it does not. In this manner, table configuration becomes fully
|
22
|
+
# declarative.
|
23
|
+
#
|
24
|
+
# @example A basic model with configuration.
|
25
|
+
# class Model
|
26
|
+
# include Aws::Record
|
27
|
+
# string_attr :uuid, hash_key: true
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# table_config = Aws::Record::TableConfig.define do |t|
|
31
|
+
# t.model_class Model
|
32
|
+
# t.read_capacity_units 10
|
33
|
+
# t.write_capacity_units 5
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# @example Running a conditional migration on a basic model.
|
37
|
+
# table_config = Aws::Record::TableConfig.define do |t|
|
38
|
+
# t.model_class Model
|
39
|
+
# t.read_capacity_units 10
|
40
|
+
# t.write_capacity_units 5
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# table_config.migrate! unless table_config.compatible?
|
44
|
+
#
|
45
|
+
# @example A model with a global secondary index.
|
46
|
+
# class Forum
|
47
|
+
# include Aws::Record
|
48
|
+
# string_attr :forum_uuid, hash_key: true
|
49
|
+
# integer_attr :post_id, range_key: true
|
50
|
+
# string_attr :post_title
|
51
|
+
# string_attr :post_body
|
52
|
+
# string_attr :author_username
|
53
|
+
# datetime_attr :created_date
|
54
|
+
# datetime_attr :updated_date
|
55
|
+
# string_set_attr :tags
|
56
|
+
# map_attr :metadata, default_value: {}
|
57
|
+
#
|
58
|
+
# global_secondary_index(
|
59
|
+
# :title,
|
60
|
+
# hash_key: :forum_uuid,
|
61
|
+
# range_key: :post_title,
|
62
|
+
# projection_type: "ALL"
|
63
|
+
# )
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# table_config = Aws::Record::TableConfig.define do |t|
|
67
|
+
# t.model_class Forum
|
68
|
+
# t.read_capacity_units 10
|
69
|
+
# t.write_capacity_units 5
|
70
|
+
#
|
71
|
+
# t.global_secondary_index(:title) do |i|
|
72
|
+
# i.read_capacity_units 5
|
73
|
+
# i.write_capacity_units 5
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
class TableConfig
|
78
|
+
|
79
|
+
attr_accessor :client
|
80
|
+
|
81
|
+
class << self
|
82
|
+
|
83
|
+
# Creates a new table configuration, using a DSL in the provided block.
|
84
|
+
# The DSL has the following methods:
|
85
|
+
# * +#model_class+ A class name reference to the +Aws::Record+ model
|
86
|
+
# class.
|
87
|
+
# * +#read_capacity_units+ Sets the read capacity units for the table.
|
88
|
+
# * +#write_capacity_units+ Sets the write capacity units for the table.
|
89
|
+
# * +#global_secondary_index(index_symbol, &block)+ Defines a global
|
90
|
+
# secondary index with capacity attributes in a block:
|
91
|
+
# * +#read_capacity_units+ Sets the read capacity units for the
|
92
|
+
# index.
|
93
|
+
# * +#write_capacity_units+ Sets the write capacity units for the
|
94
|
+
# index.
|
95
|
+
#
|
96
|
+
# @example Defining a migration with a GSI.
|
97
|
+
# class Forum
|
98
|
+
# include Aws::Record
|
99
|
+
# string_attr :forum_uuid, hash_key: true
|
100
|
+
# integer_attr :post_id, range_key: true
|
101
|
+
# string_attr :post_title
|
102
|
+
# string_attr :post_body
|
103
|
+
# string_attr :author_username
|
104
|
+
# datetime_attr :created_date
|
105
|
+
# datetime_attr :updated_date
|
106
|
+
# string_set_attr :tags
|
107
|
+
# map_attr :metadata, default_value: {}
|
108
|
+
#
|
109
|
+
# global_secondary_index(
|
110
|
+
# :title,
|
111
|
+
# hash_key: :forum_uuid,
|
112
|
+
# range_key: :post_title,
|
113
|
+
# projection_type: "ALL"
|
114
|
+
# )
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# table_config = Aws::Record::TableConfig.define do |t|
|
118
|
+
# t.model_class Forum
|
119
|
+
# t.read_capacity_units 10
|
120
|
+
# t.write_capacity_units 5
|
121
|
+
#
|
122
|
+
# t.global_secondary_index(:title) do |i|
|
123
|
+
# i.read_capacity_units 5
|
124
|
+
# i.write_capacity_units 5
|
125
|
+
# end
|
126
|
+
# end
|
127
|
+
def define(&block)
|
128
|
+
cfg = TableConfig.new
|
129
|
+
cfg.instance_eval(&block)
|
130
|
+
cfg.configure_client
|
131
|
+
cfg
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# @api private
|
136
|
+
def initialize
|
137
|
+
@client_options = {}
|
138
|
+
@global_secondary_indexes = {}
|
139
|
+
end
|
140
|
+
|
141
|
+
# @api private
|
142
|
+
def model_class(model)
|
143
|
+
@model_class = model
|
144
|
+
end
|
145
|
+
|
146
|
+
# @api private
|
147
|
+
def read_capacity_units(units)
|
148
|
+
@read_capacity_units = units
|
149
|
+
end
|
150
|
+
|
151
|
+
# @api private
|
152
|
+
def write_capacity_units(units)
|
153
|
+
@write_capacity_units = units
|
154
|
+
end
|
155
|
+
|
156
|
+
# @api private
|
157
|
+
def global_secondary_index(name, &block)
|
158
|
+
gsi = GlobalSecondaryIndex.new
|
159
|
+
gsi.instance_eval(&block)
|
160
|
+
@global_secondary_indexes[name] = gsi
|
161
|
+
end
|
162
|
+
|
163
|
+
# @api private
|
164
|
+
def client_options(opts)
|
165
|
+
@client_options = opts
|
166
|
+
end
|
167
|
+
|
168
|
+
# @api private
|
169
|
+
def configure_client
|
170
|
+
@client = Aws::DynamoDB::Client.new(@client_options)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Performs a migration, if needed, against the remote table. If
|
174
|
+
# +#compatible?+ would return true, the remote table already has the same
|
175
|
+
# throughput, key schema, attribute definitions, and global secondary
|
176
|
+
# indexes, so no further API calls are made. Otherwise, a DynamoDB table
|
177
|
+
# will be created or updated to match your declared configuration.
|
178
|
+
def migrate!
|
179
|
+
_validate_required_configuration
|
180
|
+
begin
|
181
|
+
resp = @client.describe_table(table_name: @model_class.table_name)
|
182
|
+
if _compatible_check(resp)
|
183
|
+
nil
|
184
|
+
else
|
185
|
+
# Gotcha: You need separate migrations for indexes and throughput
|
186
|
+
unless _throughput_equal(resp)
|
187
|
+
@client.update_table(
|
188
|
+
table_name: @model_class.table_name,
|
189
|
+
provisioned_throughput: {
|
190
|
+
read_capacity_units: @read_capacity_units,
|
191
|
+
write_capacity_units: @write_capacity_units
|
192
|
+
}
|
193
|
+
)
|
194
|
+
@client.wait_until(
|
195
|
+
:table_exists,
|
196
|
+
table_name: @model_class.table_name
|
197
|
+
)
|
198
|
+
end
|
199
|
+
unless _gsi_superset(resp)
|
200
|
+
@client.update_table(_update_index_opts(resp))
|
201
|
+
@client.wait_until(
|
202
|
+
:table_exists,
|
203
|
+
table_name: @model_class.table_name
|
204
|
+
)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
rescue DynamoDB::Errors::ResourceNotFoundException
|
208
|
+
# Code Smell: Exception as control flow.
|
209
|
+
# Can I use SDK ability to skip raising an exception for this?
|
210
|
+
@client.create_table(_create_table_opts)
|
211
|
+
@client.wait_until(:table_exists, table_name: @model_class.table_name)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Checks the remote table for compatibility. Similar to +#exact_match?+,
|
216
|
+
# this will return +false+ if the remote table does not exist. It also
|
217
|
+
# checks the keys, declared global secondary indexes, declared attribute
|
218
|
+
# definitions, and throughput for exact matches. However, if the remote
|
219
|
+
# end has additional attribute definitions and global secondary indexes
|
220
|
+
# not defined in your config, will still return +true+. This allows for a
|
221
|
+
# check that is friendly to single table inheritance use cases.
|
222
|
+
#
|
223
|
+
# @return [Boolean] true if remote is compatible, false otherwise.
|
224
|
+
def compatible?
|
225
|
+
begin
|
226
|
+
resp = @client.describe_table(table_name: @model_class.table_name)
|
227
|
+
_compatible_check(resp)
|
228
|
+
rescue DynamoDB::Errors::ResourceNotFoundException
|
229
|
+
false
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Checks against the remote table's configuration. If the remote table
|
234
|
+
# does not exist, guaranteed +false+. Otherwise, will check if the remote
|
235
|
+
# throughput, keys, attribute definitions, and global secondary indexes
|
236
|
+
# are exactly equal to your declared configuration.
|
237
|
+
#
|
238
|
+
# @return [Boolean] true if remote is an exact match, false otherwise.
|
239
|
+
def exact_match?
|
240
|
+
begin
|
241
|
+
resp = @client.describe_table(table_name: @model_class.table_name)
|
242
|
+
_throughput_equal(resp) &&
|
243
|
+
_keys_equal(resp) &&
|
244
|
+
_ad_equal(resp) &&
|
245
|
+
_gsi_equal(resp)
|
246
|
+
rescue DynamoDB::Errors::ResourceNotFoundException
|
247
|
+
false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
def _compatible_check(resp)
|
253
|
+
_throughput_equal(resp) &&
|
254
|
+
_keys_equal(resp) &&
|
255
|
+
_ad_superset(resp) &&
|
256
|
+
_gsi_superset(resp)
|
257
|
+
end
|
258
|
+
|
259
|
+
def _create_table_opts
|
260
|
+
opts = {
|
261
|
+
table_name: @model_class.table_name,
|
262
|
+
provisioned_throughput: {
|
263
|
+
read_capacity_units: @read_capacity_units,
|
264
|
+
write_capacity_units: @write_capacity_units
|
265
|
+
}
|
266
|
+
}
|
267
|
+
opts[:key_schema] = _key_schema
|
268
|
+
opts[:attribute_definitions] = _attribute_definitions
|
269
|
+
gsi = _global_secondary_indexes
|
270
|
+
unless gsi.empty?
|
271
|
+
opts[:global_secondary_indexes] = gsi
|
272
|
+
end
|
273
|
+
opts
|
274
|
+
end
|
275
|
+
|
276
|
+
def _update_index_opts(resp)
|
277
|
+
gsi_updates, attribute_definitions = _gsi_updates(resp)
|
278
|
+
opts = {
|
279
|
+
table_name: @model_class.table_name,
|
280
|
+
global_secondary_index_updates: gsi_updates
|
281
|
+
}
|
282
|
+
unless attribute_definitions.empty?
|
283
|
+
opts[:attribute_definitions] = attribute_definitions
|
284
|
+
end
|
285
|
+
opts
|
286
|
+
end
|
287
|
+
|
288
|
+
def _gsi_updates(resp)
|
289
|
+
gsi_updates = []
|
290
|
+
attributes_referenced = Set.new
|
291
|
+
remote_gsis = resp.table.global_secondary_indexes
|
292
|
+
local_gsis = _global_secondary_indexes
|
293
|
+
remote_idx, local_idx = _gsi_index_names(remote_gsis, local_gsis)
|
294
|
+
create_candidates = local_idx - remote_idx
|
295
|
+
update_candidates = local_idx.intersection(remote_idx)
|
296
|
+
create_candidates.each do |index_name|
|
297
|
+
gsi = @model_class.global_secondary_indexes_for_migration.find do |i|
|
298
|
+
i[:index_name].to_s == index_name
|
299
|
+
end
|
300
|
+
gsi[:key_schema].each do |k|
|
301
|
+
attributes_referenced.add(k[:attribute_name])
|
302
|
+
end
|
303
|
+
# This may be a problem, check if I can maintain symbols.
|
304
|
+
lgsi = @global_secondary_indexes[index_name.to_sym]
|
305
|
+
gsi[:provisioned_throughput] = lgsi.provisioned_throughput
|
306
|
+
gsi_updates << {
|
307
|
+
create: gsi
|
308
|
+
}
|
309
|
+
end
|
310
|
+
update_candidates.each do |index_name|
|
311
|
+
# This may be a problem, check if I can maintain symbols.
|
312
|
+
lgsi = @global_secondary_indexes[index_name.to_sym]
|
313
|
+
gsi_updates << {
|
314
|
+
update: {
|
315
|
+
index_name: index_name,
|
316
|
+
provisioned_throughput: lgsi.provisioned_throughput
|
317
|
+
}
|
318
|
+
}
|
319
|
+
end
|
320
|
+
attribute_definitions = _attribute_definitions
|
321
|
+
incremental_attributes = attributes_referenced.map do |attr_name|
|
322
|
+
attribute_definitions.find do |ad|
|
323
|
+
ad[:attribute_name] == attr_name
|
324
|
+
end
|
325
|
+
end
|
326
|
+
[gsi_updates, incremental_attributes]
|
327
|
+
end
|
328
|
+
|
329
|
+
def _key_schema
|
330
|
+
_keys.map do |type, attr|
|
331
|
+
{
|
332
|
+
attribute_name: attr.database_name,
|
333
|
+
key_type: type == :hash ? "HASH" : "RANGE"
|
334
|
+
}
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def _attribute_definitions
|
339
|
+
attribute_definitions = _keys.map do |type, attr|
|
340
|
+
{
|
341
|
+
attribute_name: attr.database_name,
|
342
|
+
attribute_type: attr.dynamodb_type
|
343
|
+
}
|
344
|
+
end
|
345
|
+
@model_class.global_secondary_indexes.each do |_, attributes|
|
346
|
+
gsi_keys = [attributes[:hash_key]]
|
347
|
+
gsi_keys << attributes[:range_key] if attributes[:range_key]
|
348
|
+
gsi_keys.each do |name|
|
349
|
+
attribute = @model_class.attributes.attribute_for(name)
|
350
|
+
exists = attribute_definitions.any? do |ad|
|
351
|
+
ad[:attribute_name] == attribute.database_name
|
352
|
+
end
|
353
|
+
unless exists
|
354
|
+
attribute_definitions << {
|
355
|
+
attribute_name: attribute.database_name,
|
356
|
+
attribute_type: attribute.dynamodb_type
|
357
|
+
}
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
attribute_definitions
|
362
|
+
end
|
363
|
+
|
364
|
+
def _keys
|
365
|
+
@model_class.keys.inject({}) do |acc, (type, name)|
|
366
|
+
acc[type] = @model_class.attributes.attribute_for(name)
|
367
|
+
acc
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def _throughput_equal(resp)
|
372
|
+
expected = resp.table.provisioned_throughput.to_h
|
373
|
+
actual = {
|
374
|
+
read_capacity_units: @read_capacity_units,
|
375
|
+
write_capacity_units: @write_capacity_units
|
376
|
+
}
|
377
|
+
actual.all? do |k,v|
|
378
|
+
expected[k] == v
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def _keys_equal(resp)
|
383
|
+
remote_key_schema = resp.table.key_schema.map { |i| i.to_h }
|
384
|
+
_array_unsorted_eql(remote_key_schema, _key_schema)
|
385
|
+
end
|
386
|
+
|
387
|
+
def _ad_equal(resp)
|
388
|
+
remote_ad = resp.table.attribute_definitions.map { |i| i.to_h }
|
389
|
+
_array_unsorted_eql(remote_ad, _attribute_definitions)
|
390
|
+
end
|
391
|
+
|
392
|
+
def _ad_superset(resp)
|
393
|
+
remote_ad = resp.table.attribute_definitions.map { |i| i.to_h }
|
394
|
+
_attribute_definitions.all? do |attribute_definition|
|
395
|
+
remote_ad.include?(attribute_definition)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def _gsi_superset(resp)
|
400
|
+
remote_gsis = resp.table.global_secondary_indexes
|
401
|
+
local_gsis = _global_secondary_indexes
|
402
|
+
remote_idx, local_idx = _gsi_index_names(remote_gsis, local_gsis)
|
403
|
+
if local_idx.subset?(remote_idx)
|
404
|
+
_gsi_set_compare(remote_gsis, local_gsis)
|
405
|
+
else
|
406
|
+
# If we have any local indexes not on the remote table,
|
407
|
+
# guaranteed false.
|
408
|
+
false
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def _gsi_equal(resp)
|
413
|
+
remote_gsis = resp.table.global_secondary_indexes
|
414
|
+
local_gsis = _global_secondary_indexes
|
415
|
+
remote_idx, local_idx = _gsi_index_names(remote_gsis, local_gsis)
|
416
|
+
if local_idx == remote_idx
|
417
|
+
_gsi_set_compare(remote_gsis, local_gsis)
|
418
|
+
else
|
419
|
+
false
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def _gsi_set_compare(remote_gsis, local_gsis)
|
424
|
+
local_gsis.all? do |lgsi|
|
425
|
+
rgsi = remote_gsis.find do |r|
|
426
|
+
r.index_name == lgsi[:index_name].to_s
|
427
|
+
end
|
428
|
+
|
429
|
+
remote_key_schema = rgsi.key_schema.map { |i| i.to_h }
|
430
|
+
ks_match = _array_unsorted_eql(remote_key_schema, lgsi[:key_schema])
|
431
|
+
|
432
|
+
rpt = rgsi.provisioned_throughput.to_h
|
433
|
+
lpt = lgsi[:provisioned_throughput]
|
434
|
+
pt_match = lpt.all? do |k,v|
|
435
|
+
rpt[k] == v
|
436
|
+
end
|
437
|
+
|
438
|
+
rp = rgsi.projection.to_h
|
439
|
+
lp = lgsi[:projection]
|
440
|
+
rp[:non_key_attributes].sort! if rp[:non_key_attributes]
|
441
|
+
lp[:non_key_attributes].sort! if lp[:non_key_attributes]
|
442
|
+
p_match = rp == lp
|
443
|
+
|
444
|
+
ks_match && pt_match && p_match
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
def _gsi_index_names(remote, local)
|
449
|
+
remote_index_names = Set.new
|
450
|
+
local_index_names = Set.new
|
451
|
+
if remote
|
452
|
+
remote.each do |gsi|
|
453
|
+
remote_index_names.add(gsi.index_name)
|
454
|
+
end
|
455
|
+
end
|
456
|
+
if local
|
457
|
+
local.each do |gsi|
|
458
|
+
local_index_names.add(gsi[:index_name].to_s)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
[remote_index_names, local_index_names]
|
462
|
+
end
|
463
|
+
|
464
|
+
def _global_secondary_indexes
|
465
|
+
gsis = []
|
466
|
+
model_gsis = @model_class.global_secondary_indexes_for_migration
|
467
|
+
gsi_config = @global_secondary_indexes
|
468
|
+
if model_gsis
|
469
|
+
model_gsis.each do |mgsi|
|
470
|
+
config = gsi_config[mgsi[:index_name]]
|
471
|
+
# Validate throughput exists? Validate each throughput is in model?
|
472
|
+
gsis << mgsi.merge(
|
473
|
+
provisioned_throughput: config.provisioned_throughput
|
474
|
+
)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
gsis
|
478
|
+
end
|
479
|
+
|
480
|
+
def _array_unsorted_eql(a, b)
|
481
|
+
a.all? { |x| b.include?(x) } && b.all? { |x| a.include?(x) }
|
482
|
+
end
|
483
|
+
|
484
|
+
def _validate_required_configuration
|
485
|
+
missing_config = []
|
486
|
+
missing_config << 'model_class' unless @model_class
|
487
|
+
missing_config << 'read_capacity_units' unless @read_capacity_units
|
488
|
+
missing_config << 'write_capacity_units' unless @write_capacity_units
|
489
|
+
unless missing_config.empty?
|
490
|
+
msg = missing_config.join(', ')
|
491
|
+
raise Errors::MissingRequiredConfiguration, 'Missing: ' + msg
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
# @api private
|
496
|
+
class GlobalSecondaryIndex
|
497
|
+
attr_reader :provisioned_throughput
|
498
|
+
|
499
|
+
def initialize
|
500
|
+
@provisioned_throughput = {}
|
501
|
+
end
|
502
|
+
|
503
|
+
def read_capacity_units(units)
|
504
|
+
@provisioned_throughput[:read_capacity_units] = units
|
505
|
+
end
|
506
|
+
|
507
|
+
def write_capacity_units(units)
|
508
|
+
@provisioned_throughput[:write_capacity_units] = units
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amazon Web Services
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-resources
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- lib/aws-record/record/model_attributes.rb
|
55
55
|
- lib/aws-record/record/query.rb
|
56
56
|
- lib/aws-record/record/secondary_indexes.rb
|
57
|
+
- lib/aws-record/record/table_config.rb
|
57
58
|
- lib/aws-record/record/table_migration.rb
|
58
59
|
- lib/aws-record/record/version.rb
|
59
60
|
homepage: http://github.com/aws/aws-sdk-ruby-record
|