dynamoid 3.13.1 → 3.14.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -5
  3. data/README.md +60 -1584
  4. data/lib/dynamoid/associations/association.rb +10 -2
  5. data/lib/dynamoid/associations/belongs_to.rb +3 -1
  6. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +0 -1
  7. data/lib/dynamoid/associations/has_many.rb +0 -1
  8. data/lib/dynamoid/associations/has_one.rb +1 -2
  9. data/lib/dynamoid/associations/many_association.rb +1 -0
  10. data/lib/dynamoid/associations.rb +64 -48
  11. data/lib/dynamoid/components.rb +1 -0
  12. data/lib/dynamoid/criteria/chain.rb +43 -30
  13. data/lib/dynamoid/dirty.rb +3 -2
  14. data/lib/dynamoid/document.rb +29 -21
  15. data/lib/dynamoid/fields.rb +55 -41
  16. data/lib/dynamoid/finders.rb +1 -1
  17. data/lib/dynamoid/indexes.rb +29 -21
  18. data/lib/dynamoid/persistence/inc.rb +15 -7
  19. data/lib/dynamoid/persistence/save.rb +21 -14
  20. data/lib/dynamoid/persistence.rb +88 -54
  21. data/lib/dynamoid/transactions/mutation/base.rb +76 -0
  22. data/lib/dynamoid/transactions/mutation/builders/delete_request_builder.rb +44 -0
  23. data/lib/dynamoid/transactions/mutation/builders/update_request_builder.rb +139 -0
  24. data/lib/dynamoid/transactions/mutation/create.rb +52 -0
  25. data/lib/dynamoid/transactions/mutation/delete_with_instance.rb +80 -0
  26. data/lib/dynamoid/transactions/mutation/delete_with_primary_key.rb +62 -0
  27. data/lib/dynamoid/transactions/mutation/destroy.rb +96 -0
  28. data/lib/dynamoid/transactions/mutation/import.rb +84 -0
  29. data/lib/dynamoid/transactions/mutation/inc.rb +101 -0
  30. data/lib/dynamoid/transactions/mutation/increment.rb +63 -0
  31. data/lib/dynamoid/transactions/mutation/save.rb +194 -0
  32. data/lib/dynamoid/transactions/mutation/touch.rb +99 -0
  33. data/lib/dynamoid/transactions/mutation/update_attributes.rb +49 -0
  34. data/lib/dynamoid/transactions/mutation/update_fields.rb +185 -0
  35. data/lib/dynamoid/transactions/mutation/upsert.rb +94 -0
  36. data/lib/dynamoid/transactions/mutation.rb +935 -0
  37. data/lib/dynamoid/transactions/retrieval/find.rb +140 -0
  38. data/lib/dynamoid/transactions/retrieval.rb +150 -0
  39. data/lib/dynamoid/transactions.rb +63 -0
  40. data/lib/dynamoid/version.rb +1 -1
  41. data/lib/dynamoid.rb +1 -2
  42. metadata +23 -17
  43. data/lib/dynamoid/transaction_read/find.rb +0 -137
  44. data/lib/dynamoid/transaction_read.rb +0 -146
  45. data/lib/dynamoid/transaction_write/base.rb +0 -59
  46. data/lib/dynamoid/transaction_write/create.rb +0 -49
  47. data/lib/dynamoid/transaction_write/delete_with_instance.rb +0 -65
  48. data/lib/dynamoid/transaction_write/delete_with_primary_key.rb +0 -64
  49. data/lib/dynamoid/transaction_write/destroy.rb +0 -84
  50. data/lib/dynamoid/transaction_write/item_updater.rb +0 -60
  51. data/lib/dynamoid/transaction_write/save.rb +0 -177
  52. data/lib/dynamoid/transaction_write/update_attributes.rb +0 -46
  53. data/lib/dynamoid/transaction_write/update_fields.rb +0 -247
  54. data/lib/dynamoid/transaction_write/upsert.rb +0 -113
  55. data/lib/dynamoid/transaction_write.rb +0 -673
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dynamoid
4
+ module Transactions
5
+ class Retrieval
6
+ # @private
7
+ class Find
8
+ attr_reader :model_class
9
+
10
+ def initialize(model_class, *ids, **options)
11
+ @model_class = model_class
12
+ @ids = ids
13
+ @options = options
14
+ end
15
+
16
+ def on_registration
17
+ validate_primary_key!
18
+ end
19
+
20
+ def observable_by_user_result
21
+ nil
22
+ end
23
+
24
+ def action_request
25
+ if single_key_given?
26
+ action_request_for_single_key
27
+ else
28
+ action_request_for_multiple_keys
29
+ end
30
+ end
31
+
32
+ def process_responses(responses)
33
+ models = responses.map do |response|
34
+ if response.item
35
+ @model_class.from_database(response.item)
36
+ elsif @options[:raise_error] == false
37
+ nil
38
+ else
39
+ message = build_record_not_found_exception_message(responses)
40
+ raise Dynamoid::Errors::RecordNotFound, message
41
+ end
42
+ end
43
+
44
+ unless single_key_given?
45
+ models.compact!
46
+ end
47
+
48
+ models.each { |m| m&.run_callbacks :find }
49
+ models
50
+ end
51
+
52
+ private
53
+
54
+ def single_key_given?
55
+ @ids.size == 1 && !@ids[0].is_a?(Array)
56
+ end
57
+
58
+ def validate_primary_key!
59
+ if single_key_given?
60
+ partition_key = @ids[0]
61
+ sort_key = @options[:range_key]
62
+
63
+ raise Dynamoid::Errors::MissingHashKey if partition_key.nil?
64
+ raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key && sort_key.nil?
65
+ else
66
+ ids = @ids.flatten(1)
67
+
68
+ raise Dynamoid::Errors::MissingHashKey if ids.any? { |pk, _sk| pk.nil? }
69
+ raise Errors::MissingRangeKey if @model_class.range_key && ids.any? { |_pk, sk| sk.nil? }
70
+ end
71
+ end
72
+
73
+ def action_request_for_single_key
74
+ partition_key = @ids[0]
75
+
76
+ key = { @model_class.hash_key => cast_and_dump_attribute(@model_class.hash_key, partition_key) }
77
+
78
+ if @model_class.range_key
79
+ sort_key = @options[:range_key]
80
+ key[@model_class.range_key] = cast_and_dump_attribute(@model_class.range_key, sort_key)
81
+ end
82
+
83
+ {
84
+ get: {
85
+ key: key,
86
+ table_name: @model_class.table_name
87
+ }
88
+ }
89
+ end
90
+
91
+ def action_request_for_multiple_keys
92
+ @ids.flatten(1).map do |id|
93
+ if @model_class.range_key
94
+ # expect [hash-key, range-key] pair
95
+ pk, sk = id
96
+ pk_dumped = cast_and_dump_attribute(@model_class.hash_key, pk)
97
+ sk_dumped = cast_and_dump_attribute(@model_class.range_key, sk)
98
+
99
+ key = { @model_class.hash_key => pk_dumped, @model_class.range_key => sk_dumped }
100
+ else
101
+ pk_dumped = cast_and_dump_attribute(@model_class.hash_key, id)
102
+
103
+ key = { @model_class.hash_key => pk_dumped }
104
+ end
105
+
106
+ {
107
+ get: {
108
+ key: key,
109
+ table_name: @model_class.table_name
110
+ }
111
+ }
112
+ end
113
+ end
114
+
115
+ def cast_and_dump_attribute(name, value)
116
+ attribute_options = @model_class.attributes[name]
117
+ casted_value = TypeCasting.cast_field(value, attribute_options)
118
+ Dumping.dump_field(casted_value, attribute_options)
119
+ end
120
+
121
+ def build_record_not_found_exception_message(responses)
122
+ items = responses.map(&:item)
123
+ ids = @ids.flatten(1)
124
+
125
+ if single_key_given?
126
+ id = ids[0]
127
+ primary_key = @model_class.range_key ? "(#{id.inspect},#{@options[:range_key].inspect})" : id.inspect
128
+ message = "Couldn't find #{@model_class.name} with primary key #{primary_key}"
129
+ else
130
+ ids_list = @model_class.range_key ? ids.map { |pk, sk| "(#{pk.inspect},#{sk.inspect})" } : ids.map(&:inspect)
131
+ message = "Couldn't find all #{@model_class.name.pluralize} with primary keys [#{ids_list.join(', ')}] "
132
+ message += "(found #{items.compact.size} results, but was looking for #{items.size})"
133
+ end
134
+
135
+ message
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'retrieval/find'
4
+
5
+ module Dynamoid
6
+ module Transactions
7
+ # The class +Retrieval+ provides means to perform multiple reading
8
+ # operations in transaction, that is atomically, so that either all of them
9
+ # succeed, or all of them fail.
10
+ #
11
+ # The reading methods are supposed to be as close as possible to their
12
+ # non-transactional counterparts:
13
+ #
14
+ # ```
15
+ # user_id = params[:user_id]
16
+ # payment = params[:payment_id]
17
+ #
18
+ # models = Dynamoid::Transactions::Retrieval.execute do |t|
19
+ # t.find User, user_id
20
+ # t.find Payment, payment_id
21
+ # end
22
+ # ```
23
+ #
24
+ # The only difference is that the methods are called on a transaction
25
+ # instance and a model or a model class should be specified. So +User.find+
26
+ # becomes +t.find(user_id)+ and +Payment.find(payment_id)+ becomes +t.find
27
+ # Payment, payment_id+.
28
+ #
29
+ # A transaction can be used without a block. This way a transaction instance
30
+ # should be instantiated and committed manually with +#commit+ method:
31
+ #
32
+ # t = Dynamoid::Transactions::Retrieval.new
33
+ #
34
+ # t.find user_id
35
+ # t.find payment_id
36
+ #
37
+ # models = t.commit
38
+ #
39
+ #
40
+ # ### DynamoDB's transactions
41
+ #
42
+ # The main difference between DynamoDB transactions and a common interface is
43
+ # that DynamoDB's transactions are executed in batch. So in Dynamoid no
44
+ # data actually loaded when some transactional method (e.g+ `#find+) is
45
+ # called. All the changes are loaded at the end.
46
+ #
47
+ # A +TransactGetItems+ DynamoDB operation is used (see
48
+ # [documentation](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html)
49
+ # for details).
50
+ class Retrieval
51
+ def self.execute
52
+ transaction = new
53
+
54
+ begin
55
+ yield transaction
56
+ transaction.commit
57
+ end
58
+ end
59
+
60
+ def initialize
61
+ @actions = []
62
+ end
63
+
64
+ # Load all the models.
65
+ #
66
+ # transaction = Dynamoid::Transactions::Retrieval.new
67
+ # # ...
68
+ # transaction.commit
69
+ def commit
70
+ return [] if @actions.empty?
71
+
72
+ # some actions may produce multiple requests
73
+ action_request_groups = @actions.map(&:action_request).map do |action_request|
74
+ action_request.is_a?(Array) ? action_request : [action_request]
75
+ end
76
+ action_requests = action_request_groups.flatten(1)
77
+
78
+ return [] if action_requests.empty?
79
+
80
+ response = Dynamoid.adapter.transact_read_items(action_requests)
81
+
82
+ responses = response.responses.dup
83
+ @actions.zip(action_request_groups).map do |action, action_requests|
84
+ action_responses = responses.shift(action_requests.size)
85
+ action.process_responses(action_responses)
86
+ end.flatten
87
+ end
88
+
89
+ # Find one or many objects, specified by one id or an array of ids.
90
+ #
91
+ # By default it raises +RecordNotFound+ exception if at least one model
92
+ # isn't found. This behavior can be changed with +raise_error+ option. If
93
+ # specified +raise_error: false+ option then +find+ will not raise the
94
+ # exception.
95
+ #
96
+ # When a document schema includes range key it should always be specified
97
+ # in +find+ method call. In case it's missing +MissingRangeKey+ exception
98
+ # will be raised.
99
+ #
100
+ # Please note that there are the following differences between
101
+ # transactional and non-transactional +find+:
102
+ # - transactional +find+ preserves order of models in result when given multiple ids
103
+ # - transactional +find+ doesn't return results immediately, a single
104
+ # collection with results of all the +find+ calls is returned instead
105
+ # - +:consistent_read+ option isn't supported
106
+ # - transactional +find+ is subject to limitations of the
107
+ # [TransactGetItems](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html)
108
+ # DynamoDB operation, e.g. the whole read transaction can load only up to 100 models.
109
+ #
110
+ # @param [Object|Array] ids a single primary key or an array of primary keys
111
+ # @param [Hash] options optional parameters of the operation
112
+ # @option options [Object] :range_key sort key of a model; required when a single partition key is given and a sort key is declared for a model
113
+ # @option options [true|false] :raise_error whether to raise a +RecordNotFound+ exception; specify explicitly +raise_error: false+ to suppress the exception; default is +true+
114
+ # @return [nil]
115
+ #
116
+ # @example Find by partition key
117
+ # Dynamoid::Transactions::Retrieval.execute do |t|
118
+ # t.find Document, 101
119
+ # end
120
+ #
121
+ # @example Find by partition key and sort key
122
+ # Dynamoid::Transactions::Retrieval.execute do |t|
123
+ # t.find Document, 101, range_key: 'archived'
124
+ # end
125
+ #
126
+ # @example Find several documents by partition key
127
+ # Dynamoid::Transactions::Retrieval.execute do |t|
128
+ # t.find Document, 101, 102, 103
129
+ # t.find Document, [101, 102, 103]
130
+ # end
131
+ #
132
+ # @example Find several documents by partition key and sort key
133
+ # Dynamoid::Transactions::Retrieval.execute do |t|
134
+ # t.find Document, [[101, 'archived'], [102, 'new'], [103, 'deleted']]
135
+ # end
136
+ def find(model_class, *ids, **options)
137
+ action = Find.new(model_class, *ids, **options)
138
+ register_action action
139
+ end
140
+
141
+ private
142
+
143
+ def register_action(action)
144
+ @actions << action
145
+ action.on_registration
146
+ action.observable_by_user_result
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dynamoid/transactions/retrieval'
4
+ require 'dynamoid/transactions/mutation'
5
+
6
+ module Dynamoid
7
+ # The Transactions module provides a way to run blocks of code within a
8
+ # DynamoDB transaction.
9
+ #
10
+ # DynamoDB supports two types of transactions: read-only and write-only.
11
+ # This module provides a unified interface to both.
12
+ #
13
+ # @example Rails-style transaction (defaults to write)
14
+ # User.transaction do |t|
15
+ # t.save(user)
16
+ # t.create(Account, name: 'A')
17
+ # end
18
+ #
19
+ # @example Explicit write transaction
20
+ # User.transaction.writing do |t|
21
+ # t.save(user)
22
+ # t.create(Account, name: 'A')
23
+ # end
24
+ #
25
+ # @example Explicit read transaction
26
+ # users = User.transaction.reading do |t|
27
+ # t.find(User, '1')
28
+ # t.find(User, '2')
29
+ # end
30
+ module Transactions
31
+ extend ActiveSupport::Concern
32
+
33
+ # Proxy object to provide .writing and .reading methods on .transaction
34
+ class Proxy
35
+ def writing(&block)
36
+ Mutation.execute(&block)
37
+ end
38
+
39
+ def reading(&block)
40
+ Retrieval.execute(&block)
41
+ end
42
+ end
43
+
44
+ module ClassMethods
45
+ # Start a transaction.
46
+ #
47
+ # If a block is given, it starts a write transaction.
48
+ #
49
+ # If no block is given, it returns a proxy object that supports
50
+ # .writing and .reading methods.
51
+ #
52
+ # @param block [Proc] optional block to run in a write transaction
53
+ # @return [Proxy|nil|Array<Dynamoid::Document>]
54
+ def transaction(&block)
55
+ if block_given?
56
+ Mutation.execute(&block)
57
+ else
58
+ Proxy.new
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
- VERSION = '3.13.1'
4
+ VERSION = '3.14.0'
5
5
  end
