synamoid 1.2.1
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 +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
|