aws-record 1.0.0.pre.4 → 1.0.0.pre.5
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/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
|