aws-record 1.0.0.pre.8 → 1.0.0.pre.9
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 +4 -4
- data/lib/aws-record.rb +11 -11
- data/lib/aws-record/record.rb +21 -0
- data/lib/aws-record/record/attribute.rb +35 -3
- data/lib/aws-record/record/attributes.rb +137 -16
- data/lib/aws-record/record/dirty_tracking.rb +48 -8
- data/lib/aws-record/record/item_operations.rb +96 -48
- data/lib/aws-record/record/marshalers/boolean_marshaler.rb +53 -0
- data/lib/aws-record/record/marshalers/date_marshaler.rb +61 -0
- data/lib/aws-record/record/marshalers/date_time_marshaler.rb +72 -0
- data/lib/aws-record/record/marshalers/float_marshaler.rb +52 -0
- data/lib/aws-record/record/marshalers/integer_marshaler.rb +52 -0
- data/lib/aws-record/record/marshalers/list_marshaler.rb +56 -0
- data/lib/aws-record/record/marshalers/map_marshaler.rb +56 -0
- data/lib/aws-record/record/marshalers/numeric_set_marshaler.rb +69 -0
- data/lib/aws-record/record/marshalers/string_marshaler.rb +52 -0
- data/lib/aws-record/record/marshalers/string_set_marshaler.rb +69 -0
- data/lib/aws-record/record/version.rb +1 -1
- metadata +12 -13
- data/lib/aws-record/record/attributes/boolean_marshaler.rb +0 -54
- data/lib/aws-record/record/attributes/date_marshaler.rb +0 -54
- data/lib/aws-record/record/attributes/date_time_marshaler.rb +0 -55
- data/lib/aws-record/record/attributes/float_marshaler.rb +0 -53
- data/lib/aws-record/record/attributes/integer_marshaler.rb +0 -53
- data/lib/aws-record/record/attributes/list_marshaler.rb +0 -66
- data/lib/aws-record/record/attributes/map_marshaler.rb +0 -66
- data/lib/aws-record/record/attributes/numeric_set_marshaler.rb +0 -72
- data/lib/aws-record/record/attributes/string_marshaler.rb +0 -60
- data/lib/aws-record/record/attributes/string_set_marshaler.rb +0 -70
@@ -21,17 +21,17 @@ module Aws
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Saves this instance of an item to Amazon DynamoDB. If this item is "new"
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
24
|
+
# as defined by having new or altered key attributes, will attempt a
|
25
|
+
# conditional
|
26
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
|
27
|
+
# call, which will not overwrite an existing item. If the item only has
|
28
|
+
# altered non-key attributes, will perform an
|
29
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
|
30
|
+
# call. Uses this item instance's attributes in order to build the
|
31
|
+
# request on your behalf.
|
32
32
|
#
|
33
33
|
# You can use the +:force+ option to perform a simple put/overwrite
|
34
|
-
#
|
34
|
+
# without conditional validation or update logic.
|
35
35
|
#
|
36
36
|
# @param [Hash] opts
|
37
37
|
# @option opts [Boolean] :force if true, will save as a put operation and
|
@@ -54,17 +54,17 @@ module Aws
|
|
54
54
|
end
|
55
55
|
|
56
56
|
# Saves this instance of an item to Amazon DynamoDB. If this item is "new"
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
57
|
+
# as defined by having new or altered key attributes, will attempt a
|
58
|
+
# conditional
|
59
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
|
60
|
+
# call, which will not overwrite an existing item. If the item only has
|
61
|
+
# altered non-key attributes, will perform an
|
62
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
|
63
|
+
# call. Uses this item instance's attributes in order to build the
|
64
|
+
# request on your behalf.
|
65
65
|
#
|
66
66
|
# You can use the +:force+ option to perform a simple put/overwrite
|
67
|
-
#
|
67
|
+
# without conditional validation or update logic.
|
68
68
|
#
|
69
69
|
# @param [Hash] opts
|
70
70
|
# @option opts [Boolean] :force if true, will save as a put operation and
|
@@ -112,12 +112,12 @@ module Aws
|
|
112
112
|
if force
|
113
113
|
dynamodb_client.put_item(
|
114
114
|
table_name: self.class.table_name,
|
115
|
-
item:
|
115
|
+
item: _build_item_for_save
|
116
116
|
)
|
117
117
|
elsif expect_new
|
118
118
|
put_opts = {
|
119
119
|
table_name: self.class.table_name,
|
120
|
-
item:
|
120
|
+
item: _build_item_for_save
|
121
121
|
}.merge(prevent_overwrite_expression)
|
122
122
|
begin
|
123
123
|
dynamodb_client.put_item(put_opts)
|
@@ -129,25 +129,52 @@ module Aws
|
|
129
129
|
)
|
130
130
|
end
|
131
131
|
else
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
132
|
+
update_pairs = _dirty_changes_for_update
|
133
|
+
update_tuple = self.class.send(
|
134
|
+
:_build_update_expression,
|
135
|
+
update_pairs
|
136
136
|
)
|
137
|
+
if update_tuple
|
138
|
+
uex, exp_attr_names, exp_attr_values = update_tuple
|
139
|
+
dynamodb_client.update_item(
|
140
|
+
table_name: self.class.table_name,
|
141
|
+
key: key_values,
|
142
|
+
update_expression: uex,
|
143
|
+
expression_attribute_names: exp_attr_names,
|
144
|
+
expression_attribute_values: exp_attr_values
|
145
|
+
)
|
146
|
+
else
|
147
|
+
dynamodb_client.update_item(
|
148
|
+
table_name: self.class.table_name,
|
149
|
+
key: key_values
|
150
|
+
)
|
151
|
+
end
|
137
152
|
end
|
138
153
|
end
|
139
154
|
|
140
|
-
def
|
155
|
+
def _build_item_for_save
|
141
156
|
validate_key_values
|
142
157
|
attributes = self.class.attributes
|
158
|
+
_populate_default_values(attributes)
|
143
159
|
@data.inject({}) do |acc, name_value_pair|
|
144
160
|
attr_name, raw_value = name_value_pair
|
145
|
-
|
146
|
-
|
161
|
+
attribute = attributes[attr_name]
|
162
|
+
if !raw_value.nil? || attribute.persist_nil?
|
163
|
+
db_name = attribute.database_name
|
164
|
+
acc[db_name] = attribute.serialize(raw_value)
|
165
|
+
end
|
147
166
|
acc
|
148
167
|
end
|
149
168
|
end
|
150
169
|
|
170
|
+
def _populate_default_values(attributes)
|
171
|
+
attributes.each do |attr_name, attribute|
|
172
|
+
if !attribute.default_value.nil? && @data[attribute.name].nil?
|
173
|
+
@data[attr_name] = attribute.default_value
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
151
178
|
def key_values
|
152
179
|
validate_key_values
|
153
180
|
attributes = self.class.attributes
|
@@ -198,15 +225,10 @@ module Aws
|
|
198
225
|
}
|
199
226
|
end
|
200
227
|
|
201
|
-
def
|
228
|
+
def _dirty_changes_for_update
|
202
229
|
attributes = self.class.attributes
|
203
230
|
ret = dirty.inject({}) do |acc, attr_name|
|
204
|
-
|
205
|
-
value = {
|
206
|
-
value: attributes[attr_name].serialize(@data[attr_name]),
|
207
|
-
action: "PUT"
|
208
|
-
}
|
209
|
-
acc[key] = value
|
231
|
+
acc[attr_name] = @data[attr_name]
|
210
232
|
acc
|
211
233
|
end
|
212
234
|
ret
|
@@ -289,32 +311,54 @@ module Aws
|
|
289
311
|
table_name: table_name,
|
290
312
|
key: key
|
291
313
|
}
|
292
|
-
|
314
|
+
update_tuple = _build_update_expression(opts)
|
315
|
+
unless update_tuple.nil?
|
316
|
+
uex, exp_attr_names, exp_attr_values = update_tuple
|
317
|
+
request_opts[:update_expression] = uex
|
318
|
+
request_opts[:expression_attribute_names] = exp_attr_names
|
319
|
+
request_opts[:expression_attribute_values] = exp_attr_values
|
320
|
+
end
|
321
|
+
dynamodb_client.update_item(request_opts)
|
322
|
+
end
|
323
|
+
|
324
|
+
private
|
325
|
+
def _build_update_expression(attr_value_pairs)
|
326
|
+
set_expressions = []
|
327
|
+
remove_expressions = []
|
293
328
|
exp_attr_names = {}
|
294
329
|
exp_attr_values = {}
|
295
|
-
name_sub_token = "
|
296
|
-
value_sub_token = "
|
297
|
-
|
330
|
+
name_sub_token = "UE_A"
|
331
|
+
value_sub_token = "ue_a"
|
332
|
+
attr_value_pairs.each do |attr_sym, value|
|
298
333
|
name_sub = "#" + name_sub_token
|
299
334
|
value_sub = ":" + value_sub_token
|
300
335
|
name_sub_token = name_sub_token.succ
|
301
336
|
value_sub_token = value_sub_token.succ
|
302
337
|
|
303
|
-
|
304
|
-
|
338
|
+
attribute = attributes[attr_sym]
|
339
|
+
attr_name = attribute.database_name
|
305
340
|
exp_attr_names[name_sub] = attr_name
|
306
|
-
|
341
|
+
if _update_type_remove?(attribute, value)
|
342
|
+
remove_expressions << "#{name_sub}"
|
343
|
+
else
|
344
|
+
set_expressions << "#{name_sub} = #{value_sub}"
|
345
|
+
exp_attr_values[value_sub] = attribute.serialize(value)
|
346
|
+
end
|
307
347
|
end
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
348
|
+
update_expressions = []
|
349
|
+
unless set_expressions.empty?
|
350
|
+
update_expressions << "SET " + set_expressions.join(", ")
|
351
|
+
end
|
352
|
+
unless remove_expressions.empty?
|
353
|
+
update_expressions << "REMOVE " + remove_expressions.join(", ")
|
354
|
+
end
|
355
|
+
if update_expressions.empty?
|
356
|
+
nil
|
357
|
+
else
|
358
|
+
[update_expressions.join(" "), exp_attr_names, exp_attr_values]
|
313
359
|
end
|
314
|
-
dynamodb_client.update_item(request_opts)
|
315
360
|
end
|
316
361
|
|
317
|
-
private
|
318
362
|
def build_item_from_resp(resp)
|
319
363
|
record = new
|
320
364
|
data = record.instance_variable_get("@data")
|
@@ -323,6 +367,10 @@ module Aws
|
|
323
367
|
end
|
324
368
|
record
|
325
369
|
end
|
370
|
+
|
371
|
+
def _update_type_remove?(attribute, value)
|
372
|
+
value.nil? && !attribute.persist_nil?
|
373
|
+
end
|
326
374
|
end
|
327
375
|
|
328
376
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright 2016 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
|
+
module Marshalers
|
17
|
+
|
18
|
+
class BooleanMarshaler
|
19
|
+
def initialize(opts = {})
|
20
|
+
end
|
21
|
+
|
22
|
+
def type_cast(raw_value)
|
23
|
+
case raw_value
|
24
|
+
when nil
|
25
|
+
nil
|
26
|
+
when ''
|
27
|
+
nil
|
28
|
+
when false, 'false', '0', 0
|
29
|
+
false
|
30
|
+
else
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialize(raw_value)
|
36
|
+
boolean = type_cast(raw_value)
|
37
|
+
case boolean
|
38
|
+
when nil
|
39
|
+
nil
|
40
|
+
when false
|
41
|
+
false
|
42
|
+
when true
|
43
|
+
true
|
44
|
+
else
|
45
|
+
msg = "expected a boolean value or nil, got #{boolean.class}"
|
46
|
+
raise ArgumentError, msg
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright 2016 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
|
+
require 'date'
|
15
|
+
|
16
|
+
module Aws
|
17
|
+
module Record
|
18
|
+
module Marshalers
|
19
|
+
|
20
|
+
class DateMarshaler
|
21
|
+
def initialize(opts = {})
|
22
|
+
@formatter = opts[:formatter] || Iso8601Formatter
|
23
|
+
end
|
24
|
+
|
25
|
+
def type_cast(raw_value)
|
26
|
+
case raw_value
|
27
|
+
when nil
|
28
|
+
nil
|
29
|
+
when ''
|
30
|
+
nil
|
31
|
+
when Date
|
32
|
+
raw_value
|
33
|
+
when Integer
|
34
|
+
Date.parse(Time.at(raw_value).to_s) # assumed timestamp
|
35
|
+
else
|
36
|
+
Date.parse(raw_value.to_s) # Time, DateTime or String
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def serialize(raw_value)
|
41
|
+
date = type_cast(raw_value)
|
42
|
+
if date.nil?
|
43
|
+
nil
|
44
|
+
elsif date.is_a?(Date)
|
45
|
+
@formatter.format(date)
|
46
|
+
else
|
47
|
+
raise ArgumentError, "expected a Date value or nil, got #{date.class}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
module Iso8601Formatter
|
54
|
+
def self.format(date)
|
55
|
+
date.iso8601
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright 2016 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
|
+
require 'date'
|
15
|
+
|
16
|
+
module Aws
|
17
|
+
module Record
|
18
|
+
module Marshalers
|
19
|
+
|
20
|
+
class DateTimeMarshaler
|
21
|
+
def initialize(opts = {})
|
22
|
+
@formatter = opts[:formatter] || Iso8601Formatter
|
23
|
+
@use_local_time = opts[:use_local_time] ? true : false
|
24
|
+
end
|
25
|
+
|
26
|
+
def type_cast(raw_value)
|
27
|
+
value = _format(raw_value)
|
28
|
+
if !@use_local_time && value.is_a?(::DateTime)
|
29
|
+
value.new_offset(0)
|
30
|
+
else
|
31
|
+
value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialize(raw_value)
|
36
|
+
datetime = type_cast(raw_value)
|
37
|
+
if datetime.nil?
|
38
|
+
nil
|
39
|
+
elsif datetime.is_a?(::DateTime)
|
40
|
+
@formatter.format(datetime)
|
41
|
+
else
|
42
|
+
msg = "expected a DateTime value or nil, got #{datetime.class}"
|
43
|
+
raise ArgumentError, msg
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def _format(raw_value)
|
49
|
+
case raw_value
|
50
|
+
when nil
|
51
|
+
nil
|
52
|
+
when ''
|
53
|
+
nil
|
54
|
+
when ::DateTime
|
55
|
+
raw_value
|
56
|
+
when Integer # timestamp
|
57
|
+
::DateTime.parse(Time.at(raw_value).to_s)
|
58
|
+
else # Time, Date or String
|
59
|
+
::DateTime.parse(raw_value.to_s)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module Iso8601Formatter
|
65
|
+
def self.format(datetime)
|
66
|
+
datetime.iso8601
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright 2016 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
|
+
module Marshalers
|
17
|
+
|
18
|
+
class FloatMarshaler
|
19
|
+
def initialize(opts = {})
|
20
|
+
end
|
21
|
+
|
22
|
+
def type_cast(raw_value)
|
23
|
+
case raw_value
|
24
|
+
when nil
|
25
|
+
nil
|
26
|
+
when ''
|
27
|
+
nil
|
28
|
+
when Float
|
29
|
+
raw_value
|
30
|
+
else
|
31
|
+
raw_value.respond_to?(:to_f) ?
|
32
|
+
raw_value.to_f :
|
33
|
+
raw_value.to_s.to_f
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def serialize(raw_value)
|
38
|
+
float = type_cast(raw_value)
|
39
|
+
if float.nil?
|
40
|
+
nil
|
41
|
+
elsif float.is_a?(Float)
|
42
|
+
float
|
43
|
+
else
|
44
|
+
msg = "expected a Float value or nil, got #{float.class}"
|
45
|
+
raise ArgumentError, msg
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|