adept_dynamoid 0.5.0.8 → 0.6.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.
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: