adept_dynamoid 0.5.0.8 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -14,11 +14,11 @@ require 'rake'
14
14
  require 'jeweler'
15
15
  Jeweler::Tasks.new do |gem|
16
16
  # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "adept_dynamoid"
17
+ gem.name = "dynamoid"
18
18
  gem.homepage = "http://github.com/Veraticus/Dynamoid"
19
19
  gem.license = "MIT"
20
20
  gem.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
21
- gem.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement. Make sure you add require 'dynamoid' to before your configure script with adept_dynamoid"
21
+ gem.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement."
22
22
  gem.email = "josh@joshsymonds.com"
23
23
  gem.authors = ["Josh Symonds"]
24
24
  # dependencies defined in Gemfile
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0.8
1
+ 0.0.0
@@ -0,0 +1,195 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "adept_dynamoid"
8
+ s.version = "0.6.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Josh Symonds"]
12
+ s.date = "2012-12-05"
13
+ s.description = "Dynamoid is an ORM for Amazon's DynamoDB that supports offline development, associations, querying, and everything else you'd expect from an ActiveRecord-style replacement. Make sure you add require 'dynamoid' to before your configure script with adept_dynamoid"
14
+ s.email = "josh@joshsymonds.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".project",
22
+ ".rspec",
23
+ "Dynamoid.gemspec",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.markdown",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "adept_dynamoid.gemspec",
31
+ "doc/.nojekyll",
32
+ "doc/Dynamoid.html",
33
+ "doc/Dynamoid/Adapter.html",
34
+ "doc/Dynamoid/Adapter/AwsSdk.html",
35
+ "doc/Dynamoid/Adapter/Local.html",
36
+ "doc/Dynamoid/Associations.html",
37
+ "doc/Dynamoid/Associations/Association.html",
38
+ "doc/Dynamoid/Associations/BelongsTo.html",
39
+ "doc/Dynamoid/Associations/ClassMethods.html",
40
+ "doc/Dynamoid/Associations/HasAndBelongsToMany.html",
41
+ "doc/Dynamoid/Associations/HasMany.html",
42
+ "doc/Dynamoid/Associations/HasOne.html",
43
+ "doc/Dynamoid/Associations/ManyAssociation.html",
44
+ "doc/Dynamoid/Associations/SingleAssociation.html",
45
+ "doc/Dynamoid/Components.html",
46
+ "doc/Dynamoid/Config.html",
47
+ "doc/Dynamoid/Config/Options.html",
48
+ "doc/Dynamoid/Criteria.html",
49
+ "doc/Dynamoid/Criteria/Chain.html",
50
+ "doc/Dynamoid/Criteria/ClassMethods.html",
51
+ "doc/Dynamoid/Document.html",
52
+ "doc/Dynamoid/Document/ClassMethods.html",
53
+ "doc/Dynamoid/Errors.html",
54
+ "doc/Dynamoid/Errors/DocumentNotValid.html",
55
+ "doc/Dynamoid/Errors/Error.html",
56
+ "doc/Dynamoid/Errors/InvalidField.html",
57
+ "doc/Dynamoid/Errors/MissingRangeKey.html",
58
+ "doc/Dynamoid/Fields.html",
59
+ "doc/Dynamoid/Fields/ClassMethods.html",
60
+ "doc/Dynamoid/Finders.html",
61
+ "doc/Dynamoid/Finders/ClassMethods.html",
62
+ "doc/Dynamoid/Indexes.html",
63
+ "doc/Dynamoid/Indexes/ClassMethods.html",
64
+ "doc/Dynamoid/Indexes/Index.html",
65
+ "doc/Dynamoid/Persistence.html",
66
+ "doc/Dynamoid/Persistence/ClassMethods.html",
67
+ "doc/Dynamoid/Validations.html",
68
+ "doc/_index.html",
69
+ "doc/class_list.html",
70
+ "doc/css/common.css",
71
+ "doc/css/full_list.css",
72
+ "doc/css/style.css",
73
+ "doc/file.LICENSE.html",
74
+ "doc/file.README.html",
75
+ "doc/file_list.html",
76
+ "doc/frames.html",
77
+ "doc/index.html",
78
+ "doc/js/app.js",
79
+ "doc/js/full_list.js",
80
+ "doc/js/jquery.js",
81
+ "doc/method_list.html",
82
+ "doc/top-level-namespace.html",
83
+ "lib/dynamoid.rb",
84
+ "lib/dynamoid/adapter.rb",
85
+ "lib/dynamoid/adapter/aws_sdk.rb",
86
+ "lib/dynamoid/associations.rb",
87
+ "lib/dynamoid/associations/association.rb",
88
+ "lib/dynamoid/associations/belongs_to.rb",
89
+ "lib/dynamoid/associations/has_and_belongs_to_many.rb",
90
+ "lib/dynamoid/associations/has_many.rb",
91
+ "lib/dynamoid/associations/has_one.rb",
92
+ "lib/dynamoid/associations/many_association.rb",
93
+ "lib/dynamoid/associations/single_association.rb",
94
+ "lib/dynamoid/components.rb",
95
+ "lib/dynamoid/config.rb",
96
+ "lib/dynamoid/config/options.rb",
97
+ "lib/dynamoid/criteria.rb",
98
+ "lib/dynamoid/criteria/chain.rb",
99
+ "lib/dynamoid/dirty.rb",
100
+ "lib/dynamoid/document.rb",
101
+ "lib/dynamoid/errors.rb",
102
+ "lib/dynamoid/fields.rb",
103
+ "lib/dynamoid/finders.rb",
104
+ "lib/dynamoid/identity_map.rb",
105
+ "lib/dynamoid/indexes.rb",
106
+ "lib/dynamoid/indexes/index.rb",
107
+ "lib/dynamoid/middleware/identity_map.rb",
108
+ "lib/dynamoid/persistence.rb",
109
+ "lib/dynamoid/validations.rb",
110
+ "spec/app/models/address.rb",
111
+ "spec/app/models/camel_case.rb",
112
+ "spec/app/models/magazine.rb",
113
+ "spec/app/models/message.rb",
114
+ "spec/app/models/sponsor.rb",
115
+ "spec/app/models/subscription.rb",
116
+ "spec/app/models/tweet.rb",
117
+ "spec/app/models/user.rb",
118
+ "spec/dynamoid/adapter/aws_sdk_spec.rb",
119
+ "spec/dynamoid/adapter_spec.rb",
120
+ "spec/dynamoid/associations/association_spec.rb",
121
+ "spec/dynamoid/associations/belongs_to_spec.rb",
122
+ "spec/dynamoid/associations/has_and_belongs_to_many_spec.rb",
123
+ "spec/dynamoid/associations/has_many_spec.rb",
124
+ "spec/dynamoid/associations/has_one_spec.rb",
125
+ "spec/dynamoid/associations_spec.rb",
126
+ "spec/dynamoid/config_spec.rb",
127
+ "spec/dynamoid/criteria/chain_spec.rb",
128
+ "spec/dynamoid/criteria_spec.rb",
129
+ "spec/dynamoid/dirty_spec.rb",
130
+ "spec/dynamoid/document_spec.rb",
131
+ "spec/dynamoid/fields_spec.rb",
132
+ "spec/dynamoid/finders_spec.rb",
133
+ "spec/dynamoid/identity_map_spec.rb",
134
+ "spec/dynamoid/indexes/index_spec.rb",
135
+ "spec/dynamoid/indexes_spec.rb",
136
+ "spec/dynamoid/persistence_spec.rb",
137
+ "spec/dynamoid/validations_spec.rb",
138
+ "spec/dynamoid_spec.rb",
139
+ "spec/spec_helper.rb"
140
+ ]
141
+ s.homepage = "http://github.com/Veraticus/Dynamoid"
142
+ s.licenses = ["MIT"]
143
+ s.require_paths = ["lib"]
144
+ s.rubygems_version = "1.8.24"
145
+ s.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
146
+
147
+ if s.respond_to? :specification_version then
148
+ s.specification_version = 3
149
+
150
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
151
+ s.add_runtime_dependency(%q<activemodel>, [">= 0"])
152
+ s.add_runtime_dependency(%q<tzinfo>, [">= 0"])
153
+ s.add_runtime_dependency(%q<aws-sdk>, [">= 0"])
154
+ s.add_development_dependency(%q<rake>, [">= 0"])
155
+ s.add_development_dependency(%q<rspec>, [">= 0"])
156
+ s.add_development_dependency(%q<bundler>, [">= 0"])
157
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
158
+ s.add_development_dependency(%q<yard>, [">= 0"])
159
+ s.add_development_dependency(%q<redcarpet>, ["= 1.17.2"])
160
+ s.add_development_dependency(%q<github-markup>, [">= 0"])
161
+ s.add_development_dependency(%q<pry>, [">= 0"])
162
+ s.add_development_dependency(%q<fake_dynamo>, [">= 0"])
163
+ s.add_development_dependency(%q<mocha>, ["= 0.10.0"])
164
+ else
165
+ s.add_dependency(%q<activemodel>, [">= 0"])
166
+ s.add_dependency(%q<tzinfo>, [">= 0"])
167
+ s.add_dependency(%q<aws-sdk>, [">= 0"])
168
+ s.add_dependency(%q<rake>, [">= 0"])
169
+ s.add_dependency(%q<rspec>, [">= 0"])
170
+ s.add_dependency(%q<bundler>, [">= 0"])
171
+ s.add_dependency(%q<jeweler>, [">= 0"])
172
+ s.add_dependency(%q<yard>, [">= 0"])
173
+ s.add_dependency(%q<redcarpet>, ["= 1.17.2"])
174
+ s.add_dependency(%q<github-markup>, [">= 0"])
175
+ s.add_dependency(%q<pry>, [">= 0"])
176
+ s.add_dependency(%q<fake_dynamo>, [">= 0"])
177
+ s.add_dependency(%q<mocha>, ["= 0.10.0"])
178
+ end
179
+ else
180
+ s.add_dependency(%q<activemodel>, [">= 0"])
181
+ s.add_dependency(%q<tzinfo>, [">= 0"])
182
+ s.add_dependency(%q<aws-sdk>, [">= 0"])
183
+ s.add_dependency(%q<rake>, [">= 0"])
184
+ s.add_dependency(%q<rspec>, [">= 0"])
185
+ s.add_dependency(%q<bundler>, [">= 0"])
186
+ s.add_dependency(%q<jeweler>, [">= 0"])
187
+ s.add_dependency(%q<yard>, [">= 0"])
188
+ s.add_dependency(%q<redcarpet>, ["= 1.17.2"])
189
+ s.add_dependency(%q<github-markup>, [">= 0"])
190
+ s.add_dependency(%q<pry>, [">= 0"])
191
+ s.add_dependency(%q<fake_dynamo>, [">= 0"])
192
+ s.add_dependency(%q<mocha>, ["= 0.10.0"])
193
+ end
194
+ end
195
+
@@ -73,7 +73,7 @@ module Dynamoid
73
73
  ids = ids.collect{|id| range_key ? [id, range_key] : id}
