dynamoid-edge 1.1.0 → 1.1.1

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: f852381ddc94efb1130d84cdfe4aa714a72ff097
4
- data.tar.gz: 20bee710a9e4312738469f91c980607304788734
3
+ metadata.gz: 80ef1eaf479540803590814d5cf4d531faa12cf2
4
+ data.tar.gz: 901734ae300344fd6294f83818781216ad3f3425
5
5
  SHA512:
6
- metadata.gz: 8be902d06c5ed5ba977338c577877fe8561bee4207e9ab09e5d752d5625ceb80db89d97e34cb248ff96c4ea2be4341626848199fb57c4e891ddd8b461d6f3e94
7
- data.tar.gz: a45f9b3bcc175054b02437e294b237f1e5440b0e35f553371e84dd2da06c357cd8dd698489d1e1058cadf4cb6c97eeb7ab57d10ff43e9ab944b26fb4f408e2b5
6
+ metadata.gz: 3fde56e03afd236911cd55022d3581dc9974248cf2b4b5656bef9fb9de32c631fd9962bbad637da971b52c54531aec3f028e7c34541a29ff7df862b4dad9ae51
7
+ data.tar.gz: 7bfe7c2c91508f757b2018a6d9459cc5f306a0ef2d84eadfd004c534ce5c68cd763cbec842cce3055117c60deb9174604cff845ec40ccf9c46afec07fec6fde1
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "dynamoid-edge"
5
- s.version = "1.1.0"
5
+ s.version = "1.1.1"
6
6
 
7
7
  # Keep in sync with README
8
8
  s.authors = [
@@ -51,6 +51,7 @@ Gem::Specification.new do |s|
51
51
  lib/dynamoid/fields.rb
52
52
  lib/dynamoid/finders.rb
53
53
  lib/dynamoid/identity_map.rb
54
+ lib/dynamoid/indexes.rb
54
55
  lib/dynamoid/middleware/identity_map.rb
55
56
  lib/dynamoid/persistence.rb
56
57
  lib/dynamoid/validations.rb
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamoid-edge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Symonds
@@ -196,6 +196,7 @@ files:
196
196
  - lib/dynamoid/fields.rb
197
197
  - lib/dynamoid/finders.rb
198
198
  - lib/dynamoid/identity_map.rb
199
+ - lib/dynamoid/indexes.rb
199
200
  - lib/dynamoid/middleware/identity_map.rb
200
201
  - lib/dynamoid/persistence.rb
201
202
  - lib/dynamoid/validations.rb
@@ -219,8 +220,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
220
  version: '0'
220
221
  requirements: []
221
222
  rubyforge_project:
222
- rubygems_version: 2.4.5.1
223
+ rubygems_version: 2.4.5
223
224
  signing_key:
224
225
  specification_version: 4
225
226
  summary: Dynamoid is an ORM for Amazon's DynamoDB
226
227
  test_files: []
228
+ has_rdoc: