synamoid 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +67 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +48 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +443 -0
- data/Rakefile +64 -0
- data/dynamoid.gemspec +53 -0
- data/lib/dynamoid.rb +53 -0
- data/lib/dynamoid/adapter.rb +190 -0
- data/lib/dynamoid/adapter_plugin/aws_sdk_v2.rb +892 -0
- data/lib/dynamoid/associations.rb +106 -0
- data/lib/dynamoid/associations/association.rb +116 -0
- data/lib/dynamoid/associations/belongs_to.rb +44 -0
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +40 -0
- data/lib/dynamoid/associations/has_many.rb +39 -0
- data/lib/dynamoid/associations/has_one.rb +39 -0
- data/lib/dynamoid/associations/many_association.rb +193 -0
- data/lib/dynamoid/associations/single_association.rb +69 -0
- data/lib/dynamoid/components.rb +37 -0
- data/lib/dynamoid/config.rb +58 -0
- data/lib/dynamoid/config/options.rb +78 -0
- data/lib/dynamoid/criteria.rb +29 -0
- data/lib/dynamoid/criteria/chain.rb +214 -0
- data/lib/dynamoid/dirty.rb +47 -0
- data/lib/dynamoid/document.rb +201 -0
- data/lib/dynamoid/errors.rb +66 -0
- data/lib/dynamoid/fields.rb +164 -0
- data/lib/dynamoid/finders.rb +199 -0
- data/lib/dynamoid/identity_map.rb +92 -0
- data/lib/dynamoid/indexes.rb +273 -0
- data/lib/dynamoid/middleware/identity_map.rb +16 -0
- data/lib/dynamoid/persistence.rb +359 -0
- data/lib/dynamoid/validations.rb +63 -0
- data/lib/dynamoid/version.rb +3 -0
- metadata +266 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Dynamoid
|
3
|
+
|
4
|
+
# This module defines the finder methods that hang off the document at the
|
5
|
+
# class level, like find, find_by_id, and the method_missing style finders.
|
6
|
+
module Finders
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
RANGE_MAP = {
|
10
|
+
'gt' => :range_greater_than,
|
11
|
+
'lt' => :range_less_than,
|
12
|
+
'gte' => :range_gte,
|
13
|
+
'lte' => :range_lte,
|
14
|
+
'begins_with' => :range_begins_with,
|
15
|
+
'between' => :range_between,
|
16
|
+
'eq' => :range_eq
|
17
|
+
}
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
|
21
|
+
# Find one or many objects, specified by one id or an array of ids.
|
22
|
+
#
|
23
|
+
# @param [Array/String] *id an array of ids or one single id
|
24
|
+
#
|
25
|
+
# @return [Dynamoid::Document] one object or an array of objects, depending on whether the input was an array or not
|
26
|
+
#
|
27
|
+
# @since 0.2.0
|
28
|
+
def find(*ids)
|
29
|
+
options = if ids.last.is_a? Hash
|
30
|
+
ids.slice!(-1)
|
31
|
+
else
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
expects_array = ids.first.kind_of?(Array)
|
35
|
+
|
36
|
+
ids = Array(ids.flatten.uniq)
|
37
|
+
if ids.count == 1
|
38
|
+
result = self.find_by_id(ids.first, options)
|
39
|
+
expects_array ? Array(result) : result
|
40
|
+
else
|
41
|
+
find_all(ids)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return objects found by the given array of ids, either hash keys, or hash/range key combinations using BatchGet.
|
46
|
+
# Returns empty array if no results found.
|
47
|
+
#
|
48
|
+
# @param [Array<ID>] ids
|
49
|
+
# @param [Hash] options: Passed to the underlying query.
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# find all the user with hash key
|
53
|
+
# User.find_all(['1', '2', '3'])
|
54
|
+
#
|
55
|
+
# find all the tweets using hash key and range key with consistent read
|
56
|
+
# Tweet.find_all([['1', 'red'], ['1', 'green']], :consistent_read => true)
|
57
|
+
def find_all(ids, options = {})
|
58
|
+
items = Dynamoid.adapter.read(self.table_name, ids, options)
|
59
|
+
items ? items[self.table_name].map{|i| from_database(i)} : []
|
60
|
+
end
|
61
|
+
|
62
|
+
# Find one object directly by id.
|
63
|
+
#
|
64
|
+
# @param [String] id the id of the object to find
|
65
|
+
#
|
66
|
+
# @return [Dynamoid::Document] the found object, or nil if nothing was found
|
67
|
+
#
|
68
|
+
# @since 0.2.0
|
69
|
+
def find_by_id(id, options = {})
|
70
|
+
if item = Dynamoid.adapter.read(self.table_name, id, options)
|
71
|
+
from_database(item)
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Find one object directly by hash and range keys
|
78
|
+
#
|
79
|
+
# @param [String] hash_key of the object to find
|
80
|
+
# @param [String/Number] range_key of the object to find
|
81
|
+
#
|
82
|
+
def find_by_composite_key(hash_key, range_key, options = {})
|
83
|
+
find_by_id(hash_key, options.merge({:range_key => range_key}))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Find all objects by hash and range keys.
|
87
|
+
#
|
88
|
+
# @example find all ChamberTypes whose level is greater than 1
|
89
|
+
# class ChamberType
|
90
|
+
# include Dynamoid::Document
|
91
|
+
# field :chamber_type, :string
|
92
|
+
# range :level, :integer
|
93
|
+
# table :key => :chamber_type
|
94
|
+
# end
|
95
|
+
# ChamberType.find_all_by_composite_key('DustVault', range_greater_than: 1)
|
96
|
+
#
|
97
|
+
# @param [String] hash_key of the objects to find
|
98
|
+
# @param [Hash] options the options for the range key
|
99
|
+
# @option options [Range] :range_value find the range key within this range
|
100
|
+
# @option options [Number] :range_greater_than find range keys greater than this
|
101
|
+
# @option options [Number] :range_less_than find range keys less than this
|
102
|
+
# @option options [Number] :range_gte find range keys greater than or equal to this
|
103
|
+
# @option options [Number] :range_lte find range keys less than or equal to this
|
104
|
+
#
|
105
|
+
# @return [Array] an array of all matching items
|
106
|
+
#
|
107
|
+
def find_all_by_composite_key(hash_key, options = {})
|
108
|
+
Dynamoid.adapter.query(self.table_name, options.merge({hash_value: hash_key})).collect do |item|
|
109
|
+
from_database(item)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Find all objects by using local secondary or global secondary index
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# class User
|
117
|
+
# include Dynamoid::Document
|
118
|
+
# field :email, :string
|
119
|
+
# field :age, :integer
|
120
|
+
# field :gender, :string
|
121
|
+
# field :rank :number
|
122
|
+
# table :key => :email
|
123
|
+
# global_secondary_index :hash_key => :age, :range_key => :rank
|
124
|
+
# end
|
125
|
+
# # NOTE: the first param and the second param are both hashes,
|
126
|
+
# # so curly braces must be used on first hash param if sending both params
|
127
|
+
# User.find_all_by_secondary_index({:age => 5}, :range => {"rank.lte" => 10})
|
128
|
+
#
|
129
|
+
# @param [Hash] eg: {:age => 5}
|
130
|
+
# @param [Hash] eg: {"rank.lte" => 10}
|
131
|
+
# @param [Hash] options - query filter, projected keys, scan_index_forward etc
|
132
|
+
# @return [Array] an array of all matching items
|
133
|
+
def find_all_by_secondary_index(hash, options = {})
|
134
|
+
range = options[:range] || {}
|
135
|
+
hash_key_field, hash_key_value = hash.first
|
136
|
+
range_key_field, range_key_value = range.first
|
137
|
+
range_op_mapped = nil
|
138
|
+
|
139
|
+
if range_key_field
|
140
|
+
range_key_field = range_key_field.to_s
|
141
|
+
range_key_op = "eq"
|
142
|
+
if range_key_field.include?(".")
|
143
|
+
range_key_field, range_key_op = range_key_field.split(".", 2)
|
144
|
+
end
|
145
|
+
range_op_mapped = RANGE_MAP.fetch(range_key_op)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Find the index
|
149
|
+
index = self.find_index(hash_key_field, range_key_field)
|
150
|
+
raise Dynamoid::Errors::MissingIndex.new("attempted to find #{[hash_key_field, range_key_field]}") if index.nil?
|
151
|
+
|
152
|
+
# query
|
153
|
+
opts = {
|
154
|
+
:hash_key => hash_key_field.to_s,
|
155
|
+
:hash_value => hash_key_value,
|
156
|
+
:index_name => index.name,
|
157
|
+
}
|
158
|
+
if range_key_field
|
159
|
+
opts[:range_key] = range_key_field
|
160
|
+
opts[range_op_mapped] = range_key_value
|
161
|
+
end
|
162
|
+
dynamo_options = opts.merge(options.reject {|key, _| key == :range })
|
163
|
+
Dynamoid.adapter.query(self.table_name, dynamo_options).map do |item|
|
164
|
+
from_database(item)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Find using exciting method_missing finders attributes. Uses criteria chains under the hood to accomplish this neatness.
|
169
|
+
#
|
170
|
+
# @example find a user by a first name
|
171
|
+
# User.find_by_first_name('Josh')
|
172
|
+
#
|
173
|
+
# @example find all users by first and last name
|
174
|
+
# User.find_all_by_first_name_and_last_name('Josh', 'Symonds')
|
175
|
+
#
|
176
|
+
# @return [Dynamoid::Document/Array] the found object, or an array of found objects if all was somewhere in the method
|
177
|
+
#
|
178
|
+
# @since 0.2.0
|
179
|
+
def method_missing(method, *args)
|
180
|
+
if method =~ /find/
|
181
|
+
finder = method.to_s.split('_by_').first
|
182
|
+
attributes = method.to_s.split('_by_').last.split('_and_')
|
183
|
+
|
184
|
+
chain = Dynamoid::Criteria::Chain.new(self)
|
185
|
+
chain.query = Hash.new.tap {|h| attributes.each_with_index {|attr, index| h[attr.to_sym] = args[index]}}
|
186
|
+
|
187
|
+
if finder =~ /all/
|
188
|
+
return chain.all
|
189
|
+
else
|
190
|
+
return chain.first
|
191
|
+
end
|
192
|
+
else
|
193
|
+
super
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Dynamoid
|
2
|
+
module IdentityMap
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def self.clear
|
6
|
+
Dynamoid.included_models.each { |m| m.identity_map.clear }
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def identity_map
|
11
|
+
@identity_map ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def from_database(attrs = {})
|
15
|
+
return super if identity_map_off?
|
16
|
+
|
17
|
+
key = identity_map_key(attrs)
|
18
|
+
document = identity_map[key]
|
19
|
+
|
20
|
+
if document.nil?
|
21
|
+
document = super
|
22
|
+
identity_map[key] = document
|
23
|
+
else
|
24
|
+
document.load(attrs)
|
25
|
+
end
|
26
|
+
|
27
|
+
document
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_by_id(id, options = {})
|
31
|
+
return super if identity_map_off?
|
32
|
+
|
33
|
+
key = id.to_s
|
34
|
+
|
35
|
+
if range_key = options[:range_key]
|
36
|
+
key += "::#{range_key}"
|
37
|
+
end
|
38
|
+
|
39
|
+
if identity_map[key]
|
40
|
+
identity_map[key]
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def identity_map_key(attrs)
|
47
|
+
key = attrs[hash_key].to_s
|
48
|
+
if range_key
|
49
|
+
key += "::#{attrs[range_key]}"
|
50
|
+
end
|
51
|
+
key
|
52
|
+
end
|
53
|
+
|
54
|
+
def identity_map_on?
|
55
|
+
Dynamoid::Config.identity_map
|
56
|
+
end
|
57
|
+
|
58
|
+
def identity_map_off?
|
59
|
+
!identity_map_on?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def identity_map
|
64
|
+
self.class.identity_map
|
65
|
+
end
|
66
|
+
|
67
|
+
def save(*args)
|
68
|
+
return super if self.class.identity_map_off?
|
69
|
+
|
70
|
+
if result = super
|
71
|
+
identity_map[identity_map_key] = self
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete
|
77
|
+
return super if self.class.identity_map_off?
|
78
|
+
|
79
|
+
identity_map.delete(identity_map_key)
|
80
|
+
super
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def identity_map_key
|
85
|
+
key = hash_key.to_s
|
86
|
+
if self.class.range_key
|
87
|
+
key += "::#{range_value}"
|
88
|
+
end
|
89
|
+
key
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module Dynamoid
|
2
|
+
module Indexes
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :local_secondary_indexes
|
7
|
+
class_attribute :global_secondary_indexes
|
8
|
+
self.local_secondary_indexes = {}
|
9
|
+
self.global_secondary_indexes = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Defines a Global Secondary index on a table. Keys can be specified as
|
14
|
+
# hash-only, or hash & range.
|
15
|
+
#
|
16
|
+
# @param [Hash] options options to pass for this table
|
17
|
+
# @option options [Symbol] :name the name for the index; this still gets
|
18
|
+
# namespaced. If not specified, will use a default name.
|
19
|
+
# @option options [Symbol] :hash_key the index hash key column.
|
20
|
+
# @option options [Symbol] :range_key the index range key column (if
|
21
|
+
# applicable).
|
22
|
+
# @option options [Symbol, Array<Symbol>] :projected_attributes table
|
23
|
+
# attributes to project for this index. Can be :keys_only, :all
|
24
|
+
# or an array of included fields. If not specified, defaults to
|
25
|
+
# :keys_only.
|
26
|
+
# @option options [Integer] :read_capacity set the read capacity for the
|
27
|
+
# index; does not work on existing indexes.
|
28
|
+
# @option options [Integer] :write_capacity set the write capacity for
|
29
|
+
# the index; does not work on existing indexes.
|
30
|
+
def global_secondary_index(options={})
|
31
|
+
unless options.present?
|
32
|
+
raise Dynamoid::Errors::InvalidIndex.new('empty index definition')
|
33
|
+
end
|
34
|
+
|
35
|
+
unless options[:hash_key].present?
|
36
|
+
raise Dynamoid::Errors::InvalidIndex.new(
|
37
|
+
'A global secondary index requires a :hash_key to be specified'
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
index_opts = {
|
42
|
+
:read_capacity => Dynamoid::Config.read_capacity,
|
43
|
+
:write_capacity => Dynamoid::Config.write_capacity
|
44
|
+
}.merge(options)
|
45
|
+
|
46
|
+
index_opts[:dynamoid_class] = self
|
47
|
+
index_opts[:type] = :global_secondary
|
48
|
+
|
49
|
+
index = Dynamoid::Indexes::Index.new(index_opts)
|
50
|
+
gsi_key = index_key(options[:hash_key], options[:range_key])
|
51
|
+
self.global_secondary_indexes[gsi_key] = index
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Defines a local secondary index on a table. Will use the same primary
|
57
|
+
# hash key as the table.
|
58
|
+
#
|
59
|
+
# @param [Hash] options options to pass for this index.
|
60
|
+
# @option options [Symbol] :name the name for the index; this still gets
|
61
|
+
# namespaced. If not specified, a name is automatically generated.
|
62
|
+
# @option options [Symbol] :range_key the range key column for the index.
|
63
|
+
# @option options [Symbol, Array<Symbol>] :projected_attributes table
|
64
|
+
# attributes to project for this index. Can be :keys_only, :all
|
65
|
+
# or an array of included fields. If not specified, defaults to
|
66
|
+
# :keys_only.
|
67
|
+
def local_secondary_index(options={})
|
68
|
+
unless options.present?
|
69
|
+
raise Dynamoid::Errors::InvalidIndex.new('empty index definition')
|
70
|
+
end
|
71
|
+
|
72
|
+
primary_hash_key = self.hash_key
|
73
|
+
primary_range_key = self.range_key
|
74
|
+
index_range_key = options[:range_key]
|
75
|
+
|
76
|
+
unless index_range_key.present?
|
77
|
+
raise Dynamoid::Errors::InvalidIndex.new('A local secondary index '\
|
78
|
+
'requires a :range_key to be specified')
|
79
|
+
end
|
80
|
+
|
81
|
+
if primary_range_key.present? && index_range_key == primary_range_key
|
82
|
+
raise Dynamoid::Errors::InvalidIndex.new('A local secondary index'\
|
83
|
+
' must use a different :range_key than the primary key')
|
84
|
+
end
|
85
|
+
|
86
|
+
index_opts = options.merge({
|
87
|
+
:dynamoid_class => self,
|
88
|
+
:type => :local_secondary,
|
89
|
+
:hash_key => primary_hash_key
|
90
|
+
})
|
91
|
+
|
92
|
+
index = Dynamoid::Indexes::Index.new(index_opts)
|
93
|
+
key = index_key(primary_hash_key, index_range_key)
|
94
|
+
self.local_secondary_indexes[key] = index
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def find_index(hash, range=nil)
|
100
|
+
index = self.indexes[index_key(hash, range)]
|
101
|
+
index
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
# Returns true iff the provided hash[,range] key combo is a local
|
106
|
+
# secondary index.
|
107
|
+
#
|
108
|
+
# @param [Symbol] hash hash key name.
|
109
|
+
# @param [Symbol] range range key name.
|
110
|
+
# @return [Boolean] true iff provided keys correspond to a local
|
111
|
+
# secondary index.
|
112
|
+
def is_local_secondary_index?(hash, range=nil)
|
113
|
+
self.local_secondary_indexes[index_key(hash, range)].present?
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Returns true iff the provided hash[,range] key combo is a global
|
118
|
+
# secondary index.
|
119
|
+
#
|
120
|
+
# @param [Symbol] hash hash key name.
|
121
|
+
# @param [Symbol] range range key name.
|
122
|
+
# @return [Boolean] true iff provided keys correspond to a global
|
123
|
+
# secondary index.
|
124
|
+
def is_global_secondary_index?(hash, range=nil)
|
125
|
+
self.global_secondary_indexes[index_key(hash, range)].present?
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# Generates a convenient lookup key name for a hash/range index.
|
130
|
+
# Should normally not be used directly.
|
131
|
+
#
|
132
|
+
# @param [Symbol] hash hash key name.
|
133
|
+
# @param [Symbol] range range key name.
|
134
|
+
# @return [String] returns "hash" if hash only, "hash_range" otherwise.
|
135
|
+
def index_key(hash, range=nil)
|
136
|
+
name = hash.to_s
|
137
|
+
if range.present?
|
138
|
+
name += "_#{range.to_s}"
|
139
|
+
end
|
140
|
+
name
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
# Generates a default index name.
|
145
|
+
#
|
146
|
+
# @param [Symbol] hash hash key name.
|
147
|
+
# @param [Symbol] range range key name.
|
148
|
+
# @return [String] index name of the form "table_name_index_index_key".
|
149
|
+
def index_name(hash, range=nil)
|
150
|
+
"#{self.table_name}_index_#{self.index_key(hash, range)}"
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
# Convenience method to return all indexes on the table.
|
155
|
+
#
|
156
|
+
# @return [Hash<String, Object>] the combined hash of global and local
|
157
|
+
# secondary indexes.
|
158
|
+
def indexes
|
159
|
+
self.local_secondary_indexes.merge(self.global_secondary_indexes)
|
160
|
+
end
|
161
|
+
|
162
|
+
def indexed_hash_keys
|
163
|
+
self.global_secondary_indexes.map do |name, index|
|
164
|
+
index.hash_key.to_s
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# Represents the attributes of a DynamoDB index.
|
171
|
+
class Index
|
172
|
+
include ActiveModel::Validations
|
173
|
+
|
174
|
+
PROJECTION_TYPES = [:keys_only, :all].to_set
|
175
|
+
DEFAULT_PROJECTION_TYPE = :keys_only
|
176
|
+
|
177
|
+
attr_accessor :name, :dynamoid_class, :type, :hash_key, :range_key,
|
178
|
+
:hash_key_schema, :range_key_schema, :projected_attributes,
|
179
|
+
:read_capacity, :write_capacity
|
180
|
+
|
181
|
+
|
182
|
+
validate do
|
183
|
+
validate_index_type
|
184
|
+
validate_hash_key
|
185
|
+
validate_range_key
|
186
|
+
validate_projected_attributes
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
def initialize(attrs={})
|
191
|
+
unless attrs[:dynamoid_class].present?
|
192
|
+
raise Dynamoid::Errors::InvalidIndex.new(':dynamoid_class is required')
|
193
|
+
end
|
194
|
+
|
195
|
+
@dynamoid_class = attrs[:dynamoid_class]
|
196
|
+
@type = attrs[:type]
|
197
|
+
@hash_key = attrs[:hash_key]
|
198
|
+
@range_key = attrs[:range_key]
|
199
|
+
@name = attrs[:name] || @dynamoid_class.index_name(@hash_key, @range_key)
|
200
|
+
@projected_attributes =
|
201
|
+
attrs[:projected_attributes] || DEFAULT_PROJECTION_TYPE
|
202
|
+
@read_capacity = attrs[:read_capacity]
|
203
|
+
@write_capacity = attrs[:write_capacity]
|
204
|
+
|
205
|
+
raise Dynamoid::Errors::InvalidIndex.new(self) unless self.valid?
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# Convenience method to determine the projection type for an index.
|
210
|
+
# Projection types are: :keys_only, :all, :include.
|
211
|
+
#
|
212
|
+
# @return [Symbol] the projection type.
|
213
|
+
def projection_type
|
214
|
+
if @projected_attributes.is_a? Array
|
215
|
+
:include
|
216
|
+
else
|
217
|
+
@projected_attributes
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def validate_projected_attributes
|
225
|
+
unless (@projected_attributes.is_a?(Array) ||
|
226
|
+
PROJECTION_TYPES.include?(@projected_attributes))
|
227
|
+
errors.add(:projected_attributes, 'Invalid projected attributes specified.')
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def validate_index_type
|
232
|
+
unless (@type.present? &&
|
233
|
+
[:local_secondary, :global_secondary].include?(@type))
|
234
|
+
errors.add(:type, 'Invalid index :type specified')
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def validate_range_key
|
239
|
+
if @range_key.present?
|
240
|
+
range_field_attributes = @dynamoid_class.attributes[@range_key]
|
241
|
+
if range_field_attributes.present?
|
242
|
+
range_key_type = range_field_attributes[:type]
|
243
|
+
if Dynamoid::Fields::PERMITTED_KEY_TYPES.include?(range_key_type)
|
244
|
+
@range_key_schema = {
|
245
|
+
@range_key => @dynamoid_class.dynamo_type(range_key_type)
|
246
|
+
}
|
247
|
+
else
|
248
|
+
errors.add(:range_key, 'Index :range_key is not a valid key type')
|
249
|
+
end
|
250
|
+
else
|
251
|
+
errors.add(:range_key, "No such field #{@range_key} defined on table")
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def validate_hash_key
|
257
|
+
hash_field_attributes = @dynamoid_class.attributes[@hash_key]
|
258
|
+
if hash_field_attributes.present?
|
259
|
+
hash_field_type = hash_field_attributes[:type]
|
260
|
+
if Dynamoid::Fields::PERMITTED_KEY_TYPES.include?(hash_field_type)
|
261
|
+
@hash_key_schema = {
|
262
|
+
@hash_key => @dynamoid_class.dynamo_type(hash_field_type)
|
263
|
+
}
|
264
|
+
else
|
265
|
+
errors.add(:hash_key, 'Index :hash_key is not a valid key type')
|
266
|
+
end
|
267
|
+
else
|
268
|
+
errors.add(:hash_key, "No such field #{@hash_key} defined on table")
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|