aws-record 2.8.0 → 2.10.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2aaa5337431b2b2f59b35245afd1fb9eb4cacb5226f9cc60a2fefeed5bde7619
4
- data.tar.gz: 8e8a225d9abfb3ec8ca8ee30606df05016166bbb10e0affc4d81fbfbcaaa21f4
3
+ metadata.gz: e1d49110ed24d748680a2fe7c1d124e5faa40c1ee224b6f20059cc3252dc8b27
4
+ data.tar.gz: 4efe9261ad7642c172e267f4cec4b614f61f42e7c57970c7561b6eb51c6ac516
5
5
  SHA512:
6
- metadata.gz: f3ca0395934b8b5f464e7208c8b9b55c104475a045e6e93c01d9a8dfbcbbcab5bed6e4d3563ff963d43a93cce79e2918bfba032e4dab83d42168603289aa2bee
7
- data.tar.gz: 5ed32da4f64d414076c16265ae5200beeb2a02225fb03cc9626626df9bdc1e07a48918231576236e59a22edff0e940fb48be582f7a3fb7162353bbeb6cc9a4fc
6
+ metadata.gz: 9d2d80ea4671ab80debef324a8f80b40cb41034b2a426c69d95db48d436d86f1f7c4d1c8458da363595c28de3254c0755d9956f78f3e25dcacea83d695c8cede
7
+ data.tar.gz: ae7e17fb7b3fcd56a3e5f78c92da540bcceb5d4a3ec62ac70b95cf0eaefaedb89a5845b701d5b28eba0221294c6eb1f769800756017e04a812dd1a552dfd43ce
@@ -20,8 +20,19 @@ module Aws
20
20
  model_attributes = ModelAttributes.new(self)
21
21
  sub_class.instance_variable_set("@attributes", model_attributes)
22
22
  sub_class.instance_variable_set("@keys", KeyAttributes.new(model_attributes))
23
+ if Aws::Record.extends_record?(sub_class)
24
+ inherit_attributes(sub_class)
25
+ end
23
26
  end
24
27
 
28
+ # Base initialization method for a new item. Optionally, allows you to
29
+ # provide initial attribute values for the model. You do not need to
30
+ # provide all, or even any, attributes at item creation time.
31
+ #
32
+ # === Inheritance Support
33
+ # Child models will inherit the attributes and keys defined in the parent
34
+ # model. Child models can override attribute keys if defined in their own model.
35
+ # See examples below to see the feature in action.
25
36
  # @example Usage Example
26
37
  # class MyModel
27
38
  # include Aws::Record
@@ -31,11 +42,34 @@ module Aws
31
42
  # end
32
43
  #
33
44
  # item = MyModel.new(id: 1, name: "Quick Create")
45
+ # @example Child model inheriting from Parent model
46
+ # class Animal
47
+ # include Aws::Record
48
+ # string_attr :name, hash_key: true
49
+ # integer_attr :age, default_value: 1
50
+ # end
34
51
  #
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.
52
+ # class Cat < Animal
53
+ # include Aws::Record
54
+ # integer_attr :num_of_wiskers
55
+ # end
38
56
  #
57
+ # cat = Cat.find(name: 'Foo')
58
+ # cat.age # => 1
59
+ # cat.num_of_wiskers = 200
60
+ # @example Child model overrides the hash key
61
+ # class Animal
62
+ # include Aws::Record
63
+ # string_attr :name, hash_key: true
64
+ # integer_attr :age, range_key: true
65
+ # end
66
+ #
67
+ # class Dog < Animal
68
+ # include Aws::Record
69
+ # integer_attr :id, hash_key: true
70
+ # end
71
+ #
72
+ # Dog.keys # => {:hash=>:id, :range=>:age}
39
73
  # @param [Hash] attr_values Attribute symbol/value pairs for any initial
40
74
  # attribute values you wish to set.
41
75
  # @return [Aws::Record] An item instance for your model.
@@ -56,6 +90,27 @@ module Aws
56
90
  @data.hash_copy
57
91
  end
58
92
 
93
+ private
94
+ def self.inherit_attributes(klass)
95
+ superclass_attributes = klass.superclass.instance_variable_get("@attributes")
96
+
97
+ superclass_attributes.attributes.each do |name, attribute|
98
+ subclass_attributes = klass.instance_variable_get("@attributes")
99
+ subclass_attributes.register_superclass_attribute(name, attribute)
100
+ end
101
+
102
+ superclass_keys = klass.superclass.instance_variable_get("@keys")
103
+ subclass_keys = klass.instance_variable_get("@keys")
104
+
105
+ if superclass_keys.hash_key
106
+ subclass_keys.hash_key = superclass_keys.hash_key
107
+ end
108
+
109
+ if superclass_keys.range_key
110
+ subclass_keys.range_key = superclass_keys.range_key
111
+ end
112
+ end
113
+
59
114
  module ClassMethods
60
115
 
61
116
  # Define an attribute for your model, providing your own attribute type.
@@ -1,15 +1,4 @@
1
- # Copyright 2015-2021 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.
1
+ # frozen_string_literal: true
13
2
 
14
3
  module Aws
15
4
  module Record
@@ -17,6 +6,28 @@ module Aws
17
6
  extend ClientConfiguration
18
7
 
19
8
  class << self
9
+ # Provides a thin wrapper to the
10
+ # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method Aws::DynamoDB::Client#batch_write_item}
11
+ # method. Up to 25 +PutItem+ or +DeleteItem+ operations are supported.
12
+ # A single request may write up to 16 MB of data, with each item having a
13
+ # write limit of 400 KB.
14
+ #
15
+ # *Note*: this operation does not support dirty attribute handling,
16
+ # nor does it enforce safe write operations (i.e. update vs new record
17
+ # checks).
18
+ #
19
+ # This call may partially execute write operations. Failed operations
20
+ # are returned as {BatchWrite.unprocessed_items unprocessed_items} (i.e. the
21
+ # table fails to meet requested write capacity). Any unprocessed
22
+ # items may be retried by calling {BatchWrite.execute! .execute!}
23
+ # again. You can determine if the request needs to be retried by calling
24
+ # the {BatchWrite.complete? .complete?} method - which returns +true+
25
+ # when all operations have been completed.
26
+ #
27
+ # Please see
28
+ # {https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.Errors.html#Programming.Errors.BatchOperations Batch Operations and Error Handling}
29
+ # in the DynamoDB Developer Guide for more details.
30
+ #
20
31
  # @example Usage Example
21
32
  # class Breakfast
22
33
  # include Aws::Record
@@ -38,29 +49,7 @@ module Aws
38
49
  # end
39
50
  #
40
51
  # # unprocessed items can be retried by calling Aws::Record::BatchWrite#execute!
41
- # operation.execute! unless operation.complete?
42
- #
43
- # Provides a thin wrapper to the
44
- # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_write_item-instance_method Aws::DynamoDB::Client#batch_write_item}
45
- # method. Up to 25 +PutItem+ or +DeleteItem+ operations are supported.
46
- # A single rquest may write up to 16 MB of data, with each item having a
47
- # write limit of 400 KB.
48
- #
49
- # *Note*: this operation does not support dirty attribute handling,
50
- # nor does it enforce safe write operations (i.e. update vs new record
51
- # checks).
52
- #
53
- # This call may partially execute write operations. Failed operations
54
- # are returned as +Aws::Record::BatchWrite#unprocessed_items+ (i.e. the
55
- # table fails to meet requested write capacity). Any unprocessed
56
- # items may be retried by calling +Aws::Record::BatchWrite#execute!+
57
- # again. You can determine if the request needs to be retried by calling
58
- # the +Aws::Record::BatchWrite#complete?+ method - which returns +true+
59
- # when all operations have been completed.
60
- #
61
- # Please see
62
- # {https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.Errors.html#Programming.Errors.BatchOperations Batch Operations and Error Handling}
63
- # in the DynamoDB Developer Guide for more details.
52
+ # operation.execute! until operation.complete?
64
53
  #
65
54
  # @param [Hash] opts the options you wish to use to create the client.
66
55
  # Note that if you include the option +:client+, all other options
@@ -76,6 +65,81 @@ module Aws
76
65
  block.call(batch)
77
66
  batch.execute!
78
67
  end
68
+
69
+ # Provides support for the
70
+ # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_get_item-instance_method
71
+ # Aws::DynamoDB::Client#batch_get_item} for aws-record models.
72
+ #
73
+ # +Aws::Record::Batch+ is Enumerable and using Enumerable methods will handle
74
+ # paging through all requested keys automatically. Alternatively, a lower level
75
+ # interface is available. You can determine if there are any unprocessed keys by calling
76
+ # {BatchRead.complete? .complete?} and any unprocessed keys can be processed by
77
+ # calling {BatchRead.execute! .execute!}. You can access all processed items
78
+ # through {BatchRead.items .items}.
79
+ #
80
+ # The +batch_get_item+ supports up to 100 operations in a single call and a single
81
+ # operation can retrieve up to 16 MB of data.
82
+ #
83
+ # +Aws::Record::BatchRead+ can take more than 100 item keys. The first 100 requests
84
+ # will be processed and the remaining requests will be stored.
85
+ # When using Enumerable methods, any pending item keys will be automatically
86
+ # processed and the new items will be added to +items+.
87
+ # Alternately, use {BatchRead.execute! .execute!} to process any pending item keys.
88
+ #
89
+ # All processed operations can be accessed by {BatchRead.items items} - which is an
90
+ # array of modeled items from the response. The items will be unordered since
91
+ # DynamoDB does not return items in any particular order.
92
+ #
93
+ # If a requested item does not exist in the database, it is not returned in the response.
94
+ #
95
+ # If there is a returned item from the call and there's no reference model class
96
+ # to be found, the item will not show up under +items+.
97
+ #
98
+ # @example Usage Example
99
+ # class Lunch
100
+ # include Aws::Record
101
+ # integer_attr :id, hash_key: true
102
+ # string_attr :name, range_key: true
103
+ # end
104
+ #
105
+ # class Dessert
106
+ # include Aws::Record
107
+ # integer_attr :id, hash_key: true
108
+ # string_attr :name, range_key: true
109
+ # end
110
+ #
111
+ # # batch operations
112
+ # operation = Aws::Record::Batch.read do |db|
113
+ # db.find(Lunch, id: 1, name: 'Papaya Salad')
114
+ # db.find(Lunch, id: 2, name: 'BLT Sandwich')
115
+ # db.find(Dessert, id: 1, name: 'Apple Pie')
116
+ # end
117
+ #
118
+ # # BatchRead is enumerable and handles pagination
119
+ # operation.each { |item| item.id }
120
+ #
121
+ # # Alternatively, BatchRead provides a lower level
122
+ # # interface through: execute!, complete? and items.
123
+ # # Unprocessed items can be processed by calling:
124
+ # operation.execute! until operation.complete?
125
+ #
126
+ # @param [Hash] opts the options you wish to use to create the client.
127
+ # Note that if you include the option +:client+, all other options
128
+ # will be ignored. See the documentation for other options in the
129
+ # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#initialize-instance_method
130
+ # AWS SDK for Ruby}.
131
+ # @option opts [Aws::DynamoDB::Client] :client allows you to pass in your
132
+ # own pre-configured client.
133
+ # @return [Aws::Record::BatchRead] An instance that contains modeled items
134
+ # from the +BatchGetItem+ result and stores unprocessed keys to be
135
+ # manually processed later.
136
+ def read(opts = {}, &block)
137
+ batch = BatchRead.new(client: _build_client(opts))
138
+ block.call(batch)
139
+ batch.execute!
140
+ batch
141
+ end
142
+
79
143
  end
80
144
  end
81
145
  end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Record
5
+ class BatchRead
6
+ include Enumerable
7
+
8
+ # @api private
9
+ BATCH_GET_ITEM_LIMIT = 100
10
+
11
+ # @param [Aws::DynamoDB::Client] client the DynamoDB SDK client.
12
+ def initialize(opts = {})
13
+ @client = opts[:client]
14
+ end
15
+
16
+ # Append the item keys to a batch read request.
17
+ #
18
+ # See {Batch.read} for example usage.
19
+ # @param [Aws::Record] klass a model class that includes {Aws::Record}
20
+ # @param [Hash] key attribute-value pairs for the key you wish to search for.
21
+ # @raise [Aws::Record::Errors::KeyMissing] if your option parameters
22
+ # do not include all item keys defined in the model.
23
+ # @raise [ArgumentError] if the provided item keys is a duplicate request
24
+ # in the same instance.
25
+ def find(klass, key = {})
26
+ unprocessed_key = format_unprocessed_key(klass, key)
27
+ store_unprocessed_key(klass, unprocessed_key)
28
+ store_item_class(klass, unprocessed_key)
29
+ end
30
+
31
+ # Perform a +batch_get_item+ request.
32
+ #
33
+ # This method processes the first 100 item keys and
34
+ # returns an array of new modeled items.
35
+ #
36
+ # See {Batch.read} for example usage.
37
+ # @return [Array] an array of unordered new items
38
+ def execute!
39
+ operation_keys = unprocessed_keys[0..BATCH_GET_ITEM_LIMIT - 1]
40
+ @unprocessed_keys = unprocessed_keys[BATCH_GET_ITEM_LIMIT..-1] || []
41
+
42
+ operations = build_operations(operation_keys)
43
+ result = @client.batch_get_item(request_items: operations)
44
+ new_items = build_items(result.responses)
45
+ items.concat(new_items)
46
+
47
+ unless result.unprocessed_keys.nil?
48
+ update_unprocessed_keys(result.unprocessed_keys)
49
+ end
50
+
51
+ new_items
52
+ end
53
+
54
+ # Provides an enumeration of the results from the +batch_get_item+ request
55
+ # and handles pagination.
56
+ #
57
+ # Any pending item keys will be automatically processed and be
58
+ # added to the {#items}.
59
+ #
60
+ # See {Batch.read} for example usage.
61
+ # @yieldparam [Aws::Record] item a modeled item
62
+ # @return [Enumerable<BatchRead>] an enumeration over the results of
63
+ # +batch_get_item+ request.
64
+ def each
65
+ return enum_for(:each) unless block_given?
66
+
67
+ @items.each { |item| yield item }
68
+ until complete?
69
+ new_items = execute!
70
+ new_items.each { |new_item| yield new_item }
71
+ end
72
+ end
73
+
74
+ # Indicates if all item keys have been processed.
75
+ #
76
+ # See {Batch.read} for example usage.
77
+ # @return [Boolean] +true+ if all item keys has been processed, +false+ otherwise.
78
+ def complete?
79
+ unprocessed_keys.none?
80
+ end
81
+
82
+ # Returns an array of modeled items. The items are marshaled into classes used in {#find} method.
83
+ # These items will be unordered since DynamoDB does not return items in any particular order.
84
+ #
85
+ # See {Batch.read} for example usage.
86
+ # @return [Array] an array of modeled items from the +batch_get_item+ call.
87
+ def items
88
+ @items ||= []
89
+ end
90
+
91
+ private
92
+
93
+ def unprocessed_keys
94
+ @unprocessed_keys ||= []
95
+ end
96
+
97
+ def item_classes
98
+ @item_classes ||= Hash.new { |h, k| h[k] = [] }
99
+ end
100
+
101
+ def format_unprocessed_key(klass, key)
102
+ item_key = {}
103
+ attributes = klass.attributes
104
+ klass.keys.each_value do |attr_sym|
105
+ unless key[attr_sym]
106
+ raise Errors::KeyMissing, "Missing required key #{attr_sym} in #{key}"
107
+ end
108
+
109
+ attr_name = attributes.storage_name_for(attr_sym)
110
+ item_key[attr_name] = attributes.attribute_for(attr_sym)
111
+ .serialize(key[attr_sym])
112
+ end
113
+ item_key
114
+ end
115
+
116
+ def store_unprocessed_key(klass, unprocessed_key)
117
+ unprocessed_keys << { keys: unprocessed_key, table_name: klass.table_name }
118
+ end
119
+
120
+ def store_item_class(klass, key)
121
+ if item_classes.include?(klass.table_name)
122
+ item_classes[klass.table_name].each do |item|
123
+ if item[:keys] == key && item[:class] != klass
124
+ raise ArgumentError, 'Provided item keys is a duplicate request'
125
+ end
126
+ end
127
+ end
128
+ item_classes[klass.table_name] << { keys: key, class: klass }
129
+ end
130
+
131
+ def build_operations(keys)
132
+ operations = Hash.new { |h, k| h[k] = { keys: [] } }
133
+ keys.each do |key|
134
+ operations[key[:table_name]][:keys] << key[:keys]
135
+ end
136
+ operations
137
+ end
138
+
139
+ def build_items(item_responses)
140
+ new_items = []
141
+ item_responses.each do |table, unprocessed_items|
142
+ unprocessed_items.each do |item|
143
+ item_class = find_item_class(table, item)
144
+ if item_class.nil? && @client.config.logger
145
+ @client.config.logger.warn(
146
+ 'Unexpected response from service.'\
147
+ "Received: #{item}. Skipping above item and continuing"
148
+ )
149
+ else
150
+ new_items << build_item(item, item_class)
151
+ end
152
+ end
153
+ end
154
+ new_items
155
+ end
156
+
157
+ def update_unprocessed_keys(keys)
158
+ keys.each do |table_name, table_values|
159
+ table_values.keys.each do |key|
160
+ unprocessed_keys << { keys: key, table_name: table_name }
161
+ end
162
+ end
163
+ end
164
+
165
+ def find_item_class(table, item)
166
+ selected_item = item_classes[table].find { |item_info| contains_keys?(item, item_info[:keys]) }
167
+ selected_item[:class] if selected_item
168
+ end
169
+
170
+ def contains_keys?(item, keys)
171
+ item.merge(keys) == item
172
+ end
173
+
174
+ def build_item(item, item_class)
175
+ new_item_opts = {}
176
+ item.each do |db_name, value|
177
+ name = item_class.attributes.db_to_attribute_name(db_name)
178
+ new_item_opts[name] = value
179
+ end
180
+ item = item_class.new(new_item_opts)
181
+ item.clean!
182
+ item
183
+ end
184
+ end
185
+ end
186
+ end
@@ -15,12 +15,14 @@ module Aws
15
15
  module Record
