aws-record 1.0.0.pre.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 84fe186e9a5995f6ca277aa4776a37dfaef73928
4
+ data.tar.gz: 267cf8b8556f23108e5cc3e660177af43ebe0c26
5
+ SHA512:
6
+ metadata.gz: e69390465c8fe8f2eb45eaffeeef9d90a8ded9159a440170e3d5a3258e0679a230f4c6cc50930e2b6cd377f4a132a84389607569c1811238cd858f6a5fb9e830
7
+ data.tar.gz: b2ff7b50b0c2a652f21901d592ed6476fe97bd146cc95c89bdb0fccd34a2ffec3fe4233ea1e764fe72ff53bbf9af5d53e58a7cd4ac10e4717f2c45d9fe778778
@@ -0,0 +1,24 @@
1
+ require 'aws-sdk-resources'
2
+
3
+ module Aws
4
+ autoload :Record, 'aws-record/record'
5
+
6
+ module Record
7
+ autoload :Attribute, 'aws-record/record/attribute'
8
+ autoload :Attributes, 'aws-record/record/attributes'
9
+ autoload :Errors, 'aws-record/record/errors'
10
+ autoload :ItemOperations, 'aws-record/record/item_operations'
11
+ autoload :TableMigration, 'aws-record/record/table_migration'
12
+ autoload :VERSION, 'aws-record/record/version'
13
+
14
+ module Attributes
15
+ autoload :StringMarshaler, 'aws-record/record/attributes/string_marshaler'
16
+ autoload :BooleanMarshaler, 'aws-record/record/attributes/boolean_marshaler'
17
+ autoload :IntegerMarshaler, 'aws-record/record/attributes/integer_marshaler'
18
+ autoload :FloatMarshaler, 'aws-record/record/attributes/float_marshaler'
19
+ autoload :DateMarshaler, 'aws-record/record/attributes/date_marshaler'
20
+ autoload :DateTimeMarshaler, 'aws-record/record/attributes/date_time_marshaler'
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,65 @@
1
+ module Aws
2
+ module Record
3
+ def self.included(sub_class)
4
+ sub_class.extend(RecordClassMethods)
5
+ sub_class.include(Attributes)
6
+ sub_class.include(ItemOperations)
7
+ end
8
+
9
+ private
10
+ def dynamodb_client
11
+ self.class.dynamodb_client
12
+ end
13
+
14
+ module RecordClassMethods
15
+ def table_name
16
+ if @table_name
17
+ @table_name
18
+ else
19
+ @table_name = self.name
20
+ end
21
+ end
22
+
23
+ def set_table_name(name)
24
+ @table_name = name
25
+ end
26
+
27
+ def provisioned_throughput
28
+ begin
29
+ resp = dynamodb_client.describe_table(table_name: @table_name)
30
+ throughput = resp.table.provisioned_throughput
31
+ return {
32
+ read_capacity_units: throughput.read_capacity_units,
33
+ write_capacity_units: throughput.write_capacity_units
34
+ }
35
+ rescue DynamoDB::Errors::ResourceNotFoundException
36
+ raise Record::Errors::TableDoesNotExist
37
+ end
38
+ end
39
+
40
+ def table_exists?
41
+ begin
42
+ resp = dynamodb_client.describe_table(table_name: @table_name)
43
+ if resp.table.table_status == "ACTIVE"
44
+ true
45
+ else
46
+ false
47
+ end
48
+ rescue DynamoDB::Errors::ResourceNotFoundException
49
+ false
50
+ end
51
+ end
52
+
53
+ def configure_client(opts = {})
54
+ provided_client = opts.delete(:client)
55
+ opts[:user_agent_suffix] = user_agent(opts.delete(:user_agent_suffix))
56
+ client = provided_client || Aws::DynamoDB::Client.new(opts)
57
+ @dynamodb_client = client
58
+ end
59
+
60
+ def dynamodb_client
61
+ @dynamodb_client ||= configure_client
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module Aws
2
+ module Record
3
+ class Attribute
4
+
5
+ attr_reader :name, :database_name, :dynamodb_type
6
+
7
+ def initialize(name, options = {})
8
+ @name = name
9
+ @database_name = options[:database_attribute_name] || name.to_s
10
+ @dynamodb_type = options[:dynamodb_type]
11
+ @marshaler = options[:marshaler] || DefaultMarshaler
12
+ @validators = options[:validators] || []
13
+ end
14
+
15
+ def type_cast(raw_value)
16
+ @marshaler.type_cast(raw_value)
17
+ end
18
+
19
+ def serialize(raw_value)
20
+ @marshaler.serialize(raw_value)
21
+ end
22
+
23
+ def valid?(raw_value)
24
+ value = type_cast(raw_value)
25
+ @validators.all? do |validator|
26
+ validator.validate(value)
27
+ end
28
+ end
29
+
30
+ def extract(dynamodb_item)
31
+ dynamodb_item[database_name]
32
+ end
33
+
34
+ end
35
+
36
+ module DefaultMarshaler
37
+ def self.type_cast(raw_value, options = {})
38
+ raw_value
39
+ end
40
+
41
+ def self.serialize(raw_value, options = {})
42
+ raw_value
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,242 @@
1
+ module Aws
2
+ module Record
3
+ module Attributes
4
+
5
+ def self.included(sub_class)
6
+ sub_class.extend(ClassMethods)
7
+ sub_class.instance_variable_set("@keys", {})
8
+ sub_class.instance_variable_set("@attributes", {})
9
+ sub_class.instance_variable_set("@storage_attributes", {})
10
+ end
11
+
12
+ def initialize
13
+ @data = {}
14
+ end
15
+
16
+ # Returns a hash representation of the attribute data.
17
+ #
18
+ # @return [Hash] Map of attribute names to raw values.
19
+ def to_h
20
+ @data.dup
21
+ end
22
+
23
+ module ClassMethods
24
+
25
+ # Define an attribute for your model, providing your own attribute type.
26
+ #
27
+ # @param [Symbol] name Name of this attribute. It should be a name that
28
+ # is safe to use as a method.
29
+ # @param [Marshaler] marshaler The marshaler for this attribute. So long
30
+ # as you provide a marshaler which implements `#type_cast` and
31
+ # `#serialize` that consume raw values as expected, you can bring your
32
+ # own marshaler type. Convenience methods will provide this for you.
33
+ # @param [Hash] options
34
+ # @option options [Array] :validators An array of validator classes that
35
+ # will be run when an attribute is checked for validity.
36
+ # @option options [String] :database_attribute_name Optional attribute
37
+ # used to specify a different name for database persistence than the
38
+ # `name` parameter. Must be unique (you can't have overlap between
39
+ # database attribute names and the names of other attributes).
40
+ # @option options [String] :dynamodb_type Generally used for keys and
41
+ # index attributes, one of "S", "N", "B", "BOOL", "SS", "NS", "BS",
42
+ # "M", "L". Optional if this attribute will never be used for a key or
43
+ # secondary index, but most convenience methods for setting attributes
44
+ # will provide this.
45
+ # @option options [Boolean] :hash_key Set to true if this attribute is
46
+ # the hash key for the table.
47
+ # @option options [Boolean] :range_key Set to true if this attribute is
48
+ # the range key for the table.
49
+ def attr(name, marshaler, opts = {})
50
+ validate_attr_name(name)
51
+
52
+ opts = opts.merge(marshaler: marshaler)
53
+ attribute = Attribute.new(name, opts)
54
+
55
+ storage_name = attribute.database_name
56
+
57
+ check_for_naming_collisions(name, storage_name)
58
+ check_if_reserved(name)
59
+
60
+ @attributes[name] = attribute
61
+ @storage_attributes[storage_name] = name
62
+
63
+ define_attr_methods(name, attribute)
64
+ key_attributes(name, opts)
65
+ end
66
+
67
+ # Define a string-type attribute for your model.
68
+ #
69
+ # @param [Symbol] name Name of this attribute. It should be a name that
70
+ # is safe to use as a method.
71
+ # @param [Hash] options
72
+ # @option options [Boolean] :hash_key Set to true if this attribute is
73
+ # the hash key for the table.
74
+ # @option options [Boolean] :range_key Set to true if this attribute is
75
+ # the range key for the table.
76
+ def string_attr(id, opts = {})
77
+ opts[:dynamodb_type] = "S"
78
+ attr(id, Attributes::StringMarshaler, opts)
79
+ end
80
+
81
+ # Define a boolean-type attribute for your model.
82
+ #
83
+ # @param [Symbol] name Name of this attribute. It should be a name that
84
+ # is safe to use as a method.
85
+ # @param [Hash] options
86
+ # @option options [Boolean] :hash_key Set to true if this attribute is
87
+ # the hash key for the table.
88
+ # @option options [Boolean] :range_key Set to true if this attribute is
89
+ # the range key for the table.
90
+ def boolean_attr(id, opts = {})
91
+ opts[:dynamodb_type] = "BOOL"
92
+ attr(id, Attributes::BooleanMarshaler, opts)
93
+ end
94
+
95
+ # Define a integer-type attribute for your model.
96
+ #
97
+ # @param [Symbol] name Name of this attribute. It should be a name that
98
+ # is safe to use as a method.
99
+ # @param [Hash] options
100
+ # @option options [Boolean] :hash_key Set to true if this attribute is
101
+ # the hash key for the table.
102
+ # @option options [Boolean] :range_key Set to true if this attribute is
103
+ # the range key for the table.
104
+ def integer_attr(id, opts = {})
105
+ opts[:dynamodb_type] = "N"
106
+ attr(id, Attributes::IntegerMarshaler, opts)
107
+ end
108
+
109
+ # Define a float-type attribute for your model.
110
+ #
111
+ # @param [Symbol] name Name of this attribute. It should be a name that
112
+ # is safe to use as a method.
113
+ # @param [Hash] options
114
+ # @option options [Boolean] :hash_key Set to true if this attribute is
115
+ # the hash key for the table.
116
+ # @option options [Boolean] :range_key Set to true if this attribute is
117
+ # the range key for the table.
118
+ def float_attr(id, opts = {})
119
+ opts[:dynamodb_type] = "N"
120
+ attr(id, Attributes::FloatMarshaler, opts)
121
+ end
122
+
123
+ # Define a date-type attribute for your model.
124
+ #
125
+ # @param [Symbol] name Name of this attribute. It should be a name that
126
+ # is safe to use as a method.
127
+ # @param [Hash] options
128
+ # @option options [Boolean] :hash_key Set to true if this attribute is
129
+ # the hash key for the table.
130
+ # @option options [Boolean] :range_key Set to true if this attribute is
131
+ # the range key for the table.
132
+ def date_attr(id, opts = {})
133
+ opts[:dynamodb_type] = "S"
134
+ attr(id, Attributes::DateMarshaler, opts)
135
+ end
136
+
137
+ # Define a datetime-type attribute for your model.
138
+ #
139
+ # @param [Symbol] name Name of this attribute. It should be a name that
140
+ # is safe to use as a method.
141
+ # @param [Hash] options
142
+ # @option options [Boolean] :hash_key Set to true if this attribute is
143
+ # the hash key for the table.
144
+ # @option options [Boolean] :range_key Set to true if this attribute is
145
+ # the range key for the table.
146
+ def datetime_attr(id, opts = {})
147
+ opts[:dynamodb_type] = "S"
148
+ attr(id, Attributes::DateTimeMarshaler, opts)
149
+ end
150
+
151
+ # @return [Hash] hash of symbolized attribute names to attribute objects
152
+ def attributes
153
+ @attributes
154
+ end
155
+
156
+ # @return [Aws::Record::Attribute,nil]
157
+ def hash_key
158
+ @attributes[@keys[:hash]]
159
+ end
160
+
161
+ # @return [Aws::Record::Attribute,nil]
162
+ def range_key
163
+ @attributes[@keys[:range]]
164
+ end
165
+
166
+ # @return [Hash] A mapping of the :hash and :range keys to the attribute
167
+ # name symbols associated with them.
168
+ def keys
169
+ @keys
170
+ end
171
+
172
+ private
173
+ def define_attr_methods(name, attribute)
174
+ define_method(name) do
175
+ raw = @data[name]
176
+ attribute.type_cast(raw)
177
+ end
178
+
179
+ define_method("#{name}=") do |value|
180
+ @data[name] = value
181
+ end
182
+ end
183
+
184
+ def key_attributes(id, opts)
185
+ if opts[:hash_key] == true && opts[:range_key] == true
186
+ raise ArgumentError.new(
187
+ "Cannot have the same attribute be a hash and range key."
188
+ )
189
+ elsif opts[:hash_key] == true
190
+ define_key(id, :hash)
191
+ elsif opts[:range_key] == true
192
+ define_key(id, :range)
193
+ end
194
+ end
195
+
196
+ def define_key(id, type)
197
+ @keys[type] = id
198
+ end
199
+
200
+ def validate_attr_name(name)
201
+ unless name.is_a?(Symbol)
202
+ raise ArgumentError.new("Must use symbolized :name attribute.")
203
+ end
204
+ if @attributes[name]
205
+ raise Errors::NameCollision.new(
206
+ "Cannot overwrite existing attribute #{name}"
207
+ )
208
+ end
209
+ end
210
+
211
+ def check_if_reserved(name)
212
+ if instance_methods.include?(name)
213
+ raise Errors::ReservedName.new(
214
+ "Cannot name an attribute #{name}, that would collide with an"\
215
+ " existing instance method."
216
+ )
217
+ end
218
+ end
219
+
220
+ def check_for_naming_collisions(name, storage_name)
221
+ if @attributes[storage_name]
222
+ raise Errors::NameCollision.new(
223
+ "Custom storage name #{storage_name} already exists as an"\
224
+ " attribute name in #{@attributes}"
225
+ )
226
+ elsif @storage_attributes[name]
227
+ raise Errors::NameCollision.new(
228
+ "Attribute name #{name} already exists as a custom storage"\
229
+ " name in #{@storage_attributes}"
230
+ )
231
+ elsif @storage_attributes[storage_name]
232
+ raise Errors::NameCollision.new(
233
+ "Custom storage name #{storage_name} already in use in"\
234
+ " #{@storage_attributes}"
235
+ )
236
+ end
237
+ end
238
+ end
239
+
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,41 @@
1
+ module Aws
2
+ module Record
3
+ module Attributes
4
+ module BooleanMarshaler
5
+
6
+ class << self
7
+
8
+ def type_cast(raw_value, options = {})
9
+ case raw_value
10
+ when nil
11
+ nil
12
+ when ''
13
+ nil
14
+ when false, 'false', '0', 0
15
+ false
16
+ else
17
+ true
18
+ end
19
+ end
20
+
21
+ def serialize(raw_value, options = {})
22
+ boolean = type_cast(raw_value, options)
23
+ case boolean
24
+ when nil
25
+ nil
26
+ when false
27
+ false
28
+ when true
29
+ true
30
+ else
31
+ msg = "expected a boolean value or nil, got #{boolean.class}"
32
+ raise ArgumentError, msg
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ require 'date'
2
+
3
+ module Aws
4
+ module Record
5
+ module Attributes
6
+ module DateMarshaler
7
+
8
+ class << self
9
+
10
+ def type_cast(raw_value, options = {})
11
+ case raw_value
12
+ when nil
13
+ nil
14
+ when ''
15
+ nil
16
+ when Date
17
+ raw_value
18
+ when Integer
19
+ begin
20
+ Date.parse(Time.at(raw_value).to_s) # assumed timestamp
21
+ rescue
22
+ nil
23
+ end
24
+ else
25
+ begin
26
+ Date.parse(raw_value.to_s) # Time, DateTime or String
27
+ rescue
28
+ nil
29
+ end
30
+ end
31
+ end
32
+
33
+ def serialize(raw_value, options = {})
34
+ date = type_cast(raw_value)
35
+ if date.nil?
36
+ nil
37
+ elsif date.is_a?(Date)
38
+ date.strftime('%Y-%m-%d')
39
+ else
40
+ raise ArgumentError, "expected a Date value or nil, got #{date.class}"
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ require 'date'
2
+
3
+ module Aws
4
+ module Record
5
+ module Attributes
6
+ module DateTimeMarshaler
7
+
8
+ class << self
9
+
10
+ def type_cast(raw_value, options = {})
11
+ case raw_value
12
+ when nil
13
+ nil
14
+ when ''
15
+ nil
16
+ when DateTime
17
+ raw_value
18
+ when Integer
19
+ begin
20
+ DateTime.parse(Time.at(raw_value).to_s) # timestamp
21
+ rescue
22
+ nil
23
+ end
24
+ else
25
+ begin
26
+ DateTime.parse(raw_value.to_s) # Time, Date or String
27
+ rescue
28
+ nil
29
+ end
30
+ end
31
+ end
32
+
33
+ def serialize(raw_value, options = {})
34
+ datetime = type_cast(raw_value)
35
+ if datetime.nil?
36
+ nil
37
+ elsif datetime.is_a?(DateTime)
38
+ datetime.strftime('%Y-%m-%dT%H:%M:%S%Z')
39
+ else
40
+ msg = "expected a DateTime value or nil, got #{datetime.class}"
41
+ raise ArgumentError, msg
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,40 @@
1
+ module Aws
2
+ module Record
3
+ module Attributes
4
+ module FloatMarshaler
5
+
6
+ class << self
7
+
8
+ def type_cast(raw_value, options = {})
9
+ case raw_value
10
+ when nil
11
+ nil
12
+ when ''
13
+ nil
14
+ when Float
15
+ raw_value
16
+ else
17
+ raw_value.respond_to?(:to_f) ?
18
+ raw_value.to_f :
19
+ raw_value.to_s.to_f
20
+ end
21
+ end
22
+
23
+ def serialize(raw_value, options = {})
24
+ float = type_cast(raw_value, options = {})
25
+ if float.nil?
26
+ nil
27
+ elsif float.is_a?(Float)
28
+ float
29
+ else
30
+ msg = "expected a Float value or nil, got #{value.class}"
31
+ raise ArgumentError, msg
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ module Aws
2
+ module Record
3
+ module Attributes
4
+ module IntegerMarshaler
5
+
6
+ class << self
7
+
8
+ def type_cast(raw_value, options = {})
9
+ case raw_value
10
+ when nil
11
+ nil
12
+ when ''
13
+ nil
14
+ when Integer
15
+ raw_value
16
+ else
17
+ raw_value.respond_to?(:to_i) ?
18
+ raw_value.to_i :
19
+ raw_value.to_s.to_i
20
+ end
21
+ end
22
+
23
+ def serialize(raw_value, options = {})
24
+ integer = type_cast(raw_value, options = {})
25
+ if integer.nil?
26
+ nil
27
+ elsif integer.is_a?(Integer)
28
+ integer
29
+ else
30
+ msg = "expected an Integer value or nil, got #{value.class}"
31
+ raise ArgumentError, msg
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,47 @@
1
+ module Aws
2
+ module Record
3
+ module Attributes
4
+ module StringMarshaler
5
+
6
+ class << self
7
+
8
+ def type_cast(raw_value, options = {})
9
+ case raw_value
10
+ when nil
11
+ if options[:nil_as_empty_string]
12
+ ''
13
+ else
14
+ nil
15
+ end
16
+ when String
17
+ if raw_value.empty? && !options[:nil_as_empty_string]
18
+ nil
19
+ else
20
+ raw_value
21
+ end
22
+ else
23
+ raw_value.to_s
24
+ end
25
+ end
26
+
27
+ def serialize(raw_value, options = {})
28
+ value = type_cast(raw_value)
29
+ if value.is_a?(String)
30
+ if value.empty?
31
+ nil
32
+ else
33
+ value
34
+ end
35
+ elsif value.nil?
36
+ nil
37
+ else
38
+ msg = "expected a String value or nil, got #{value.class}"
39
+ raise ArgumentError, msg
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ module Aws
2
+ module Record
3
+ module Errors
4
+
5
+ class KeyMissing < RuntimeError; end
6
+ class NameCollision < RuntimeError; end
7
+ class ReservedName < RuntimeError; end
8
+ class InvalidModel < RuntimeError; end
9
+ class TableDoesNotExist < RuntimeError; end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,107 @@
1
+ module Aws
2
+ module Record
3
+ module ItemOperations
4
+
5
+ def self.included(sub_class)
6
+ sub_class.extend(ItemOperationsClassMethods)
7
+ end
8
+
9
+ def save
10
+ dynamodb_client.put_item(
11
+ table_name: self.class.table_name,
12
+ item: build_item_for_save
13
+ )
14
+ end
15
+
16
+ def delete!
17
+ dynamodb_client.delete_item(
18
+ table_name: self.class.table_name,
19
+ key: key_values
20
+ )
21
+ true
22
+ end
23
+
24
+ private
25
+ def build_item_for_save
26
+ validate_key_values
27
+ attributes = self.class.attributes
28
+ @data.inject({}) do |acc, name_value_pair|
29
+ attr_name, raw_value = name_value_pair
30
+ db_name = attributes[attr_name].database_name
31
+ acc[db_name] = attributes[attr_name].serialize(raw_value)
32
+ acc
33
+ end
34
+ end
35
+
36
+ def key_values
37
+ validate_key_values
38
+ attributes = self.class.attributes
39
+ self.class.keys.inject({}) do |acc, (_, attr_name)|
40
+ db_name = attributes[attr_name].database_name
41
+ acc[db_name] = attributes[attr_name].serialize(@data[attr_name])
42
+ acc
43
+ end
44
+ end
45
+
46
+ def validate_key_values
47
+ missing = missing_key_values
48
+ unless missing.empty?
49
+ raise Errors::KeyMissing.new(
50
+ "Missing required keys: #{missing.join(', ')}"
51
+ )
52
+ end
53
+ end
54
+
55
+ def missing_key_values
56
+ self.class.keys.inject([]) do |acc, key|
57
+ acc << key.last if @data[key.last].nil?
58
+ acc
59
+ end
60
+ end
61
+
62
+ module ItemOperationsClassMethods
63
+ def find(opts)
64
+ key = {}
65
+ @keys.each_value do |attr_sym|
66
+ unless opts[attr_sym]
67
+ raise Errors::KeyMissing.new(
68
+ "Missing required key #{attr_sym} in #{opts}"
69
+ )
70
+ end
71
+ attr_name = attr_sym.to_s
72
+ key[attr_name] = attributes[attr_sym].serialize(opts[attr_sym])
73
+ end
74
+ request_opts = {
75
+ table_name: table_name,
76
+ key: key
77
+ }
78
+ resp = dynamodb_client.get_item(request_opts)
79
+ if resp.item.nil?
80
+ nil
81
+ else
82
+ build_item_from_resp(resp)
83
+ end
84
+ end
85
+
86
+ private
87
+ def build_item_from_resp(resp)
88
+ record = new
89
+ data = record.instance_variable_get("@data")
90
+ attributes.each do |name, attr|
91
+ data[name] = attr.extract(resp.item)
92
+ end
93
+ record
94
+ end
95
+
96
+ def user_agent(custom)
97
+ if custom
98
+ custom
99
+ else
100
+ " aws-record/#{VERSION}"
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,89 @@
1
+ module Aws
2
+ module Record
3
+ class TableMigration
4
+
5
+ attr_accessor :client
6
+
7
+ def initialize(model, client: nil)
8
+ assert_model_valid(model)
9
+ @model = model
10
+ @client = client || Aws::DynamoDB::Client.new
11
+ end
12
+
13
+ def create!(opts)
14
+ create_opts = opts.merge({
15
+ table_name: @model.table_name,
16
+ attribute_definitions: attribute_definitions,
17
+ key_schema: key_schema
18
+ })
19
+ @client.create_table(create_opts)
20
+ end
21
+
22
+ def update!(opts)
23
+ begin
24
+ update_opts = opts.merge({
25
+ table_name: @model.table_name
26
+ })
27
+ @client.update_table(update_opts)
28
+ rescue DynamoDB::Errors::ResourceNotFoundException => e
29
+ raise Errors::TableDoesNotExist.new(e)
30
+ end
31
+ end
32
+
33
+ def delete!
34
+ begin
35
+ @client.delete_table(table_name: @model.table_name)
36
+ rescue DynamoDB::Errors::ResourceNotFoundException => e
37
+ raise Errors::TableDoesNotExist.new(e)
38
+ end
39
+ end
40
+
41
+ def wait_until_available
42
+ @client.wait_until(:table_exists, table_name: @model.table_name)
43
+ end
44
+
45
+ private
46
+ def assert_model_valid(model)
47
+ assert_required_include(model)
48
+ assert_keys(model)
49
+ end
50
+
51
+ def assert_required_include(model)
52
+ unless model.include?(::Aws::Record)
53
+ raise Errors::InvalidModel.new("Table models must include Aws::Record")
54
+ end
55
+ end
56
+
57
+ def assert_keys(model)
58
+ if model.hash_key.nil?
59
+ raise Errors::InvalidModel.new("Table models must include a hash key")
60
+ end
61
+ end
62
+
63
+ def attribute_definitions
64
+ keys.map do |type, attr|
65
+ {
66
+ attribute_name: attr.database_name,
67
+ attribute_type: attr.dynamodb_type
68
+ }
69
+ end
70
+ end
71
+
72
+ def key_schema
73
+ keys.map do |type, attr|
74
+ {
75
+ attribute_name: attr.database_name,
76
+ key_type: type == :hash ? "HASH" : "RANGE"
77
+ }
78
+ end
79
+ end
80
+
81
+ def keys
82
+ @model.keys.inject({}) do |acc, (type, name)|
83
+ acc[type] = @model.attributes[name]
84
+ acc
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ module Aws
2
+ module Record
3
+ VERSION = "1.0.0.pre.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws-record
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.1
5
+ platform: ruby
6
+ authors:
7
+ - Amazon Web Services
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-resources
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ description: Provides an object mapping abstraction for Amazon DynamoDB.
28
+ email:
29
+ - alexwood@amazon.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/aws-record.rb
35
+ - lib/aws-record/record.rb
36
+ - lib/aws-record/record/attribute.rb
37
+ - lib/aws-record/record/attributes.rb
38
+ - lib/aws-record/record/attributes/boolean_marshaler.rb
39
+ - lib/aws-record/record/attributes/date_marshaler.rb
40
+ - lib/aws-record/record/attributes/date_time_marshaler.rb
41
+ - lib/aws-record/record/attributes/float_marshaler.rb
42
+ - lib/aws-record/record/attributes/integer_marshaler.rb
43
+ - lib/aws-record/record/attributes/string_marshaler.rb
44
+ - lib/aws-record/record/errors.rb
45
+ - lib/aws-record/record/item_operations.rb
46
+ - lib/aws-record/record/table_migration.rb
47
+ - lib/aws-record/record/version.rb
48
+ homepage: http://github.com/aws/aws-sdk-ruby-record
49
+ licenses:
50
+ - Apache 2.0
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">"
64
+ - !ruby/object:Gem::Version
65
+ version: 1.3.1
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.2.2
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: AWS Record library for Amazon DynamoDB
72
+ test_files: []
73
+ has_rdoc: