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