16
16
  class BatchWrite
17
17
  # @param [Aws::DynamoDB::Client] client the DynamoDB SDK client.
18
- def initialize(client:)
19
- @client = client
18
+ def initialize(opts = {})
19
+ @client = opts[:client]
20
20
  end
21
21
 
22
22
  # Append a +PutItem+ operation to a batch write request.
23
23
  #
24
+ # See {Batch.write} for example usage.
25
+ #
24
26
  # @param [Aws::Record] record a model class that includes {Aws::Record}.
25
27
  def put(record)
26
28
  table_name, params = record_put_params(record)
@@ -30,6 +32,7 @@ module Aws
30
32
 
31
33
  # Append a +DeleteItem+ operation to a batch write request.
32
34
  #
35
+ # See {Batch.write} for example usage.
33
36
  # @param [Aws::Record] record a model class that includes {Aws::Record}.
34
37
  def delete(record)
35
38
  table_name, params = record_delete_params(record)
@@ -39,6 +42,7 @@ module Aws
39
42
 
40
43
  # Perform a +batch_write_item+ request.
41
44
  #
45
+ # See {Batch.write} for example usage.
42
46
  # @return [Aws::Record::BatchWrite] an instance that provides access to
43
47
  # unprocessed items and allows for retries.
44
48
  def execute!
@@ -49,6 +53,7 @@ module Aws
49
53
 
50
54
  # Indicates if all items have been processed.
51
55
  #
56
+ # See {Batch.write} for example usage.
52
57
  # @return [Boolean] +true+ if +unprocessed_items+ is empty, +false+
53
58
  # otherwise
54
59
  def complete?
@@ -58,6 +63,7 @@ module Aws
58
63
  # Returns all +DeleteItem+ and +PutItem+ operations that have not yet been
59
64
  # processed successfully.
60
65
  #
66
+ # See {Batch.write} for example usage.
61
67
  # @return [Hash] All operations that have not yet successfully completed.
62
68
  def unprocessed_items
63
69
  operations
@@ -26,6 +26,8 @@ module Aws
26
26
  # in doubt, call this method to ensure your client is configured the way
27
27
  # you want it to be configured.
28
28
  #
29
+ # *Note*: {#dynamodb_client} is inherited from a parent model when
30
+ # +configure_client+ is explicitly specified in the parent.
29
31
  # @param [Hash] opts the options you wish to use to create the client.
30
32
  # Note that if you include the option +:client+, all other options
31
33
  # will be ignored. See the documentation for other options in the
@@ -33,15 +35,23 @@ module Aws
33
35
  # @option opts [Aws::DynamoDB::Client] :client allows you to pass in your
34
36
  # own pre-configured client.
35
37
  def configure_client(opts = {})
36
- @dynamodb_client = _build_client(opts)
38
+ if self.class != Module && Aws::Record.extends_record?(self) && opts.empty? &&
39
+ self.superclass.instance_variable_get('@dynamodb_client')
40
+ @dynamodb_client = self.superclass.instance_variable_get('@dynamodb_client')
41
+ else
42
+ @dynamodb_client = _build_client(opts)
43
+ end
37
44
  end
38
45
 
39
46
  # Gets the
40
- # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html}
47
+ # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html Client}
41
48
  # instance that Transactions use. When called for the first time, if
42
49
  # {#configure_client} has not yet been called, will configure a new
43
50
  # client for you with default parameters.
44
51
  #
52
+ # *Note*: +dynamodb_client+ is inherited from a parent model when
53
+ # {configure_client} is explicitly specified in the parent.
54
+ #
45
55
  # @return [Aws::DynamoDB::Client] the Amazon DynamoDB client instance.
46
56
  def dynamodb_client
47
57
  @dynamodb_client ||= configure_client
@@ -54,7 +54,7 @@ module Aws
54
54
  # match your search.
55
55
  #
56
56
  # @return [Array<Aws::Record>] an array of the record items found in the
57
- # first page of reponses from the query or scan call.
57
+ # first page of responses from the query or scan call.
58
58
  def page
59
59
  search_response = items
60
60
  @last_evaluated_key = search_response.last_evaluated_key
@@ -1,15 +1,4 @@
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.
1
+ # frozen_string_literal: true
13
2
 
14
3
  module Aws
15
4
  module Record
@@ -514,6 +503,43 @@ module Aws
514
503
  end
515
504
  end
516
505
 
506
+
507
+ # @example Usage Example
508
+ # class MyModel
509
+ # include Aws::Record
510
+ # integer_attr :id, hash_key: true
511
+ # string_attr :name, range_key: true
512
+ # end
513
+ #
514
+ # # returns a homogenous list of items
515
+ # foo_items = MyModel.find_all(
516
+ # [
517
+ # {id: 1, name: 'n1'},
518
+ # {id: 2, name: 'n2'}
519
+ # ])
520
+ #
521
+ # Provides support for the
522
+ # {https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#batch_get_item-instance_method
523
+ # Aws::DynamoDB::Client#batch_get_item} for your model.
524
+ #
525
+ # This method will take a list of keys and return an instance of +Aws::Record::BatchRead+
526
+ #
527
+ # See {Batch.read} for more details.
528
+ # @param [Array] keys an array of item key hashes you wish to search for.
529
+ # @return [Aws::Record::BatchRead] An instance that contains modeled items
530
+ # from the +BatchGetItem+ result and stores unprocessed keys to be
531
+ # manually processed later.
532
+ # @raise [Aws::Record::Errors::KeyMissing] if your param hashes do not
533
+ # include all the keys defined in model.
534
+ # @raise [ArgumentError] if the provided keys are a duplicate.
535
+ def find_all(keys)
536
+ Aws::Record::Batch.read do |db|
537
+ keys.each do |key|
538
+ db.find(self, key)
539
+ end
540
+ end
541
+ end
542
+
517
543
  # @example Usage Example
518
544
  # class MyModel
519
545
  # include Aws::Record
@@ -32,6 +32,13 @@ module Aws
32
32
  attribute
33
33
  end
34
34
 
35
+ def register_superclass_attribute (name, attribute)
36
+ _new_attr_validation(name, attribute)
37
+ @attributes[name] = attribute.dup
38
+ @storage_attributes[attribute.database_name] = name
39
+ attribute
40
+ end
41
+
35
42
  def attribute_for(name)
36
43
  @attributes[name]
37
44
  end
@@ -20,6 +20,17 @@ module Aws
20
20
  sub_class.instance_variable_set("@local_secondary_indexes", {})
21
21
  sub_class.instance_variable_set("@global_secondary_indexes", {})
22
22
  sub_class.extend(SecondaryIndexesClassMethods)
23
+ if Aws::Record.extends_record?(sub_class)
24
+ inherit_indexes(sub_class)
25
+ end
26
+ end
27
+
28
+ private
29
+ def self.inherit_indexes(klass)
30
+ superclass_lsi = klass.superclass.instance_variable_get("@local_secondary_indexes").dup
31
+ superclass_gsi = klass.superclass.instance_variable_get("@global_secondary_indexes").dup
32
+ klass.instance_variable_set("@local_secondary_indexes", superclass_lsi)
33
+ klass.instance_variable_set("@global_secondary_indexes", superclass_gsi)
23
34
  end
24
35
 
25
36
  module SecondaryIndexesClassMethods
@@ -28,6 +39,8 @@ module Aws
28
39
  # Secondary Indexes in the
29
40
  # {http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html Amazon DynamoDB Developer Guide}.
30
41
  #
42
+ # *Note*: {#local_secondary_indexes} is inherited from a parent model
43
+ # when +local_secondary_index+ is explicitly specified in the parent.
31
44
  # @param [Symbol] name index name for this local secondary index
32
45
  # @param [Hash] opts
33
46
  # @option opts [Symbol] :range_key the range key used by this local
@@ -46,6 +59,8 @@ module Aws
46
59
  # Global Secondary Indexes in the
47
60
  # {http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html Amazon DynamoDB Developer Guide}.
48
61
  #
62
+ # *Note*: {#global_secondary_indexes} is inherited from a parent model
63
+ # when +global_secondary_index+ is explicitly specified in the parent.
49
64
  # @param [Symbol] name index name for this global secondary index
50
65
  # @param [Hash] opts
51
66
  # @option opts [Symbol] :hash_key the hash key used by this global
@@ -60,12 +75,20 @@ module Aws
60
75
  global_secondary_indexes[name] = opts
61
76
  end
62
77
 
78
+ # Returns hash of local secondary index names to the index’s attributes.
79
+ #
80
+ # *Note*: +local_secondary_indexes+ is inherited from a parent model when {#local_secondary_index}
81
+ # is explicitly specified in the parent.
63
82
  # @return [Hash] hash of local secondary index names to the index's
64
83
  # attributes.
65
84
  def local_secondary_indexes
66
85
  @local_secondary_indexes
67
86
  end
68
87
 
88
+ # Returns hash of global secondary index names to the index’s attributes.
89
+ #
90
+ # *Note*: +global_secondary_indexes+ is inherited from a parent model when {#global_secondary_index}
91
+ # is explicitly specified in the parent.
69
92
  # @return [Hash] hash of global secondary index names to the index's
70
93
  # attributes.