data/lib/dynamoid.rb CHANGED
@@ -32,11 +32,10 @@ require 'dynamoid/finders'
32
32
  require 'dynamoid/identity_map'
33
33
  require 'dynamoid/config'
34
34
  require 'dynamoid/loadable'
35
+ require 'dynamoid/transactions'
35
36
  require 'dynamoid/components'
36
37
  require 'dynamoid/document'
37
38
  require 'dynamoid/adapter'
38
- require 'dynamoid/transaction_read'
39
- require 'dynamoid/transaction_write'
40
39
 
41
40
  require 'dynamoid/tasks/database'
42
41
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.1
4
+ version: 3.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Symonds
@@ -241,19 +241,25 @@ files:
241
241
  - lib/dynamoid/tasks.rb
242
242
  - lib/dynamoid/tasks/database.rake
243
243
  - lib/dynamoid/tasks/database.rb
244
- - lib/dynamoid/transaction_read.rb
245
- - lib/dynamoid/transaction_read/find.rb
246
- - lib/dynamoid/transaction_write.rb
247
- - lib/dynamoid/transaction_write/base.rb
248
- - lib/dynamoid/transaction_write/create.rb
249
- - lib/dynamoid/transaction_write/delete_with_instance.rb
250
- - lib/dynamoid/transaction_write/delete_with_primary_key.rb
251
- - lib/dynamoid/transaction_write/destroy.rb
252
- - lib/dynamoid/transaction_write/item_updater.rb
253
- - lib/dynamoid/transaction_write/save.rb
254
- - lib/dynamoid/transaction_write/update_attributes.rb
255
- - lib/dynamoid/transaction_write/update_fields.rb
256
- - lib/dynamoid/transaction_write/upsert.rb
244
+ - lib/dynamoid/transactions.rb
245
+ - lib/dynamoid/transactions/mutation.rb
246
+ - lib/dynamoid/transactions/mutation/base.rb
247
+ - lib/dynamoid/transactions/mutation/builders/delete_request_builder.rb
248
+ - lib/dynamoid/transactions/mutation/builders/update_request_builder.rb
249
+ - lib/dynamoid/transactions/mutation/create.rb
250
+ - lib/dynamoid/transactions/mutation/delete_with_instance.rb
251
+ - lib/dynamoid/transactions/mutation/delete_with_primary_key.rb
252
+ - lib/dynamoid/transactions/mutation/destroy.rb
253
+ - lib/dynamoid/transactions/mutation/import.rb
254
+ - lib/dynamoid/transactions/mutation/inc.rb
255
+ - lib/dynamoid/transactions/mutation/increment.rb
256
+ - lib/dynamoid/transactions/mutation/save.rb
257
+ - lib/dynamoid/transactions/mutation/touch.rb
258
+ - lib/dynamoid/transactions/mutation/update_attributes.rb
259
+ - lib/dynamoid/transactions/mutation/update_fields.rb
260
+ - lib/dynamoid/transactions/mutation/upsert.rb
261
+ - lib/dynamoid/transactions/retrieval.rb
262
+ - lib/dynamoid/transactions/retrieval/find.rb
257
263
  - lib/dynamoid/type_casting.rb