74
74
  if Dynamoid::Config.partitioning?
75
75
  results = batch_get_item(table => id_with_partitions(ids))
76
- {table => result_for_partition(results[table])}
76
+ {table => result_for_partition(results[table],table)}
77
77
  else
78
78
  batch_get_item(table => ids)
79
79
  end
@@ -81,7 +81,7 @@ module Dynamoid
81
81
  if Dynamoid::Config.partitioning?
82
82
  ids = range_key ? [[ids, range_key]] : ids
83
83
  results = batch_get_item(table => id_with_partitions(ids))
84
- result_for_partition(results[table]).first
84
+ result_for_partition(results[table],table).first
85
85
  else
86
86
  get_item(table, ids, options)
87
87
  end
@@ -91,15 +91,31 @@ module Dynamoid
91
91
  # Delete an item from a table. If partitioning is turned on, deletes all partitioned keys as well.
92
92
  #
93
93
  # @param [String] table the name of the table to write the object to
94
- # @param [String] id the id of the record
95
- # @param [Number] range_key the range key of the record
94
+ # @param [Array] ids to delete, can also be a string of just one id
95
+ # @param [Array] range_key of the record to delete, can also be a string of just one range_key
96
96
  #
97
- # @since 0.2.0
98
- def delete(table, id, options = {})
99
- if Dynamoid::Config.partitioning?
100
- id_with_partitions(id).each {|i| delete_item(table, i, options)}
97
+ def delete(table, ids, options = {})
98
+ range_key = options[:range_key] #array of range keys that matches the ids passed in
99
+ if ids.respond_to?(:each)
100
+ if range_key.respond_to?(:each)
101
+ #turn ids into array of arrays each element being hash_key, range_key
102
+ ids = ids.each_with_index.map{|id,i| [id,range_key[i]]}
103
+ else
104
+ ids = range_key ? [[ids, range_key]] : ids
105
+ end
106
+
107
+ if Dynamoid::Config.partitioning?
108
+ batch_delete_item(table => id_with_partitions(ids))
109
+ else
110
+ batch_delete_item(table => ids)
111
+ end
101
112
  else
