aws-record 1.0.0.pre.4 → 1.0.0.pre.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 102f5cb13fee3789030badc882c2d171ddb800e8
4
- data.tar.gz: e4bad779188f59b940492fb2644636901826f41e
3
+ metadata.gz: e3fb756f0dd0d63461d393e36ec6059482648f79
4
+ data.tar.gz: 8f3ea1c9ea27cf57f7a2aa26d0096d62e8c6f4cf
5
5
  SHA512:
6
- metadata.gz: a3ef6f71866145568e25d6cac31ebd31c33ad37bfa776ae648e4a0424f69e2e7c6fa46b8d22e5b0c5d5231f1fbde98d1460e5f83e88a812ea85dd4d87e3d3b68
7
- data.tar.gz: 9cb2cf7a7644a0a6754c7e32452dbf5b83e422e6d7e94ca8a5c0442aa1532ac81ed47519bb00175a1a5e8107f193c1612a0a2ebd3df66a046763daa6c51378a5
6
+ metadata.gz: bda87438c533121bb4cc80f2e7dca54f2a0757f613d4993790c1519733e99c6c3d3df2c54e72c14c24173d8c424dc6368be0ede2b27d86b417847be3152c8fd8
7
+ data.tar.gz: 2b9a63d40bb40daa48dee6ec115950fe8c7ea85a0c43b80ed7dc64d12936eb7e5b5504343ffbea7b8a26cb446bc37e5241b497452a9a3022902e6817be8cb2e4
@@ -13,6 +13,17 @@
13
13
 
14
14
  module Aws
15
15
  module Record
16
+ # @!parse extend RecordClassMethods
17
+ # @!parse include Attributes
18
+ # @!parse extend Attributes::ClassMethods
19
+ # @!parse include ItemOperations
20
+ # @!parse extend ItemOperations::ItemOperationsClassMethods
21
+ # @!parse include Query
22
+ # @!parse extend Query::QueryClassMethods
23
+ # @!parse include SecondaryIndexes
24
+ # @!parse extend SecondaryIndexes::SecondaryIndexesClassMethods
25
+ # @!parse include DirtyTracking
26
+ # @!parse extend DirtyTracking::DirtyTrackingClassMethods
16
27
 
17
28
  # Usage of {Aws::Record} requires only that you include this module. This
18
29
  # method will then pull in the other default modules.
@@ -60,7 +71,7 @@ module Aws
60
71
  if @table_name
61
72
  @table_name
62
73
  else
63
- @table_name = self.name
74
+ @table_name = self.name.split("::").join("_")
64
75
  end
65
76
  end
66
77
 
@@ -21,8 +21,8 @@ module Aws
21
21
  # safe to use as a method.
22
22
  # @param [Hash] options
23
23
  # @option options [Marshaler] :marshaler The marshaler for this attribute.
24
- # So long as you provide a marshaler which implements `#type_cast` and
25
- # `#serialize` that consume raw values as expected, you can bring your
24
+ # So long as you provide a marshaler which implements +#type_cast+ and
25
+ # +#serialize+ that consume raw values as expected, you can bring your
26
26
  # own marshaler type.
27
27
  # @option options [Array] :validators An array of validator classes that
28
28
  # will be run when an attribute is checked for validity.
@@ -54,8 +54,8 @@ module Aws
54
54
  # @param [Symbol] name Name of this attribute. It should be a name that
55
55
  # is safe to use as a method.
56
56
  # @param [Marshaler] marshaler The marshaler for this attribute. So long
57
- # as you provide a marshaler which implements `#type_cast` and
58
- # `#serialize` that consume raw values as expected, you can bring your
57
+ # as you provide a marshaler which implements +#type_cast+ and
58
+ # +#serialize+ that consume raw values as expected, you can bring your
59
59
  # own marshaler type. Convenience methods will provide this for you.
60
60
  # @param [Hash] opts
61
61
  # @option opts [Array] :validators An array of validator classes that
@@ -269,7 +269,7 @@ module Aws
269
269
 
270
270
  # Define a numeric set attribute for your model.
271
271
  #
272
- # Numeric sets are homogeneous sets, containing only strings. Note that
272
+ # Numeric sets are homogeneous sets, containing only numbers. Note that
273
273
  # empty sets cannot be persisted to DynamoDB. Empty sets are valid for
274
274
  # aws-record items, but they will not be persisted as sets. nil values
275
275
  # from your table, or a lack of value from your table, will be treated
@@ -284,7 +284,7 @@ module Aws
284
284
  # the hash key for the table.
285
285
  # @option opts [Boolean] :range_key Set to true if this attribute is
286
286
  # the range key for the table.
287
- def string_set_attr(name, opts = {})
287
+ def numeric_set_attr(name, opts = {})
288
288
  opts[:dynamodb_type] = "NS"
289
289
  attr(name, Attributes::NumericSetMarshaler, opts)
290
290
  end
@@ -15,10 +15,14 @@ module Aws
15
15
  module Record
16
16
  module Errors
17
17
 
18
- class KeyMissing < RuntimeError; end
18
+ class RecordError < RuntimeError; end
19
+
20
+ class KeyMissing < RecordError; end
21
+ class NotFound < RecordError; end
22
+ class ItemAlreadyExists < RecordError; end
23
+
19
24
  class NameCollision < RuntimeError; end
20
25
  class ReservedName < RuntimeError; end
21
- class NotFound < RuntimeError; end
22
26
  class InvalidModel < RuntimeError; end
23
27
  class TableDoesNotExist < RuntimeError; end
24
28
 
@@ -12,17 +12,18 @@ module Aws
12
12
 
13
13
  def each(&block)
14
14
  return enum_for(:each) unless block_given?
15
- unless @result
16
- @result = @client.send(@search_method, @search_params)
17
- end
18
- @result.each_page do |page|
19
- items = _build_items_from_response(page.items, @model)
20
- items.each do |item|
15
+ items.each_page do |page|
16
+ items_array = _build_items_from_response(page.items, @model)
17
+ items_array.each do |item|
21
18
  yield item
22
19
  end
23
20
  end
24
21
  end
25
22
 
23
+ def empty?
24
+ items.empty?
25
+ end
26
+
26
27
  private
27
28
  def _build_items_from_response(items, model)
28
29
  ret = []
@@ -37,6 +38,10 @@ module Aws
37
38
  ret
38
39
  end
39
40
 
41
+ def items
42
+ @_items ||= @client.send(@search_method, @search_params)
43
+ end
44
+
40
45
  end
41
46
  end
42
47
  end
@@ -18,20 +18,96 @@ module Aws
18
18
  # @api private
19
19
  def self.included(sub_class)
20
20
  sub_class.extend(ItemOperationsClassMethods)
21
+ sub_class.instance_variable_set("@errors", [])
21
22
  end
22
23
 
23
- # Saves this instance of an item to Amazon DynamoDB using the
24
- # {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
25
- # API. Uses this item instance's attributes in order to build the request
26
- # on your behalf.
24
+ # Saves this instance of an item to Amazon DynamoDB. If this item is "new"
25
+ # as defined by having new or altered key attributes, will attempt a
26
+ # conditional
27
+ # {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
28
+ # call, which will not overwrite an existing item. If the item only has
29
+ # altered non-key attributes, will perform an
30
+ # {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
31
+ # call. Uses this item instance's attributes in order to build the
32
+ # request on your behalf.
27
33
  #
34
+ # You can use the +:force+ option to perform a simple put/overwrite
35
+ # without conditional validation or update logic.
36
+ #
37
+ # @param [Hash] opts
38
+ # @option opts [Boolean] :force if true, will save as a put operation and
39
+ # overwrite any existing item on the remote end. Otherwise, and by
40
+ # default, will either perform a conditional put or an update call.
28
41
  # @raise [Aws::Record::Errors::KeyMissing] if a required key attribute
29
42
  # does not have a value within this item instance.