258
264
  - lib/dynamoid/undumping.rb
259
265
  - lib/dynamoid/validations.rb
@@ -263,10 +269,10 @@ licenses:
263
269
  - MIT
264
270
  metadata:
265
271
  homepage_uri: http://github.com/Dynamoid/dynamoid
266
- source_code_uri: https://github.com/Dynamoid/dynamoid/tree/v3.13.1
267
- changelog_uri: https://github.com/Dynamoid/dynamoid/blob/v3.13.1/CHANGELOG.md
272
+ source_code_uri: https://github.com/Dynamoid/dynamoid/tree/v3.14.0
273
+ changelog_uri: https://github.com/Dynamoid/dynamoid/blob/v3.14.0/CHANGELOG.md
268
274
  bug_tracker_uri: https://github.com/Dynamoid/dynamoid/issues
269
- documentation_uri: https://www.rubydoc.info/gems/dynamoid/3.13.1
275
+ documentation_uri: https://www.rubydoc.info/gems/dynamoid/3.14.0
270
276
  funding_uri: https://opencollective.com/dynamoid
271
277
  wiki_uri: https://github.com/Dynamoid/dynamoid/wiki
272
278
  rubygems_mfa_required: 'true'
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Dynamoid
4
- class TransactionRead
5
- class Find
6
- attr_reader :model_class
7
-
8
- def initialize(model_class, *ids, **options)
9
- @model_class = model_class
10
- @ids = ids
11
- @options = options
12
- end
13
-
14
- def on_registration
15
- validate_primary_key!
16
- end
17
-
18
- def observable_by_user_result
19
- nil
20
- end
21
-
22
- def action_request
23
- if single_key_given?
24
- action_request_for_single_key
25
- else
26
- action_request_for_multiple_keys
27
- end
28
- end
29
-
30
- def process_responses(responses)
31
- models = responses.map do |response|
32
- if response.item
33
- @model_class.from_database(response.item)
34
- elsif @options[:raise_error] == false
35
- nil
36
- else
37
- message = build_record_not_found_exception_message(responses)
38
- raise Dynamoid::Errors::RecordNotFound, message
39
- end
40
- end
41
-
42
- unless single_key_given?
43
- models.compact!
44
- end
45
-
46
- models.each { |m| m&.run_callbacks :find }
47
- models
48
- end
49
-
50
- private
51
-
52
- def single_key_given?
53
- @ids.size == 1 && !@ids[0].is_a?(Array)
54
- end
55
-
56
- def validate_primary_key!
57
- if single_key_given?
58
- partition_key = @ids[0]
59
- sort_key = @options[:range_key]
60
-
61
- raise Dynamoid::Errors::MissingHashKey if partition_key.nil?
62
- raise Dynamoid::Errors::MissingRangeKey if @model_class.range_key && sort_key.nil?
63
- else
64
- ids = @ids.flatten(1)
65
-
66
- raise Dynamoid::Errors::MissingHashKey if ids.any? { |pk, _sk| pk.nil? }
67
- raise Errors::MissingRangeKey if @model_class.range_key && ids.any? { |_pk, sk| sk.nil? }
68
- end
69
- end
70
-
71
- def action_request_for_single_key
72
- partition_key = @ids[0]
73
-
74
- key = { @model_class.hash_key => cast_and_dump_attribute(@model_class.hash_key, partition_key) }
75
-
76
- if @model_class.range_key
77
- sort_key = @options[:range_key]
78
- key[@model_class.range_key] = cast_and_dump_attribute(@model_class.range_key, sort_key)
79
- end
80
-
81
- {
82
- get: {
83
- key: key,
84
- table_name: @model_class.table_name
85
- }
86
- }
87
- end
88
-
89
- def action_request_for_multiple_keys
90
- @ids.flatten(1).map do |id|
91
- if @model_class.range_key
92
- # expect [hash-key, range-key] pair
93
- pk, sk = id
94
- pk_dumped = cast_and_dump_attribute(@model_class.hash_key, pk)
95
- sk_dumped = cast_and_dump_attribute(@model_class.range_key, sk)
96
-
97
- key = { @model_class.hash_key => pk_dumped, @model_class.range_key => sk_dumped }
98
- else
99
- pk_dumped = cast_and_dump_attribute(@model_class.hash_key, id)
100
-
101
- key = { @model_class.hash_key => pk_dumped }
102
- end
103
-
104
- {
105
- get: {
106
- key: key,
107
- table_name: @model_class.table_name
108
- }
109
- }
110
- end
111
- end
112
-
113
- def cast_and_dump_attribute(name, value)
114
- attribute_options = @model_class.attributes[name]
115
- casted_value = TypeCasting.cast_field(value, attribute_options)
116
- Dumping.dump_field(casted_value, attribute_options)
117
- end
118
-
119
- def build_record_not_found_exception_message(responses)
120
- items = responses.map(&:item)
121
- ids = @ids.flatten(1)
122
-
123
- if single_key_given?
124
- id = ids[0]
125
- primary_key = @model_class.range_key ? "(#{id.inspect},#{@options[:range_key].inspect})" : id.inspect
126
- message = "Couldn't find #{@model_class.name} with primary key #{primary_key}"
127
- else
128
- ids_list = @model_class.range_key ? ids.map { |pk, sk| "(#{pk.inspect},#{sk.inspect})" } : ids.map(&:inspect)
129
- message = "Couldn't find all #{@model_class.name.pluralize} with primary keys [#{ids_list.join(', ')}] "
130
- message += "(found #{items.compact.size} results, but was looking for #{items.size})"
131
- end
132
-
133
- message
134
- end
135
- end
136
- end
137
- end