102
- delete_item(table, id, options)
113
+ if Dynamoid::Config.partitioning?
114
+ ids = range_key ? [[ids, range_key]] : ids
115
+ batch_delete_item(table => id_with_partitions(ids))
116
+ else
117
+ delete_item(table, ids, options)
118
+ end
103
119
  end
104
120
  end
105
121
 
@@ -112,7 +128,7 @@ module Dynamoid
112
128
  def scan(table, query, opts = {})
113
129
  if Dynamoid::Config.partitioning?
114
130
  results = benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
115
- result_for_partition(results)
131
+ result_for_partition(results,table)
116
132
  else
117
133
  benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
118
134
  end
@@ -141,27 +157,59 @@ module Dynamoid
141
157
  def id_with_partitions(ids)
142
158
  Array(ids).collect {|id| (0...Dynamoid::Config.partition_size).collect{|n| id.is_a?(Array) ? ["#{id.first}.#{n}", id.last] : "#{id}.#{n}"}}.flatten(1)
143
159
  end
160
+
161
+ #Get original id (hash_key) and partiton number from a hash_key
162
+ #
163
+ # @param [String] id the id or hash_key of a record, ex. xxxxx.13
164
+ #
165
+ # @return [String,String] original_id and the partition number, ex original_id = xxxxx partition = 13
166
+ def get_original_id_and_partition id
167
+ partition = id.split('.').last
168
+ id = id.split(".#{partition}").first
169
+
170
+ return id, partition
171
+ end
144
172
 