71
94
  def global_secondary_indexes
@@ -17,7 +17,19 @@ module Aws
17
17
  # decorate them with the Amazon DynamoDB integration methods provided by this
18
18
  # library. Methods you can use are shown below, in sub-modules organized by
19
19
  # functionality.
20
- #
20
+ # === Inheritance Support
21
+ # Aws Record models can be extended using standard ruby inheritance. The child
22
+ # model must include +Aws::Record+ in their model and the following will
23
+ # be inherited:
24
+ # * {#set_table_name set_table_name}
25
+ # * {#initialize Attributes and keys}
26
+ # * Mutation Tracking:
27
+ # * {#enable_mutation_tracking enable_mutation_tracking}
28
+ # * {#disable_mutation_tracking disable_mutation_tracking}
29
+ # * {#local_secondary_indexes local_secondary_indexes}
30
+ # * {#global_secondary_indexes global_secondary_indexes}
31
+ # * {ClientConfiguration#configure_client configure_client}
32
+ # See example below to see the feature in action.
21
33
  # @example A class definition using +Aws::Record+
22
34
  # class MyModel
23
35
  # include Aws::Record
@@ -28,6 +40,21 @@ module Aws
28
40
  # string_set_attr :tags
29
41
  # map_attr :metadata
30
42
  # end
43
+ # @example Inheritance between models
44
+ # class Animal
45
+ # include Aws::Record
46
+ # string_attr :name, hash_key: true
47
+ # integer_attr :age
48
+ # end
49
+ #
50
+ # class Dog < Animal
51
+ # include Aws::Record
52
+ # boolean_attr :family_friendly
53
+ # end
54
+ #
55
+ # dog = Dog.find(name: 'Sunflower')
56
+ # dog.age = 3
57
+ # dog.family_friendly = true
31
58
  module Record
32
59
  # @!parse extend RecordClassMethods
33
60
  # @!parse include Attributes
@@ -57,6 +84,14 @@ module Aws
57
84
  sub_class.send(:include, DirtyTracking)
58
85
  sub_class.send(:include, Query)
59
86
  sub_class.send(:include, SecondaryIndexes)
87
+ if Aws::Record.extends_record?(sub_class)
88
+ inherit_track_mutations(sub_class)
89
+ end
90
+ end
91
+
92
+ # @api private
93
+ def self.extends_record?(klass)
94
+ klass.superclass.include?(Aws::Record)
60
95
  end
61
96
 
62
97
  private
@@ -64,6 +99,11 @@ module Aws
64
99
  self.class.dynamodb_client
65
100
  end
66
101
 
102
+ def self.inherit_track_mutations(klass)
103
+ superclass_track_mutations = klass.superclass.instance_variable_get("@track_mutations")
104
+ klass.instance_variable_set("@track_mutations", superclass_track_mutations)
105
+ end
106
+
67
107
  module RecordClassMethods
68
108
 
69
109
  # Returns the Amazon DynamoDB table name for this model class.
@@ -72,12 +112,14 @@ module Aws
72
112
  # also define a custom table name at the class level to be anything that
73
113
  # you want.
74
114
  #
115
+ # *Note*: +table_name+ is inherited from a parent model when {set_table_name}
116
+ # is explicitly specified in the parent.
75
117
  # @example
76
118
  # class MyTable
77
119
  # include Aws::Record
78
120
  # end
79
121
  #
80
- # class MyTableTest
122
+ # class MyOtherTable
81
123
  # include Aws::Record
82
124
  # set_table_name "test_MyTable"
83
125
  # end
@@ -85,29 +127,65 @@ module Aws
85
127
  # MyTable.table_name # => "MyTable"
86
128
  # MyOtherTable.table_name # => "test_MyTable"
87
129
  def table_name
88
- if @table_name
89
- @table_name
90
- else
91
- @table_name = self.name.split("::").join("_")
130
+ @table_name ||= begin
131
+ if Aws::Record.extends_record?(self) &&
132
+ default_table_name(self.superclass) != self.superclass.table_name
133
+ self.superclass.instance_variable_get('@table_name')
134
+ else
135
+ default_table_name(self)
136
+ end
92
137
  end
93
138
  end
94
139
 
95
140
  # Allows you to set a custom Amazon DynamoDB table name for this model
96
141
  # class.
142
+ # === Inheritance Support
143
+ # +table_name+ is inherited from a parent model when it is explicitly specified
144
+ # in the parent.
97
145
  #