30
- def save
31
- dynamodb_client.put_item(
32
- table_name: self.class.table_name,
33
- item: build_item_for_save
34
- )
43
+ # @raise [Aws::Record::Errors::ItemAlreadyExists] if a conditional put
44
+ # fails because the item exists on the remote end.
45
+ def save!(opts = {})
46
+ force = opts[:force]
47
+ expect_new = expect_new_item?
48
+ if force
49
+ dynamodb_client.put_item(
50
+ table_name: self.class.table_name,
51
+ item: build_item_for_save
52
+ )
53
+ elsif expect_new
54
+ put_opts = {
55
+ table_name: self.class.table_name,
56
+ item: build_item_for_save
57
+ }.merge(prevent_overwrite_expression)
58
+ begin
59
+ dynamodb_client.put_item(put_opts)
60
+ rescue Aws::DynamoDB::Errors::ConditionalCheckFailedException => e
61
+ raise Errors::ItemAlreadyExists.new(
62
+ "Conditional #put_item call failed, an item with the same key"\
63
+ " already exists in the table. Either load and update this"\
64
+ " item, or include the :force option to clobber the remote"\
65
+ " item."
66
+ )
67
+ end
68
+ else
69
+ dynamodb_client.update_item(
70
+ table_name: self.class.table_name,
71
+ key: key_values,
72
+ attribute_updates: dirty_changes_for_update
73
+ )
74
+ end
75
+ end
76
+
77
+ # Saves this instance of an item to Amazon DynamoDB. If this item is "new"
78
+ # as defined by having new or altered key attributes, will attempt a
79
+ # conditional
80
+ # {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#put_item-instance_method Aws::DynamoDB::Client#put_item}
81
+ # call, which will not overwrite an existing item. If the item only has
82
+ # altered non-key attributes, will perform an
83
+ # {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#update_item-instance_method Aws::DynamoDB::Client#update_item}
84
+ # call. Uses this item instance's attributes in order to build the
85
+ # request on your behalf.
86
+ #
87
+ # You can use the +:force+ option to perform a simple put/overwrite
88
+ # without conditional validation or update logic.
89
+ #
90
+ # In the case where persistence fails, will populate the +errors+ array
91
+ # with any generated error messages, and will cause +#valid?+ to return
92
+ # false until there is a successful save.
93
+ #
94
+ # @param [Hash] opts
95
+ # @option opts [Boolean] :force if true, will save as a put operation and
96
+ # overwrite any existing item on the remote end. Otherwise, and by
97
+ # default, will either perform a conditional put or an update call.
98
+ def save(opts = {})
99
+ result = save!(opts)
100
+ errors.clear
101
+ result
102
+ rescue Errors::RecordError => e
103
+ errors << e.message
104
+ false
105
+ end
106
+
107
+ # Checks if the record is a valid record. +false+ if most recent +#save+
108
+ # call raised errors, or if there are missing keys. +true+ otherwise.
109
+ def valid?
110
+ errors.empty? && missing_key_values.empty?
35
111
  end
36
112
 
37
113
  # Deletes the item instance that matches the key values of this item
@@ -46,6 +122,10 @@ module Aws
46
122
  true
47
123
  end
48
124
 
125
+ def errors
126
+ self.class.instance_variable_get("@errors")
127
+ end
128
+
49
129
  private
50
130
  def build_item_for_save
51
131
  validate_key_values
@@ -84,6 +164,44 @@ module Aws
84
164
  end
85
165
  end
86
166
 
167
+ def expect_new_item?
168
+ # Algorithm: Are keys dirty? If so, we treat as new.
169
+ self.class.keys.any? do |_, attr_name|
170
+ attribute_dirty?(attr_name)
171
+ end
172
+ end
173
+
174
+ def prevent_overwrite_expression
175
+ conditions = []
176
+ expression_attribute_names = {}
177
+ # Hash Key
178
+ conditions << "attribute_not_exists(#H)"
179
+ expression_attribute_names["#H"] = self.class.hash_key.database_name
180
+ # Range Key
181
+ if self.class.range_key
182
+ conditions << "attribute_not_exists(#R)"
183
+ expression_attribute_names["#R"] = self.class.range_key.database_name
184
+ end
185
+ {
186
+ condition_expression: conditions.join(" and "),
187
+ expression_attribute_names: expression_attribute_names
188
+ }
189
+ end
190
+
191
+ def dirty_changes_for_update
192
+ attributes = self.class.attributes
193
+ ret = dirty.inject({}) do |acc, attr_name|
194
+ key = attributes[attr_name].database_name
195
+ value = {
196
+ value: attributes[attr_name].serialize(@data[attr_name]),
197
+ action: "PUT"
198
+ }
199
+ acc[key] = value
200
+ acc
201
+ end
202
+ ret
203
+ end
204
+
87
205
  module ItemOperationsClassMethods
88
206
 
89
207
  # @example Usage Example
@@ -92,7 +210,7 @@ module Aws
92
210
  # integer_attr :id, hash_key: true
93
211
  # string_attr :name, range_key: true
94
212
  # end
95
- #
213
+ #
96
214
  # MyModel.find(id: 1, name: "First")
97
215
  #
98
216
  # @param [Hash] opts attribute-value pairs for the key you wish to
@@ -13,6 +13,6 @@
13
13
 
14
14
  module Aws
15
15
  module Record
16
- VERSION = "1.0.0.pre.4"
16
+ VERSION = '1.0.0.pre.5'
17
17
  end
18
18
  end
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
4
+ version: 1.0.0.pre.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-11 00:00:00.000000000 Z
11
+ date: 2016-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-resources
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  version: 1.3.1
74
74
  requirements: []
75
75
  rubyforge_project:
76
- rubygems_version: 2.4.8
76
+ rubygems_version: 2.5.1
77
77
  signing_key:
78
78
  specification_version: 4
79
79
  summary: AWS Record library for Amazon DynamoDB