aws-record 1.0.0.pre.4 → 1.0.0.pre.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/aws-record/record.rb +12 -1
- data/lib/aws-record/record/attribute.rb +2 -2
- data/lib/aws-record/record/attributes.rb +4 -4
- data/lib/aws-record/record/errors.rb +6 -2
- data/lib/aws-record/record/item_collection.rb +11 -6
- data/lib/aws-record/record/item_operations.rb +128 -10
- data/lib/aws-record/record/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3fb756f0dd0d63461d393e36ec6059482648f79
|
4
|
+
data.tar.gz: 8f3ea1c9ea27cf57f7a2aa26d0096d62e8c6f4cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bda87438c533121bb4cc80f2e7dca54f2a0757f613d4993790c1519733e99c6c3d3df2c54e72c14c24173d8c424dc6368be0ede2b27d86b417847be3152c8fd8
|
7
|
+
data.tar.gz: 2b9a63d40bb40daa48dee6ec115950fe8c7ea85a0c43b80ed7dc64d12936eb7e5b5504343ffbea7b8a26cb446bc37e5241b497452a9a3022902e6817be8cb2e4
|
data/lib/aws-record/record.rb
CHANGED
@@ -13,6 +13,17 @@
|
|
13
13
|
|
14
14
|
module Aws
|
15
15
|
module Record
|
16
|
+
# @!parse extend RecordClassMethods
|
17
|
+
# @!parse include Attributes
|
18
|
+
# @!parse extend Attributes::ClassMethods
|
19
|
+
# @!parse include ItemOperations
|
20
|
+
# @!parse extend ItemOperations::ItemOperationsClassMethods
|
21
|
+
# @!parse include Query
|
22
|
+
# @!parse extend Query::QueryClassMethods
|
23
|
+
# @!parse include SecondaryIndexes
|
24
|
+
# @!parse extend SecondaryIndexes::SecondaryIndexesClassMethods
|
25
|
+
# @!parse include DirtyTracking
|
26
|
+
# @!parse extend DirtyTracking::DirtyTrackingClassMethods
|
16
27
|
|
17
28
|
# Usage of {Aws::Record} requires only that you include this module. This
|
18
29
|
# method will then pull in the other default modules.
|
@@ -60,7 +71,7 @@ module Aws
|
|
60
71
|
if @table_name
|
61
72
|
@table_name
|
62
73
|
else
|
63
|
-
@table_name = self.name
|
74
|
+
@table_name = self.name.split("::").join("_")
|
64
75
|
end
|
65
76
|
end
|
66
77
|
|
@@ -21,8 +21,8 @@ module Aws
|
|
21
21
|
# safe to use as a method.
|
22
22
|
# @param [Hash] options
|
23
23
|
# @option options [Marshaler] :marshaler The marshaler for this attribute.
|
24
|
-
# So long as you provide a marshaler which implements
|
25
|
-
#
|
24
|
+
# So long as you provide a marshaler which implements +#type_cast+ and
|
25
|
+
# +#serialize+ that consume raw values as expected, you can bring your
|
26
26
|
# own marshaler type.
|
27
27
|
# @option options [Array] :validators An array of validator classes that
|
28
28
|
# will be run when an attribute is checked for validity.
|
@@ -54,8 +54,8 @@ module Aws
|
|
54
54
|
# @param [Symbol] name Name of this attribute. It should be a name that
|
55
55
|
# is safe to use as a method.
|
56
56
|
# @param [Marshaler] marshaler The marshaler for this attribute. So long
|
57
|
-
# as you provide a marshaler which implements
|
58
|
-
#
|
57
|
+
# as you provide a marshaler which implements +#type_cast+ and
|
58
|
+
# +#serialize+ that consume raw values as expected, you can bring your
|
59
59
|
# own marshaler type. Convenience methods will provide this for you.
|
60
60
|
# @param [Hash] opts
|
61
61
|
# @option opts [Array] :validators An array of validator classes that
|
@@ -269,7 +269,7 @@ module Aws
|
|
269
269
|
|
270
270
|
# Define a numeric set attribute for your model.
|
271
271
|
#
|
272
|
-
# Numeric sets are homogeneous sets, containing only
|
272
|
+
# Numeric sets are homogeneous sets, containing only numbers. Note that
|
273
273
|
# empty sets cannot be persisted to DynamoDB. Empty sets are valid for
|
274
274
|
# aws-record items, but they will not be persisted as sets. nil values
|
275
275
|
# from your table, or a lack of value from your table, will be treated
|
@@ -284,7 +284,7 @@ module Aws
|
|
284
284
|
# the hash key for the table.
|
285
285
|
# @option opts [Boolean] :range_key Set to true if this attribute is
|
286
286
|
# the range key for the table.
|
287
|
-
def
|
287
|
+
def numeric_set_attr(name, opts = {})
|
288
288
|
opts[:dynamodb_type] = "NS"
|
289
289
|
attr(name, Attributes::NumericSetMarshaler, opts)
|
290
290
|
end
|
@@ -15,10 +15,14 @@ module Aws
|
|
15
15
|
module Record
|
16
16
|
module Errors
|
17
17
|
|
18
|
-
class
|
18
|
+
class RecordError < RuntimeError; end
|
19
|
+
|
20
|
+
class KeyMissing < RecordError; end
|
21
|
+
class NotFound < RecordError; end
|
22
|
+
class ItemAlreadyExists < RecordError; end
|
23
|
+
|
19
24
|
class NameCollision < RuntimeError; end
|
20
25
|
class ReservedName < RuntimeError; end
|
21
|
-
class NotFound < RuntimeError; end
|
22
26
|
class InvalidModel < RuntimeError; end
|
23
27
|
class TableDoesNotExist < RuntimeError; end
|
24
28
|
|
@@ -12,17 +12,18 @@ module Aws
|
|
12
12
|
|
13
13
|
def each(&block)
|
14
14
|
return enum_for(:each) unless block_given?
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@result.each_page do |page|
|
19
|
-
items = _build_items_from_response(page.items, @model)
|
20
|
-
items.each do |item|
|
15
|
+
items.each_page do |page|
|
16
|
+
items_array = _build_items_from_response(page.items, @model)
|
17
|
+
items_array.each do |item|
|
21
18
|
yield item
|
22
19
|
end
|
23
20
|
end
|
24
21
|
end
|
25
22
|
|
23
|
+
def empty?
|
24
|
+
items.empty?
|
25
|
+
end
|
26
|
+
|
26
27
|
private
|
27
28
|
def _build_items_from_response(items, model)
|
28
29
|
ret = []
|
@@ -37,6 +38,10 @@ module Aws
|
|
37
38
|
ret
|
38
39
|
end
|
39
40
|
|
41
|
+
def items
|
42
|
+
@_items ||= @client.send(@search_method, @search_params)
|
43
|
+
end
|
44
|
+
|
40
45
|
end
|
41
46
|
end
|
42
47
|
end
|
@@ -18,20 +18,96 @@ module Aws
|
|
18
18
|
# @api private
|
19
19
|
def self.included(sub_class)
|
20
20
|
sub_class.extend(ItemOperationsClassMethods)
|
21
|
+
sub_class.instance_variable_set("@errors", [])
|
21
22
|
end
|
22
23
|
|
23
|
-
# Saves this instance of an item to Amazon DynamoDB
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
24
|
+
# Saves this instance of an item to Amazon DynamoDB. If this item is "new"
|
25
|
+
# as defined by having new or altered key attributes, will attempt a
|
26
|
+
# conditional
|
27
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
|
28
|
+
# call, which will not overwrite an existing item. If the item only has
|
29
|
+
# altered non-key attributes, will perform an
|
30
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
|
31
|
+
# call. Uses this item instance's attributes in order to build the
|
32
|
+
# request on your behalf.
|
27
33
|
#
|
34
|
+
# You can use the +:force+ option to perform a simple put/overwrite
|
35
|
+
# without conditional validation or update logic.
|
36
|
+
#
|
37
|
+
# @param [Hash] opts
|
38
|
+
# @option opts [Boolean] :force if true, will save as a put operation and
|
39
|
+
# overwrite any existing item on the remote end. Otherwise, and by
|
40
|
+
# default, will either perform a conditional put or an update call.
|
28
41
|
# @raise [Aws::Record::Errors::KeyMissing] if a required key attribute
|
29
42
|
# does not have a value within this item instance.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
43
|
+
# @raise [Aws::Record::Errors::ItemAlreadyExists] if a conditional put
|
44
|
+
# fails because the item exists on the remote end.
|
45
|
+
def save!(opts = {})
|
46
|
+
force = opts[:force]
|
47
|
+
expect_new = expect_new_item?
|
48
|
+
if force
|
49
|
+
dynamodb_client.put_item(
|
50
|
+
table_name: self.class.table_name,
|
51
|
+
item: build_item_for_save
|
52
|
+
)
|
53
|
+
elsif expect_new
|
54
|
+
put_opts = {
|
55
|
+
table_name: self.class.table_name,
|
56
|
+
item: build_item_for_save
|
57
|
+
}.merge(prevent_overwrite_expression)
|
58
|
+
begin
|
59
|
+
dynamodb_client.put_item(put_opts)
|
60
|
+
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
61
|
+
raise Errors::ItemAlreadyExists.new(
|
62
|
+
"Conditional #put_item call failed, an item with the same key"\
|
63
|
+
" already exists in the table. Either load and update this"\
|
64
|
+
" item, or include the :force option to clobber the remote"\
|
65
|
+
" item."
|
66
|
+
)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
dynamodb_client.update_item(
|
70
|
+
table_name: self.class.table_name,
|
71
|
+
key: key_values,
|
72
|
+
attribute_updates: dirty_changes_for_update
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Saves this instance of an item to Amazon DynamoDB. If this item is "new"
|
78
|
+
# as defined by having new or altered key attributes, will attempt a
|
79
|
+
# conditional
|
80
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
|
81
|
+
# call, which will not overwrite an existing item. If the item only has
|
82
|
+
# altered non-key attributes, will perform an
|
83
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
|
84
|
+
# call. Uses this item instance's attributes in order to build the
|
85
|
+
# request on your behalf.
|
86
|
+
#
|
87
|
+
# You can use the +:force+ option to perform a simple put/overwrite
|
88
|
+
# without conditional validation or update logic.
|
89
|
+
#
|
90
|
+
# In the case where persistence fails, will populate the +errors+ array
|
91
|
+
# with any generated error messages, and will cause +#valid?+ to return
|
92
|
+
# false until there is a successful save.
|
93
|
+
#
|
94
|
+
# @param [Hash] opts
|
95
|
+
# @option opts [Boolean] :force if true, will save as a put operation and
|
96
|
+
# overwrite any existing item on the remote end. Otherwise, and by
|
97
|
+
# default, will either perform a conditional put or an update call.
|
98
|
+
def save(opts = {})
|
99
|
+
result = save!(opts)
|
100
|
+
errors.clear
|
101
|
+
result
|
102
|
+
rescue Errors::RecordError => e
|
103
|
+
errors << e.message
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
107
|
+
# Checks if the record is a valid record. +false+ if most recent +#save+
|
108
|
+
# call raised errors, or if there are missing keys. +true+ otherwise.
|
109
|
+
def valid?
|
110
|
+
errors.empty? && missing_key_values.empty?
|
35
111
|
end
|
36
112
|
|
37
113
|
# Deletes the item instance that matches the key values of this item
|
@@ -46,6 +122,10 @@ module Aws
|
|
46
122
|
true
|
47
123
|
end
|
48
124
|
|
125
|
+
def errors
|
126
|
+
self.class.instance_variable_get("@errors")
|
127
|
+
end
|
128
|
+
|
49
129
|
private
|
50
130
|
def build_item_for_save
|
51
131
|
validate_key_values
|
@@ -84,6 +164,44 @@ module Aws
|
|
84
164
|
end
|
85
165
|
end
|
86
166
|
|
167
|
+
def expect_new_item?
|
168
|
+
# Algorithm: Are keys dirty? If so, we treat as new.
|
169
|
+
self.class.keys.any? do |_, attr_name|
|
170
|
+
attribute_dirty?(attr_name)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def prevent_overwrite_expression
|
175
|
+
conditions = []
|
176
|
+
expression_attribute_names = {}
|
177
|
+
# Hash Key
|
178
|
+
conditions << "attribute_not_exists(#H)"
|
179
|
+
expression_attribute_names["#H"] = self.class.hash_key.database_name
|
180
|
+
# Range Key
|
181
|
+
if self.class.range_key
|
182
|
+
conditions << "attribute_not_exists(#R)"
|
183
|
+
expression_attribute_names["#R"] = self.class.range_key.database_name
|
184
|
+
end
|
185
|
+
{
|
186
|
+
condition_expression: conditions.join(" and "),
|
187
|
+
expression_attribute_names: expression_attribute_names
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
def dirty_changes_for_update
|
192
|
+
attributes = self.class.attributes
|
193
|
+
ret = dirty.inject({}) do |acc, attr_name|
|
194
|
+
key = attributes[attr_name].database_name
|
195
|
+
value = {
|
196
|
+
value: attributes[attr_name].serialize(@data[attr_name]),
|
197
|
+
action: "PUT"
|
198
|
+
}
|
199
|
+
acc[key] = value
|
200
|
+
acc
|
201
|
+
end
|
202
|
+
ret
|
203
|
+
end
|
204
|
+
|
87
205
|
module ItemOperationsClassMethods
|
88
206
|
|
89
207
|
# @example Usage Example
|
@@ -92,7 +210,7 @@ module Aws
|
|
92
210
|
# integer_attr :id, hash_key: true
|
93
211
|
# string_attr :name, range_key: true
|
94
212
|
# end
|
95
|
-
#
|
213
|
+
#
|
96
214
|
# MyModel.find(id: 1, name: "First")
|
97
215
|
#
|
98
216
|
# @param [Hash] opts attribute-value pairs for the key you wish to
|
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.0.pre.
|
4
|
+
version: 1.0.0.pre.5
|
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: 2016-
|
11
|
+
date: 2016-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-resources
|
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
73
|
version: 1.3.1
|
74
74
|
requirements: []
|
75
75
|
rubyforge_project:
|
76
|
-
rubygems_version: 2.
|
76
|
+
rubygems_version: 2.5.1
|
77
77
|
signing_key:
|
78
78
|
specification_version: 4
|
79
79
|
summary: AWS Record library for Amazon DynamoDB
|