98
- # @example
146
+ # The parent model will need to have +set_table_name+ defined in their model
147
+ # for the child model to inherit the +table_name+.
148
+ # If no +set_table_name+ is defined, the parent and child models will have separate
149
+ # table names based on their class name.
150
+ #
151
+ # If both parent and child models have defined +set_table_name+ in their model,
152
+ # the child model will override the +table_name+ with theirs.
153
+ # @example Setting custom table name for model class
99
154
  # class MyTable
100
155
  # include Aws::Record
101
156
  # set_table_name "prod_MyTable"
102
157
  # end
103
158
  #
104
- # class MyTableTest
159
+ # class MyOtherTable
105
160
  # include Aws::Record
106
161
  # set_table_name "test_MyTable"
107
162
  # end
108
163
  #
109
164
  # MyTable.table_name # => "prod_MyTable"
110
165
  # MyOtherTable.table_name # => "test_MyTable"
166
+ # @example Child model inherits table name from Parent model
167
+ # class Animal
168
+ # include Aws::Record
169
+ # set_table_name "AnimalTable"
170
+ # end
171
+ #
172
+ # class Dog < Animal
173
+ # include Aws::Record
174
+ # end
175
+ #
176
+ # Dog.table_name # => "AnimalTable"
177
+ # @example Child model overrides table name from Parent model
178
+ # class Animal
179
+ # include Aws::Record
180
+ # set_table_name "AnimalTable"
181
+ # end
182
+ #
183
+ # class Dog < Animal
184
+ # include Aws::Record
185
+ # set_table_name "DogTable"
186
+ # end
187
+ #
188
+ # Dog.table_name # => "DogTable"
111
189
  def set_table_name(name)
112
190
  @table_name = name
113
191
  end
@@ -149,6 +227,9 @@ module Aws
149
227
  end
150
228
 
151
229
  # Turns off mutation tracking for all attributes in the model.
230
+ #
231
+ # *Note*: +disable_mutation_tracking+ is inherited from a parent model
232
+ # when it is explicitly specified in the parent.
152
233
  def disable_mutation_tracking
153
234
  @track_mutations = false
154
235
  end
@@ -158,6 +239,9 @@ module Aws
158
239
  # call this. It is provided in case there is a need to dynamically turn
159
240
  # this feature on and off, though that would be generally discouraged and
160
241
  # could cause inaccurate mutation tracking at runtime.
242
+ #
243
+ # *Note*: +enable_mutation_tracking+ is inherited from a parent model
244
+ # when it is explicitly specified in the parent.
161
245
  def enable_mutation_tracking
162
246
  @track_mutations = true
163
247
  end
@@ -177,6 +261,13 @@ module Aws
177
261
  raise Errors::InvalidModel.new("Table models must include a hash key")
178
262
  end
179
263
  end
264
+
265
+ private
266
+ def default_table_name(klass)
267
+ return unless klass.name
268
+ klass.name.split("::").join("_")
269
+ end
270
+
180
271
  end
181
272
  end
182
273
  end
data/lib/aws-record.rb CHANGED
@@ -1,15 +1,4 @@
1
- # Copyright 2015-2017 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.
1
+ # frozen_string_literal: true
13
2
 
14
3
  require 'aws-sdk-dynamodb'
15
4
  require_relative 'aws-record/record/client_configuration'
@@ -30,6 +19,7 @@ require_relative 'aws-record/record/table_migration'
30
19
  require_relative 'aws-record/record/version'
31
20
  require_relative 'aws-record/record/transactions'
32
21
  require_relative 'aws-record/record/buildable_search'
22
+ require_relative 'aws-record/record/batch_read'
33
23
  require_relative 'aws-record/record/batch_write'
34
24
  require_relative 'aws-record/record/batch'
35
25
  require_relative 'aws-record/record/marshalers/string_marshaler'
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: 2.8.0
4
+ version: 2.10.0
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: 2022-10-12 00:00:00.000000000 Z
11
+ date: 2023-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-dynamodb
@@ -37,6 +37,7 @@ files:
37
37
  - lib/aws-record/record/attribute.rb
38
38
  - lib/aws-record/record/attributes.rb
39
39
  - lib/aws-record/record/batch.rb
40
+ - lib/aws-record/record/batch_read.rb
40
41
  - lib/aws-record/record/batch_write.rb
41
42
  - lib/aws-record/record/buildable_search.rb
42
43
  - lib/aws-record/record/client_configuration.rb
@@ -65,7 +66,7 @@ files:
65
66
  - lib/aws-record/record/table_migration.rb
66
67
  - lib/aws-record/record/transactions.rb
67
68
  - lib/aws-record/record/version.rb
68
- homepage: http://github.com/aws/aws-sdk-ruby-record
69
+ homepage: https://github.com/aws/aws-sdk-ruby-record
69
70
  licenses:
70
71
  - Apache 2.0
71
72
  metadata: {}