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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -5
- data/README.md +60 -1584
- data/lib/dynamoid/associations/association.rb +10 -2
- data/lib/dynamoid/associations/belongs_to.rb +3 -1
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +0 -1
- data/lib/dynamoid/associations/has_many.rb +0 -1
- data/lib/dynamoid/associations/has_one.rb +1 -2
- data/lib/dynamoid/associations/many_association.rb +1 -0
- data/lib/dynamoid/associations.rb +64 -48
- data/lib/dynamoid/components.rb +1 -0
- data/lib/dynamoid/criteria/chain.rb +43 -30
- data/lib/dynamoid/dirty.rb +3 -2
- data/lib/dynamoid/document.rb +29 -21
- data/lib/dynamoid/fields.rb +55 -41
- data/lib/dynamoid/finders.rb +1 -1
- data/lib/dynamoid/indexes.rb +29 -21
- data/lib/dynamoid/persistence/inc.rb +15 -7
- data/lib/dynamoid/persistence/save.rb +21 -14
- data/lib/dynamoid/persistence.rb +88 -54
- data/lib/dynamoid/transactions/mutation/base.rb +76 -0
- data/lib/dynamoid/transactions/mutation/builders/delete_request_builder.rb +44 -0
- data/lib/dynamoid/transactions/mutation/builders/update_request_builder.rb +139 -0
- data/lib/dynamoid/transactions/mutation/create.rb +52 -0
- data/lib/dynamoid/transactions/mutation/delete_with_instance.rb +80 -0
- data/lib/dynamoid/transactions/mutation/delete_with_primary_key.rb +62 -0
- data/lib/dynamoid/transactions/mutation/destroy.rb +96 -0
- data/lib/dynamoid/transactions/mutation/import.rb +84 -0
- data/lib/dynamoid/transactions/mutation/inc.rb +101 -0
- data/lib/dynamoid/transactions/mutation/increment.rb +63 -0
- data/lib/dynamoid/transactions/mutation/save.rb +194 -0
- data/lib/dynamoid/transactions/mutation/touch.rb +99 -0
- data/lib/dynamoid/transactions/mutation/update_attributes.rb +49 -0
- data/lib/dynamoid/transactions/mutation/update_fields.rb +185 -0
- data/lib/dynamoid/transactions/mutation/upsert.rb +94 -0
- data/lib/dynamoid/transactions/mutation.rb +935 -0
- data/lib/dynamoid/transactions/retrieval/find.rb +140 -0
- data/lib/dynamoid/transactions/retrieval.rb +150 -0
- data/lib/dynamoid/transactions.rb +63 -0
- data/lib/dynamoid/version.rb +1 -1
- data/lib/dynamoid.rb +1 -2
- metadata +23 -17
- data/lib/dynamoid/transaction_read/find.rb +0 -137
- data/lib/dynamoid/transaction_read.rb +0 -146
- data/lib/dynamoid/transaction_write/base.rb +0 -59
- data/lib/dynamoid/transaction_write/create.rb +0 -49
- data/lib/dynamoid/transaction_write/delete_with_instance.rb +0 -65
- data/lib/dynamoid/transaction_write/delete_with_primary_key.rb +0 -64
- data/lib/dynamoid/transaction_write/destroy.rb +0 -84
- data/lib/dynamoid/transaction_write/item_updater.rb +0 -60
- data/lib/dynamoid/transaction_write/save.rb +0 -177
- data/lib/dynamoid/transaction_write/update_attributes.rb +0 -46
- data/lib/dynamoid/transaction_write/update_fields.rb +0 -247
- data/lib/dynamoid/transaction_write/upsert.rb +0 -113
- 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
|
data/lib/dynamoid/version.rb
CHANGED
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.
|
|
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/
|
|
245
|
-
- lib/dynamoid/
|
|
246
|
-
- lib/dynamoid/
|
|
247
|
-
- lib/dynamoid/
|
|
248
|
-
- lib/dynamoid/
|
|
249
|
-
- lib/dynamoid/
|
|
250
|
-
- lib/dynamoid/
|
|
251
|
-
- lib/dynamoid/
|
|
252
|
-
- lib/dynamoid/
|
|
253
|
-
- lib/dynamoid/
|
|
254
|
-
- lib/dynamoid/
|
|
255
|
-
- lib/dynamoid/
|
|
256
|
-
- lib/dynamoid/
|
|
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.
|
|
267
|
-
changelog_uri: https://github.com/Dynamoid/dynamoid/blob/v3.
|
|
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.
|
|
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
|