145
- # Takes an array of results that are partitioned, find the most recently updated one, and return only it. Compares each result by
173
+ # Takes an array of query results that are partitioned, find the most recently updated ones that share an id and range_key, and return only the most recently updated. Compares each result by
146
174
  # their id and updated_at attributes; if the updated_at is the greatest, then it must be the correct result.
147
175
  #
148
176
  # @param [Array] returned partitioned results from a query
177
+ # @param [String] table_name the name of the table
149
178
  #
150
179
  # @since 0.2.0
151
- def result_for_partition(results)
152
- {}.tap do |hash|
153
- Array(results).each do |result|
154
- next if result.nil?
155
- #Need to find the value of id with out the . and partition number
156
- partition = result[:id].split('.').last
157
- id = result[:id].split(".#{partition}").first
180
+ def result_for_partition(results, table_name)
181
+ table = Dynamoid::Adapter::AwsSdk.get_table(table_name)
182
+
183
+ if table.range_key
184
+ range_key_name = table.range_key.name.to_sym
185
+
186
+ final_hash = {}
187
+
188
+ results.each do |record|
189
+ test_record = final_hash[record[range_key_name]]
158
190
 
159
- if !hash[id] || (result[:updated_at] > hash[id][:updated_at])
160
- result[:id] = id
161
- hash[id] = result
191
+ if test_record.nil? || ((record[range_key_name] == test_record[range_key_name]) && (record[:updated_at] > test_record[:updated_at]))
192
+ #get ride of our partition and put it in the array with the range key
193
+ record[:id], partition = get_original_id_and_partition record[:id]
194
+ final_hash[record[range_key_name]] = record
162
195
  end
163
196
  end
164
- end.values
197
+
198
+ return final_hash.values
199
+ else
200
+ {}.tap do |hash|
201
+ Array(results).each do |result|
202
+ next if result.nil?
203
+ #Need to find the value of id with out the . and partition number
204
+ id, partition = get_original_id_and_partition result[:id]
205
+
206
+ if !hash[id] || (result[:updated_at] > hash[id][:updated_at])
207
+ result[:id] = id
208
+ hash[id] = result
209
+ end
210
+ end
211
+ end.values
212
+ end
165
213
  end
166
214
 
167
215
  # Delegate all methods that aren't defind here to the underlying adapter.
@@ -171,7 +219,47 @@ module Dynamoid
171
219
  return benchmark(method, *args) {adapter.send(method, *args, &block)} if @adapter.respond_to?(method)
172
220
  super
173
221
  end
222
+
223
+ # Query the DynamoDB table. This employs DynamoDB's indexes so is generally faster than scanning, but is
224
+ # only really useful for range queries, since it can only find by one hash key at once. Only provide
225
+ # one range key to the hash. If paritioning is on, will run a query for every parition and join the results
226
+ #
227
+ # @param [String] table_name the name of the table
228
+ # @param [Hash] opts the options to query the table with
229
+ # @option opts [String] :hash_value the value of the hash key to find
230
+ # @option opts [Range] :range_value find the range key within this range
231
+ # @option opts [Number] :range_greater_than find range keys greater than this
232
+ # @option opts [Number] :range_less_than find range keys less than this
233
+ # @option opts [Number] :range_gte find range keys greater than or equal to this
234
+ # @option opts [Number] :range_lte find range keys less than or equal to this
235
+ #
236
+ # @return [Array] an array of all matching items
237
+ #
238
+ def query(table_name, opts = {})
239
+
240
+ unless Dynamoid::Config.partitioning?
241
+ #no paritioning? just pass to the standard query method
242
+ Dynamoid::Adapter::AwsSdk.query(table_name, opts)
243
+ else
244
+ #get all the hash_values that could be possible
245
+ ids = id_with_partitions(opts[:hash_value])
174
246
 
175
- end
247
+ #lets not overwrite with the original options
248
+ modified_options = opts.clone
249
+ results = []
250
+
251
+ #loop and query on each of the partition ids
252
+ ids.each do |id|
253
+ modified_options[:hash_value] = id
254
+
255
+ query_result = Dynamoid::Adapter::AwsSdk.query(table_name, modified_options)
256
+ query_result = [query_result] if !query_result.is_a?(Array)
176
257
 
