aws-record 1.0.0.pre.7 → 1.0.0.pre.8
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/attribute.rb +8 -0
- data/lib/aws-record/record/attributes.rb +21 -1
- data/lib/aws-record/record/dirty_tracking.rb +2 -1
- data/lib/aws-record/record/errors.rb +27 -1
- data/lib/aws-record/record/item_collection.rb +13 -0
- data/lib/aws-record/record/item_operations.rb +70 -8
- data/lib/aws-record/record/query.rb +53 -0
- data/lib/aws-record/record/table_migration.rb +27 -13
- data/lib/aws-record/record/version.rb +1 -1
- data/lib/aws-record/record.rb +16 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 529b219269ff7db06904ac8069cd9d03c5a39b2f
|
4
|
+
data.tar.gz: de636bcd9279040598617f1cad6b9601f583a4fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0466dcde49ea6a9f0a026b806fba07f3e4152d7d115c8c6f89bcf1955903cd92ada636e51b72c161a8cb3f14178948514bf1e5bc11aa72995541f69559b81255
|
7
|
+
data.tar.gz: be394920aba460d6f28d1087077a13f17e25c4f07eb93f24e4ccdf1a7abb222f8d946db3766e6817966659c524d0e550a9273b95be657434bee5e82ba088697b
|
@@ -13,6 +13,12 @@
|
|
13
13
|
|
14
14
|
module Aws
|
15
15
|
module Record
|
16
|
+
|
17
|
+
# This class provides helper methods for +Aws::Record+ attributes. These
|
18
|
+
# include marshalers for type casting of item attributes, the Amazon
|
19
|
+
# DynamoDB type for use in certain table and item operation calls, and the
|
20
|
+
# ability to define a database name that is separate from the name used
|
21
|
+
# within the model class and item instances.
|
16
22
|
class Attribute
|
17
23
|
|
18
24
|
attr_reader :name, :database_name, :dynamodb_type
|
@@ -66,6 +72,8 @@ module Aws
|
|
66
72
|
|
67
73
|
end
|
68
74
|
|
75
|
+
# This is an identity marshaler, which performs no changes for type casting
|
76
|
+
# or serialization. It is generally not recommended for use.
|
69
77
|
module DefaultMarshaler
|
70
78
|
def self.type_cast(raw_value, options = {})
|
71
79
|
raw_value
|
@@ -22,8 +22,28 @@ module Aws
|
|
22
22
|
sub_class.instance_variable_set("@storage_attributes", {})
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
# @example Usage Example
|
26
|
+
# class MyModel
|
27
|
+
# include Aws::Record
|
28
|
+
# integer_attr :id, hash_key: true
|
29
|
+
# string_attr :name, range_key: true
|
30
|
+
# string_attr :body
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# item = MyModel.new(id: 1, name: "Quick Create")
|
34
|
+
#
|
35
|
+
# Base initialization method for a new item. Optionally, allows you to
|
36
|
+
# provide initial attribute values for the model. You do not need to
|
37
|
+
# provide all, or even any, attributes at item creation time.
|
38
|
+
#
|
39
|
+
# @param [Hash] attr_values Attribute symbol/value pairs for any initial
|
40
|
+
# attribute values you wish to set.
|
41
|
+
# @return [Aws::Record] An item instance for your model.
|
42
|
+
def initialize(attr_values = {})
|
26
43
|
@data = {}
|
44
|
+
attr_values.each do |attr_name, attr_value|
|
45
|
+
send("#{attr_name}=", attr_value)
|
46
|
+
end
|
27
47
|
end
|
28
48
|
|
29
49
|
# Returns a hash representation of the attribute data.
|
@@ -15,16 +15,42 @@ module Aws
|
|
15
15
|
module Record
|
16
16
|
module Errors
|
17
17
|
|
18
|
+
# RecordErrors relate to the persistence of items. They include both
|
19
|
+
# client errors and certain validation errors.
|
18
20
|
class RecordError < RuntimeError; end
|
19
21
|
|
22
|
+
# Raised when a required key attribute is missing from an item when
|
23
|
+
# persistence is attempted.
|
20
24
|
class KeyMissing < RecordError; end
|
25
|
+
|
26
|
+
# Raised when you attempt to load a record from the database, but it does
|
27
|
+
# not exist there.
|
21
28
|
class NotFound < RecordError; end
|
22
|
-
|
29
|
+
|
30
|
+
# Raised when a conditional write fails.
|
31
|
+
class ConditionalWriteFailed < RecordError; end
|
32
|
+
|
33
|
+
# Raised when a validation hook call to +:valid?+ fails.
|
23
34
|
class ValidationError < RecordError; end
|
24
35
|
|
36
|
+
# Raised when an attribute is defined that has a name collision with an
|
37
|
+
# existing attribute.
|
25
38
|
class NameCollision < RuntimeError; end
|
39
|
+
|
40
|
+
# Raised when you attempt to create an attribute which has a name that
|
41
|
+
# conflicts with reserved names (generally, defined method names). If you
|
42
|
+
# see this error, you should change the attribute name in the model. If
|
43
|
+
# the database uses this name, you can take advantage of the
|
44
|
+
# +:database_attribute_name+ option in
|
45
|
+
# {Aws::Record::Attributes::ClassMethods#attr #attr}
|
26
46
|
class ReservedName < RuntimeError; end
|
47
|
+
|
48
|
+
# Raised when you attempt a table migration and your model class is
|
49
|
+
# invalid.
|
27
50
|
class InvalidModel < RuntimeError; end
|
51
|
+
|
52
|
+
# Raised when you attempt update/delete operations on a table that does
|
53
|
+
# not exist.
|
28
54
|
class TableDoesNotExist < RuntimeError; end
|
29
55
|
|
30
56
|
end
|
@@ -1,3 +1,16 @@
|
|
1
|
+
# Copyright 2015-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
|
+
|
1
14
|
module Aws
|
2
15
|
module Record
|
3
16
|
class ItemCollection
|
@@ -39,8 +39,8 @@ module Aws
|
|
39
39
|
# default, will either perform a conditional put or an update call.
|
40
40
|
# @raise [Aws::Record::Errors::KeyMissing] if a required key attribute
|
41
41
|
# does not have a value within this item instance.
|
42
|
-
# @raise [Aws::Record::Errors::
|
43
|
-
# fails because the item exists on the remote end.
|
42
|
+
# @raise [Aws::Record::Errors::ConditionalWriteFailed] if a conditional
|
43
|
+
# put fails because the item exists on the remote end.
|
44
44
|
# @raise [Aws::Record::Errors::ValidationError] if the item responds to
|
45
45
|
# +:valid?+ and that call returned false. In such a case, checking root
|
46
46
|
# cause is dependent on the validation library you are using.
|
@@ -122,11 +122,10 @@ module Aws
|
|
122
122
|
begin
|
123
123
|
dynamodb_client.put_item(put_opts)
|
124
124
|
rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
|
125
|
-
raise Errors::
|
126
|
-
"Conditional #put_item call failed
|
127
|
-
"
|
128
|
-
"
|
129
|
-
" item."
|
125
|
+
raise Errors::ConditionalWriteFailed.new(
|
126
|
+
"Conditional #put_item call failed! Check that conditional write"\
|
127
|
+
" conditions are met, or include the :force option to clobber"\
|
128
|
+
" the remote item."
|
130
129
|
)
|
131
130
|
end
|
132
131
|
else
|
@@ -237,7 +236,7 @@ module Aws
|
|
237
236
|
"Missing required key #{attr_sym} in #{opts}"
|
238
237
|
)
|
239
238
|
end
|
240
|
-
attr_name = attr_sym.
|
239
|
+
attr_name = attributes[attr_sym].database_name
|
241
240
|
key[attr_name] = attributes[attr_sym].serialize(opts[attr_sym])
|
242
241
|
end
|
243
242
|
request_opts = {
|
@@ -252,6 +251,69 @@ module Aws
|
|
252
251
|
end
|
253
252
|
end
|
254
253
|
|
254
|
+
# @example Usage Example
|
255
|
+
# class MyModel
|
256
|
+
# include Aws::Record
|
257
|
+
# integer_attr :id, hash_key: true
|
258
|
+
# string_attr :name, range_key: true
|
259
|
+
# string_attr :body
|
260
|
+
# boolean_attr :sir_not_appearing_in_this_example
|
261
|
+
# end
|
262
|
+
#
|
263
|
+
# MyModel.update(id: 1, name: "First", body: "Hello!")
|
264
|
+
#
|
265
|
+
# Performs an
|
266
|
+
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
|
267
|
+
# call immediately on the table, using the attribute key/value pairs
|
268
|
+
# provided.
|
269
|
+
#
|
270
|
+
# @param [Hash] opts attribute-value pairs for the update operation you
|
271
|
+
# wish to perform. You must include all key attributes for a valid
|
272
|
+
# call, then you may optionally include any other attributes that you
|
273
|
+
# wish to update.
|
274
|
+
# @raise [Aws::Record::Errors::KeyMissing] if your option parameters do
|
275
|
+
# not include all table keys.
|
276
|
+
def update(opts)
|
277
|
+
key = {}
|
278
|
+
updates = {}
|
279
|
+
@keys.each_value do |attr_sym|
|
280
|
+
unless value = opts.delete(attr_sym)
|
281
|
+
raise Errors::KeyMissing.new(
|
282
|
+
"Missing required key #{attr_sym} in #{opts}"
|
283
|
+
)
|
284
|
+
end
|
285
|
+
attr_name = attributes[attr_sym].database_name
|
286
|
+
key[attr_name] = attributes[attr_sym].serialize(value)
|
287
|
+
end
|
288
|
+
request_opts = {
|
289
|
+
table_name: table_name,
|
290
|
+
key: key
|
291
|
+
}
|
292
|
+
update_expressions = []
|
293
|
+
exp_attr_names = {}
|
294
|
+
exp_attr_values = {}
|
295
|
+
name_sub_token = "A"
|
296
|
+
value_sub_token = "a"
|
297
|
+
opts.each do |attr_sym, value|
|
298
|
+
name_sub = "#" + name_sub_token
|
299
|
+
value_sub = ":" + value_sub_token
|
300
|
+
name_sub_token = name_sub_token.succ
|
301
|
+
value_sub_token = value_sub_token.succ
|
302
|
+
|
303
|
+
attr_name = attributes[attr_sym].database_name
|
304
|
+
update_expressions << "#{name_sub} = #{value_sub}"
|
305
|
+
exp_attr_names[name_sub] = attr_name
|
306
|
+
exp_attr_values[value_sub] = attributes[attr_sym].serialize(value)
|
307
|
+
end
|
308
|
+
unless update_expressions.empty?
|
309
|
+
uex = "SET " + update_expressions.join(", ")
|
310
|
+
request_opts[:update_expression] = uex
|
311
|
+
request_opts[:expression_attribute_names] = exp_attr_names
|
312
|
+
request_opts[:expression_attribute_values] = exp_attr_values
|
313
|
+
end
|
314
|
+
dynamodb_client.update_item(request_opts)
|
315
|
+
end
|
316
|
+
|
255
317
|
private
|
256
318
|
def build_item_from_resp(resp)
|
257
319
|
record = new
|
@@ -27,6 +27,35 @@ module Aws
|
|
27
27
|
# populating the +:table_name+ parameter from the model class, and
|
28
28
|
# combining this with the other parameters you provide.
|
29
29
|
#
|
30
|
+
# @example A query with key and filter expressions:
|
31
|
+
# # Example model class
|
32
|
+
# class ExampleTable
|
33
|
+
# include Aws::Record
|
34
|
+
# string_attr :uuid, hash_key: true
|
35
|
+
# integer_attr :id, range_key: true
|
36
|
+
# string_attr :body
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# query = ExampleTable.query(
|
40
|
+
# key_condition_expression: "#H = :h AND #R > :r",
|
41
|
+
# filter_expression: "contains(#B, :b)",
|
42
|
+
# expression_attribute_names: {
|
43
|
+
# "#H" => "uuid",
|
44
|
+
# "#R" => "id",
|
45
|
+
# "#B" => "body"
|
46
|
+
# },
|
47
|
+
# expression_attribute_values: {
|
48
|
+
# ":h" => "123456789uuid987654321",
|
49
|
+
# ":r" => 100,
|
50
|
+
# ":b" => "some substring"
|
51
|
+
# }
|
52
|
+
# )
|
53
|
+
#
|
54
|
+
# # You can enumerate over your results.
|
55
|
+
# query.each do |r|
|
56
|
+
# puts "UUID: #{r.uuid}\nID: #{r.id}\nBODY: #{r.body}\n"
|
57
|
+
# end
|
58
|
+
#
|
30
59
|
# @param [Hash] opts options to pass on to the client call to +#query+.
|
31
60
|
# See the documentation above in the AWS SDK for Ruby V2.
|
32
61
|
# @return [Aws::Record::ItemCollection] an enumerable collection of the
|
@@ -41,6 +70,30 @@ module Aws
|
|
41
70
|
# populating the +:table_name+ parameter from the model class, and
|
42
71
|
# combining this with the other parameters you provide.
|
43
72
|
#
|
73
|
+
# @example A scan with a filter expression:
|
74
|
+
# # Example model class
|
75
|
+
# class ExampleTable
|
76
|
+
# include Aws::Record
|
77
|
+
# string_attr :uuid, hash_key: true
|
78
|
+
# integer_attr :id, range_key: true
|
79
|
+
# string_attr :body
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# scan = ExampleTable.scan(
|
83
|
+
# filter_expression: "contains(#B, :b)",
|
84
|
+
# expression_attribute_names: {
|
85
|
+
# "#B" => "body"
|
86
|
+
# },
|
87
|
+
# expression_attribute_values: {
|
88
|
+
# ":b" => "some substring"
|
89
|
+
# }
|
90
|
+
# )
|
91
|
+
#
|
92
|
+
# # You can enumerate over your results.
|
93
|
+
# scan.each do |r|
|
94
|
+
# puts "UUID: #{r.uuid}\nID: #{r.id}\nBODY: #{r.body}\n"
|
95
|
+
# end
|
96
|
+
#
|
44
97
|
# @param [Hash] opts options to pass on to the client call to +#scan+.
|
45
98
|
# See the documentation above in the AWS SDK for Ruby V2.
|
46
99
|
# @return [Aws::Record::ItemCollection] an enumerable collection of the
|
@@ -29,7 +29,7 @@ module Aws
|
|
29
29
|
# class. If this option is not included, a client will be constructed for
|
30
30
|
# you with default parameters.
|
31
31
|
def initialize(model, opts = {})
|
32
|
-
|
32
|
+
_assert_model_valid(model)
|
33
33
|
@model = model
|
34
34
|
@client = opts[:client] || Aws::DynamoDB::Client.new
|
35
35
|
end
|
@@ -39,6 +39,20 @@ module Aws
|
|
39
39
|
# populating the attribute definitions and key schema based on your model
|
40
40
|
# class, as well as passing through other parameters as provided by you.
|
41
41
|
#
|
42
|
+
# @example Creating a table with a global secondary index named +:gsi+
|
43
|
+
# migration.create!(
|
44
|
+
# provisioned_throughput: {
|
45
|
+
# read_capacity_units: 5,
|
46
|
+
# write_capacity_units: 2
|
47
|
+
# },
|
48
|
+
# global_secondary_index_throughput: {
|
49
|
+
# gsi: {
|
50
|
+
# read_capacity_units: 3,
|
51
|
+
# write_capacity_units: 1
|
52
|
+
# }
|
53
|
+
# }
|
54
|
+
# )
|
55
|
+
#
|
42
56
|
# @param [Hash] opts options to pass on to the client call to
|
43
57
|
# +#create_table+. See the documentation above in the AWS SDK for Ruby
|
44
58
|
# V2.
|
@@ -53,8 +67,8 @@ module Aws
|
|
53
67
|
gsit = opts.delete(:global_secondary_index_throughput)
|
54
68
|
create_opts = opts.merge({
|
55
69
|
table_name: @model.table_name,
|
56
|
-
attribute_definitions:
|
57
|
-
key_schema:
|
70
|
+
attribute_definitions: _attribute_definitions,
|
71
|
+
key_schema: _key_schema
|
58
72
|
})
|
59
73
|
if lsis = @model.local_secondary_indexes_for_migration
|
60
74
|
create_opts[:local_secondary_indexes] = lsis
|
@@ -117,25 +131,25 @@ module Aws
|
|
117
131
|
end
|
118
132
|
|
119
133
|
private
|
120
|
-
def
|
121
|
-
|
122
|
-
|
134
|
+
def _assert_model_valid(model)
|
135
|
+
_assert_required_include(model)
|
136
|
+
_assert_keys(model)
|
123
137
|
end
|
124
138
|
|
125
|
-
def
|
139
|
+
def _assert_required_include(model)
|
126
140
|
unless model.include?(::Aws::Record)
|
127
141
|
raise Errors::InvalidModel.new("Table models must include Aws::Record")
|
128
142
|
end
|
129
143
|
end
|
130
144
|
|
131
|
-
def
|
145
|
+
def _assert_keys(model)
|
132
146
|
if model.hash_key.nil?
|
133
147
|
raise Errors::InvalidModel.new("Table models must include a hash key")
|
134
148
|
end
|
135
149
|
end
|
136
150
|
|
137
|
-
def
|
138
|
-
|
151
|
+
def _attribute_definitions
|
152
|
+
_keys.map do |type, attr|
|
139
153
|
{
|
140
154
|
attribute_name: attr.database_name,
|
141
155
|
attribute_type: attr.dynamodb_type
|
@@ -183,8 +197,8 @@ module Aws
|
|
183
197
|
ret
|
184
198
|
end
|
185
199
|
|
186
|
-
def
|
187
|
-
|
200
|
+
def _key_schema
|
201
|
+
_keys.map do |type, attr|
|
188
202
|
{
|
189
203
|
attribute_name: attr.database_name,
|
190
204
|
key_type: type == :hash ? "HASH" : "RANGE"
|
@@ -192,7 +206,7 @@ module Aws
|
|
192
206
|
end
|
193
207
|
end
|
194
208
|
|
195
|
-
def
|
209
|
+
def _keys
|
196
210
|
@model.keys.inject({}) do |acc, (type, name)|
|
197
211
|
acc[type] = @model.attributes[name]
|
198
212
|
acc
|
data/lib/aws-record/record.rb
CHANGED
@@ -12,6 +12,22 @@
|
|
12
12
|
# and limitations under the License.
|
13
13
|
|
14
14
|
module Aws
|
15
|
+
|
16
|
+
# +Aws::Record+ is the module you include in your model classes in order to
|
17
|
+
# decorate them with the Amazon DynamoDB integration methods provided by this
|
18
|
+
# library. Methods you can use are shown below, in sub-modules organized by
|
19
|
+
# functionality.
|
20
|
+
#
|
21
|
+
# @example A class definition using +Aws::Record+
|
22
|
+
# class MyModel
|
23
|
+
# include Aws::Record
|
24
|
+
# string_attr :uuid, hash_key: true
|
25
|
+
# integer_attr :post_id, range_key: true
|
26
|
+
# boolean_attr :is_active
|
27
|
+
# datetime_attr :created_at
|
28
|
+
# string_set_attr :tags
|
29
|
+
# map_attr :metadata
|
30
|
+
# end
|
15
31
|
module Record
|
16
32
|
# @!parse extend RecordClassMethods
|
17
33
|
# @!parse include Attributes
|
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.8
|
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-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-resources
|