258
+ results = results + query_result unless query_result.nil?
259
+ end
260
+
261
+ result_for_partition results, table_name
262
+ end
263
+ end
264
+ end
177
265
  end
@@ -54,6 +54,29 @@ module Dynamoid
54
54
  end
55
55
  hash
56
56
  end
57
+
58
+ # Delete many items at once from DynamoDB. More efficient than delete each item individually.
59
+ #
60
+ # @example Delete IDs 1 and 2 from the table testtable
61
+ # Dynamoid::Adapter::AwsSdk.batch_delete_item('table1' => ['1', '2'])
62
+ #or
63
+ # Dynamoid::Adapter::AwsSdk.batch_delete_item('table1' => [['hk1', 'rk2'], ['hk1', 'rk2']]]))
64
+ #
65
+ # @param [Hash] options the hash of tables and IDs to delete
66
+ #
67
+ # @return nil
68
+ #
69
+ def batch_delete_item(options)
70
+ return nil if options.all?{|k, v| v.empty?}
71
+ options.each do |t, ids|
72
+ Array(ids).in_groups_of(25, false) do |group|
73
+ batch = AWS::DynamoDB::BatchWrite.new(:config => @@connection.config)
74
+ batch.delete(t,group)
75
+ batch.process!
76
+ end
77
+ end
78
+ nil
79
+ end
57
80
 
58
81
  # Create a table on DynamoDB. This usually takes a long time to complete.
59
82
  #
@@ -111,9 +134,6 @@ module Dynamoid
111
134
  # @return [Hash] a hash representing the raw item in DynamoDB
112
135
  #
113
136
  # @since 0.2.0
114
-
115
-
116
-
117
137
  def get_item(table_name, key, options = {})
118
138
  range_key = options.delete(:range_key)
119
139
  table = get_table(table_name)
@@ -46,6 +46,63 @@ module Dynamoid #:nodoc:
46
46
  def all
47
47
  records
48
48
  end
49
+
50
+ # Destroys all the records matching the criteria.
51
+ #
52
+ def destroy_all
53
+ ids = []
54
+
55
+ if range?
56
+ ranges = []
57
+ Dynamoid::Adapter.query(source.table_name, range_query).collect do |hash|
58
+ ids << hash[source.hash_key.to_sym]
59
+ ranges << hash[source.range_key.to_sym]
60
+ end
61
+
62
+ Dynamoid::Adapter.delete(source.table_name, ids,{:range_key => ranges})
63
+ elsif index
64
+ #TODO: test this throughly and find a way to delete all index table records for one source record
65
+ if index.range_key?
66
+ results = Dynamoid::Adapter.query(index.table_name, index_query.merge(consistent_opts))
67
+ else
68
+ results = Dynamoid::Adapter.read(index.table_name, index_query[:hash_value], consistent_opts)
69
+ end
70
+
71
+ results.collect do |hash|
72
+ ids << hash[source.hash_key.to_sym]
73
+ index_ranges << hash[source.range_key.to_sym]
74
+ end
75
+
76
+ unless ids.nil? || ids.empty?
77
+ ids = ids.to_a
78
+
79
+ if @start
80
+ ids = ids.drop_while { |id| id != @start.hash_key }.drop(1)
81
+ index_ranges = index_ranges.drop_while { |range| range != @start.hash_key }.drop(1) unless index_ranges.nil?
82
+ end
83
+
84
+ if @limit
85
+ ids = ids.take(@limit)
86
+ index_ranges = index_ranges.take(@limit)
87
+ end
88
+
89
+ Dynamoid::Adapter.delete(source.table_name, ids)
90
+
91
+ if index.range_key?
92
+ Dynamoid::Adapter.delete(index.table_name, ids,{:range_key => index_ranges})
93
+ else
94
+ Dynamoid::Adapter.delete(index.table_name, ids)
95
+ end
96
+
97
+ end
98
+ else
99
+ Dynamoid::Adapter.scan(source.table_name, query, scan_opts).collect do |hash|
100
+ ids << hash[source.hash_key.to_sym]
101
+ end
102
+
103
+ Dynamoid::Adapter.delete(source.table_name, ids)
104
+ end
105
+ end
49
106
 
50
107
  # Returns the first record matching the criteria.
51
108
  #
@@ -151,7 +208,7 @@ module Dynamoid #:nodoc:
151
208
  raise Dynamoid::Errors::InvalidQuery, 'Consistent read is not supported by SCAN operation'
152
209
  end
153
210
 
154
- Dynamoid::Adapter.scan(source.table_name, query, query_opts).collect {|hash| source.from_database(hash) }
211
+ Dynamoid::Adapter.scan(source.table_name, query, scan_opts).collect {|hash| source.from_database(hash) }
155
212
  end
156
213
 
157
214
  # Format the provided query so that it can be used to query results from DynamoDB.
@@ -236,6 +293,13 @@ module Dynamoid #:nodoc:
236
293
  opts[:scan_index_forward] = @scan_index_forward
237
294
  opts
238
295
  end
296
+
297
+ def scan_opts
298
+ opts = {}
299
+ opts[:limit] = @limit if @limit
300
+ opts[:next_token] = start_key if @start
301
+ opts
302
+ end
239
303
  end
240
304
 
241
305
  end
@@ -95,6 +95,8 @@ module Dynamoid #:nodoc:
95
95
  end
96
96
  end
97
97
  end
98
+
99
+ save
98
100
  end
99
101
  end
100
102
 
@@ -16,7 +16,7 @@ describe Dynamoid::Adapter::AwsSdk do
16
16
  end
17
17
  end
18
18
 
19
- context 'with a preexisting table' do
19
+ context 'with a preexisting table without paritioning' do
20
20
  before(:all) do
21
21
  Dynamoid::Adapter.create_table('dynamoid_tests_TestTable1', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable1')
22
22
  Dynamoid::Adapter.create_table('dynamoid_tests_TestTable2', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable2')
@@ -99,6 +99,56 @@ describe Dynamoid::Adapter::AwsSdk do
99
99
  results['dynamoid_tests_TestTable3'].should include({:name => 'Josh', :id => '1', :range => 1.0})
100
100
  results['dynamoid_tests_TestTable3'].should include({:name => 'Justin', :id => '2', :range => 2.0})
101
101
  end
102
+
103
+ # BatchDeleteItem
104
+ it "performs BatchDeleteItem with singular keys" do
105
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
106
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable2', {:id => '1', :name => 'Justin'})
107
+
108
+ Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable1' => ['1'], 'dynamoid_tests_TestTable2' => ['1'])
109
+
110
+ results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable1' => '1', 'dynamoid_tests_TestTable2' => '1')
111
+ results.size.should == 0
112
+
113
+ results['dynamoid_tests_TestTable1'].should_not include({:name => 'Josh', :id => '1'})
114
+ results['dynamoid_tests_TestTable2'].should_not include({:name => 'Justin', :id => '1'})
115
+ end
116
+
117
+ it "performs BatchDeleteItem with multiple keys" do
118
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1', :name => 'Josh'})
119
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2', :name => 'Justin'})
120
+
121
+ Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable1' => ['1', '2'])
122
+
123
+ results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable1' => ['1', '2'])
124
+ results.size.should == 0
125
+
126
+ results['dynamoid_tests_TestTable1'].should_not include({:name => 'Josh', :id => '1'})
127
+ results['dynamoid_tests_TestTable1'].should_not include({:name => 'Justin', :id => '2'})
128
+ end
129
+
130
+ it 'performs BatchDeleteItem with one ranged key' do
131
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 1.0})
132
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '2', :name => 'Justin', :range => 2.0})
133
+
134
+ Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable3' => [['1', 1.0]])
135
+ results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable3' => [['1', 1.0]])
136
+ results.size.should == 0
137
+
138
+ results['dynamoid_tests_TestTable3'].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
139
+ end
140
+
141
+ it 'performs BatchDeleteItem with multiple ranged keys' do
142
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1', :name => 'Josh', :range => 1.0})
143
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '2', :name => 'Justin', :range => 2.0})
144
+
145
+ Dynamoid::Adapter.batch_delete_item('dynamoid_tests_TestTable3' => [['1', 1.0],['2', 2.0]])
146
+ results = Dynamoid::Adapter.batch_get_item('dynamoid_tests_TestTable3' => [['1', 1.0],['2', 2.0]])
147
+ results.size.should == 0
148
+
149
+ results['dynamoid_tests_TestTable3'].should_not include({:name => 'Josh', :id => '1', :range => 1.0})
150
+ results['dynamoid_tests_TestTable3'].should_not include({:name => 'Justin', :id => '2', :range => 2.0})
151
+ end
102
152
 
103
153
  # ListTables
104
154
  it 'performs ListTables' do
@@ -175,6 +225,90 @@ describe Dynamoid::Adapter::AwsSdk do
175
225
  Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
176
226
  end
177
227
  end
228
+
229
+ context 'with a preexisting table with paritioning' do
230
+ before(:all) do
231
+ @previous_value = Dynamoid::Config.partitioning
232
+ Dynamoid::Config.partitioning = true
233
+
234
+ Dynamoid::Adapter.create_table('dynamoid_tests_TestTable1', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable1')
235
+ Dynamoid::Adapter.create_table('dynamoid_tests_TestTable2', :id) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable2')
236
+ Dynamoid::Adapter.create_table('dynamoid_tests_TestTable3', :id, :range_key => { :range => :number }) unless Dynamoid::Adapter.list_tables.include?('dynamoid_tests_TestTable3')
237
+ end
238
+
239
+ after(:all) do
240
+ Dynamoid::Config.partitioning = @previous_value
241
+ end
242
+
243
+ # Query
244
+ it 'performs query on a table and returns items' do
245
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
246
+
247
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable1', :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
248
+ end
249
+
250
+ it 'performs query on a table and returns items if there are multiple items' do
251
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
252
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Justin'})
253
+
254
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable1', :hash_value => '1').first.should == { :id=> '1', :name=>"Josh" }
255
+ end
256
+
257
+ context 'range queries' do
258
+ before do
259
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1.1', :range => 1.0})
260
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable3', {:id => '1.1', :range => 3.0})
261
+ end
262
+
263
+ it 'performs query on a table with a range and selects items in a range' do
264
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_value => 0.0..3.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
265
+ end
266
+
267
+ it 'performs query on a table with a range and selects items greater than' do
268
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_greater_than => 1.0).should =~ [{:id => '1', :range => BigDecimal.new(3)}]
269
+ end
270
+
271
+ it 'performs query on a table with a range and selects items less than' do
272
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_less_than => 2.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}]
273
+ end
274
+
275
+ it 'performs query on a table with a range and selects items gte' do
276
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_gte => 1.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
277
+ end
278
+
279
+ it 'performs query on a table with a range and selects items lte' do
280
+ Dynamoid::Adapter.query('dynamoid_tests_TestTable3', :hash_value => '1', :range_lte => 3.0).should =~ [{:id => '1', :range => BigDecimal.new(1)}, {:id => '1', :range => BigDecimal.new(3)}]
281
+ end
282
+ end
283
+
284
+ # Scan
285
+ it 'performs scan on a table and returns items' do
286
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
287
+
288
+ Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
289
+ end
290
+
291
+ it 'performs scan on a table and returns items if there are multiple items but only one match' do
292
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
293
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Justin'})
294
+
295
+ Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should == [{ :id=> '1', :name=>"Josh" }]
296
+ end
297
+
298
+ it 'performs scan on a table and returns multiple items if there are multiple matches' do
299
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
300
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Josh'})
301
+
302
+ Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', :name => 'Josh').should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
303
+ end
304
+
305
+ it 'performs scan on a table and returns all items if no criteria are specified' do
306
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '1.1', :name => 'Josh'})
307
+ Dynamoid::Adapter.put_item('dynamoid_tests_TestTable1', {:id => '2.1', :name => 'Josh'})
308
+
309
+ Dynamoid::Adapter.scan('dynamoid_tests_TestTable1', {}).should include({:name=>"Josh", :id=>"2"}, {:name=>"Josh", :id=>"1"})
310
+ end
311
+ end
178
312
 
179
313
  # DescribeTable
180
314
 
@@ -42,6 +42,18 @@ describe "Dynamoid::Adapter" do
42
42
  Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'])
43
43
  end
44
44
 
45
+ it 'delete through the adapter for one ID' do
46
+ Dynamoid::Adapter.expects(:delete_item).with('dynamoid_tests_TestTable', '123', {}).returns(nil)
47
+
48
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123')
49
+ end
50
+
51
+ it 'deletes through the adapter for many IDs' do
52
+ Dynamoid::Adapter.expects(:batch_delete_item).with({'dynamoid_tests_TestTable' => ['1', '2']}).returns(nil)
53
+
54
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'])
55
+ end
56
+
45
57
  it 'reads through the adapter for one ID and a range key' do
46
58
  Dynamoid::Adapter.expects(:get_item).with('dynamoid_tests_TestTable', '123', :range_key => 2.0).returns(true)
47
59
 
@@ -53,6 +65,18 @@ describe "Dynamoid::Adapter" do
53
65
 
54
66
  Dynamoid::Adapter.read('dynamoid_tests_TestTable', ['1', '2'], :range_key => 2.0)
55
67
  end
68
+
69
+ it 'deletes through the adapter for one ID and a range key' do
70
+ Dynamoid::Adapter.expects(:delete_item).with('dynamoid_tests_TestTable', '123', :range_key => 2.0).returns(nil)
71
+
72
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123', :range_key => 2.0)
73
+ end
74
+
75
+ it 'deletes through the adapter for many IDs and a range key' do
76
+ Dynamoid::Adapter.expects(:batch_delete_item).with({'dynamoid_tests_TestTable' => [['1', 2.0], ['2', 2.0]]}).returns(nil)
77
+
78
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'], :range_key => [2.0,2.0])
79
+ end
56
80
  end
57
81
 
58
82
  context 'with partitioning' do
@@ -109,9 +133,38 @@ describe "Dynamoid::Adapter" do
109
133
  @time = DateTime.now
110
134
  @array =[{:id => '1.0', :updated_at => @time - 6.hours}, {:id => '1.1', :updated_at => @time - 3.hours}, {:id => '1.2', :updated_at => @time - 1.hour}, {:id => '1.3', :updated_at => @time - 6.hours}, {:id => '2.0', :updated_at => @time}]
111
135
 
112
- Dynamoid::Adapter.result_for_partition(@array).should =~ [{:id => '1', :updated_at => @time - 1.hour}, {:id => '2', :updated_at => @time}]
136
+ Dynamoid::Adapter.result_for_partition(@array,"dynamoid_tests_TestTable").should =~ [{:id => '1', :updated_at => @time - 1.hour}, {:id => '2', :updated_at => @time}]
113
137
  end
114
138
 
139
+ it 'returns a valid original id and partition number' do
140
+ @id = "12345.387327.-sdf3"
141
+ @partition_number = "4"
142
+ Dynamoid::Adapter.get_original_id_and_partition("#{@id}.#{@partition_number}").should == [@id, @partition_number]
143
+ end
144
+
145
+ it 'delete through the adapter for one ID' do
146
+ Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "123.#{n}"}).returns(nil)
147
+
148
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123')
149
+ end
150
+
151
+ it 'deletes through the adapter for many IDs' do
152
+ Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| "1.#{n}"} + (0...Dynamoid::Config.partition_size).collect{|n| "2.#{n}"}).returns(nil)
153
+
154
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'])
155
+ end
156
+
157
+ it 'deletes through the adapter for one ID and a range key' do
158
+ Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["123.#{n}", 2.0]}).returns(nil)
159
+
160
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', '123', :range_key => 2.0)
161
+ end
162
+
163
+ it 'deletes through the adapter for many IDs and a range key' do
164
+ Dynamoid::Adapter.expects(:batch_delete_item).with('dynamoid_tests_TestTable' => (0...Dynamoid::Config.partition_size).collect{|n| ["1.#{n}", 2.0]} + (0...Dynamoid::Config.partition_size).collect{|n| ["2.#{n}", 2.0]}).returns(nil)
165
+
166
+ Dynamoid::Adapter.delete('dynamoid_tests_TestTable', ['1', '2'], :range_key => [2.0,2.0])
167
+ end
115
168
  end
116
169
 
117
170
  end
@@ -136,5 +136,29 @@ describe "Dynamoid::Associations::Chain" do
136
136
  @chain.send(:records_with_range).should == [@tweet3]
137
137
  end
138
138
  end
139
+
140
+ context 'destroy alls' do
141
+ before do
142
+ @tweet1 = Tweet.create(:tweet_id => "x", :group => "one")
143
+ @tweet2 = Tweet.create(:tweet_id => "x", :group => "two")
144
+ @tweet3 = Tweet.create(:tweet_id => "xx", :group => "two")
145
+ @chain = Dynamoid::Criteria::Chain.new(Tweet)
146
+ end
147
+
148
+ it 'destroys tweet with a range simple range query' do
149
+ @chain.query = { :tweet_id => "x" }
150
+ @chain.all.size.should == 2
151
+ @chain.destroy_all
152
+ @chain.consistent.all.size.should == 0
153
+ end
154
+
155
+ it 'deletes one specific tweet with range' do
156
+ @chain = Dynamoid::Criteria::Chain.new(Tweet)
157
+ @chain.query = { :tweet_id => "xx", :group => "two" }
158
+ @chain.all.size.should == 1
159
+ @chain.destroy_all
160
+ @chain.consistent.all.size.should == 0
161
+ end
162
+ end
139
163
 
140
164
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adept_dynamoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0.8
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-28 00:00:00.000000000 Z
12
+ date: 2012-12-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -240,6 +240,7 @@ files:
240
240
  - README.markdown
241
241
  - Rakefile
242
242
  - VERSION
243
+ - adept_dynamoid.gemspec
243
244
  - doc/.nojekyll
244
245
  - doc/Dynamoid.html
245
246
  - doc/Dynamoid/Adapter.html
@@ -362,6 +363,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
362
363
  - - ! '>='
363
364
  - !ruby/object:Gem::Version
364
365
  version: '0'
366
+ segments:
367
+ - 0
368
+ hash: 108672444036973826
365
369
  required_rubygems_version: !ruby/object:Gem::Requirement
366
370
  none: false
367
371
  requirements:
@@ -375,4 +379,3 @@ signing_key:
375
379
  specification_version: 3
376
380
  summary: Dynamoid is an ORM for Amazon's DynamoDB
377
381
  test_files: []
378
- has_rdoc: