dynamoid 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/.travis.yml +4 -0
  2. data/Gemfile +3 -2
  3. data/Gemfile.lock +40 -45
  4. data/README.markdown +55 -25
  5. data/Rakefile +31 -0
  6. data/VERSION +1 -1
  7. data/doc/Dynamoid.html +58 -42
  8. data/doc/Dynamoid/Adapter.html +666 -179
  9. data/doc/Dynamoid/Adapter/AwsSdk.html +752 -236
  10. data/doc/Dynamoid/Associations.html +28 -21
  11. data/doc/Dynamoid/Associations/Association.html +102 -49
  12. data/doc/Dynamoid/Associations/BelongsTo.html +28 -25
  13. data/doc/Dynamoid/Associations/ClassMethods.html +95 -52
  14. data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +28 -25
  15. data/doc/Dynamoid/Associations/HasMany.html +28 -25
  16. data/doc/Dynamoid/Associations/HasOne.html +28 -25
  17. data/doc/Dynamoid/Associations/ManyAssociation.html +138 -94
  18. data/doc/Dynamoid/Associations/SingleAssociation.html +67 -38
  19. data/doc/Dynamoid/Components.html +60 -22
  20. data/doc/Dynamoid/Config.html +61 -44
  21. data/doc/Dynamoid/Config/Options.html +90 -61
  22. data/doc/Dynamoid/Criteria.html +28 -21
  23. data/doc/Dynamoid/Criteria/Chain.html +508 -100
  24. data/doc/Dynamoid/Criteria/ClassMethods.html +26 -19
  25. data/doc/Dynamoid/Dirty.html +424 -0
  26. data/doc/Dynamoid/Dirty/ClassMethods.html +174 -0
  27. data/doc/Dynamoid/Document.html +451 -84
  28. data/doc/Dynamoid/Document/ClassMethods.html +281 -102
  29. data/doc/Dynamoid/Errors.html +29 -22
  30. data/doc/Dynamoid/Errors/ConditionalCheckFailedException.html +141 -0
  31. data/doc/Dynamoid/Errors/DocumentNotValid.html +36 -25
  32. data/doc/Dynamoid/Errors/Error.html +27 -20
  33. data/doc/Dynamoid/Errors/InvalidField.html +27 -19
  34. data/doc/Dynamoid/Errors/InvalidQuery.html +131 -0
  35. data/doc/Dynamoid/Errors/MissingRangeKey.html +27 -19
  36. data/doc/Dynamoid/Fields.html +94 -77
  37. data/doc/Dynamoid/Fields/ClassMethods.html +166 -37
  38. data/doc/Dynamoid/Finders.html +28 -21
  39. data/doc/Dynamoid/Finders/ClassMethods.html +505 -78
  40. data/doc/Dynamoid/IdentityMap.html +492 -0
  41. data/doc/Dynamoid/IdentityMap/ClassMethods.html +534 -0
  42. data/doc/Dynamoid/Indexes.html +41 -28
  43. data/doc/Dynamoid/Indexes/ClassMethods.html +45 -29
  44. data/doc/Dynamoid/Indexes/Index.html +100 -62
  45. data/doc/Dynamoid/Middleware.html +115 -0
  46. data/doc/Dynamoid/Middleware/IdentityMap.html +264 -0
  47. data/doc/Dynamoid/Persistence.html +326 -85
  48. data/doc/Dynamoid/Persistence/ClassMethods.html +275 -109
  49. data/doc/Dynamoid/Validations.html +47 -31
  50. data/doc/_index.html +116 -71
  51. data/doc/class_list.html +13 -7
  52. data/doc/css/full_list.css +4 -2
  53. data/doc/css/style.css +60 -44
  54. data/doc/file.LICENSE.html +26 -19
  55. data/doc/file.README.html +152 -48
  56. data/doc/file_list.html +14 -8
  57. data/doc/frames.html +20 -5
  58. data/doc/index.html +152 -48
  59. data/doc/js/app.js +52 -43
  60. data/doc/js/full_list.js +14 -9
  61. data/doc/js/jquery.js +4 -16
  62. data/doc/method_list.html +446 -540
  63. data/doc/top-level-namespace.html +27 -20
  64. data/{Dynamoid.gemspec → dynamoid.gemspec} +21 -8
  65. data/lib/dynamoid/adapter.rb +11 -10
  66. data/lib/dynamoid/adapter/aws_sdk.rb +40 -19
  67. data/lib/dynamoid/components.rb +2 -1
  68. data/lib/dynamoid/criteria/chain.rb +29 -11
  69. data/lib/dynamoid/dirty.rb +6 -0
  70. data/lib/dynamoid/document.rb +34 -19
  71. data/lib/dynamoid/fields.rb +36 -30
  72. data/lib/dynamoid/finders.rb +7 -5
  73. data/lib/dynamoid/persistence.rb +37 -10
  74. data/spec/app/models/address.rb +2 -0
  75. data/spec/app/models/camel_case.rb +10 -0
  76. data/spec/app/models/car.rb +6 -0
  77. data/spec/app/models/nuclear_submarine.rb +5 -0
  78. data/spec/app/models/subscription.rb +2 -2
  79. data/spec/app/models/vehicle.rb +7 -0
  80. data/spec/dynamoid/adapter/aws_sdk_spec.rb +20 -11
  81. data/spec/dynamoid/adapter_spec.rb +67 -82
  82. data/spec/dynamoid/associations/association_spec.rb +30 -30
  83. data/spec/dynamoid/criteria/chain_spec.rb +56 -9
  84. data/spec/dynamoid/criteria_spec.rb +3 -0
  85. data/spec/dynamoid/dirty_spec.rb +8 -0
  86. data/spec/dynamoid/document_spec.rb +109 -47
  87. data/spec/dynamoid/fields_spec.rb +32 -3
  88. data/spec/dynamoid/finders_spec.rb +12 -0
  89. data/spec/dynamoid/persistence_spec.rb +73 -8
  90. data/spec/spec_helper.rb +1 -0
  91. data/spec/support/with_partitioning.rb +15 -0
  92. metadata +22 -9
@@ -6,19 +6,21 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.7.5
9
+ &mdash; Documentation by YARD 0.8.6.1
10
10
 
11
11
  </title>
12
12
 
13
- <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
14
 
15
- <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
16
 
17
17
  <script type="text/javascript" charset="utf-8">
18
+ hasFrames = window.top.frames.main ? true : false;
18
19
  relpath = '';
19
- if (relpath != '') relpath += '/';
20
+ framesUrl = "frames.html#!" + escape(window.location.href);
20
21
  </script>
21
22
 
23
+
22
24
  <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
23
25
 
24
26
  <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
@@ -26,36 +28,41 @@
26
28
 
27
29
  </head>
28
30
  <body>
29
- <script type="text/javascript" charset="utf-8">
30
- if (window.top.frames.main) document.body.className = 'frames';
31
- </script>
32
-
33
31
  <div id="header">
34
32
  <div id="menu">
35
33
 
36
- <a href="_index.html">Index</a> &raquo;
34
+ <a href="_index.html">Index</a> &raquo;
37
35
 
38
36
 
39
37
  <span class="title">Top Level Namespace</span>
40
38
 
41
-
39
+
42
40
  <div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
43
41
  </div>
44
42
 
45
43
  <div id="search">
46
44
 
47
- <a id="class_list_link" href="#">Class List</a>
45
+ <a class="full_list_link" id="class_list_link"
46
+ href="class_list.html">
47
+ Class List
48
+ </a>
48
49
 
49
- <a id="method_list_link" href="#">Method List</a>
50
+ <a class="full_list_link" id="method_list_link"
51
+ href="method_list.html">
52
+ Method List
53
+ </a>
50
54
 
51
- <a id="file_list_link" href="#">File List</a>
55
+ <a class="full_list_link" id="file_list_link"
56
+ href="file_list.html">
57
+ File List
58
+ </a>
52
59
 
53
60
  </div>
54
61
  <div class="clear"></div>
55
62
  </div>
56
-
63
+
57
64
  <iframe id="search_frame"></iframe>
58
-
65
+
59
66
  <div id="content"><h1>Top Level Namespace
60
67
 
61
68
 
@@ -76,11 +83,11 @@
76
83
 
77
84
  <h2>Defined Under Namespace</h2>
78
85
  <p class="children">
79
-
86
+
80
87
 
81
88
  <strong class="modules">Modules:</strong> <span class='object_link'><a href="Dynamoid.html" title="Dynamoid (module)">Dynamoid</a></span>
82
89
 
83
-
90
+
84
91
 
85
92
 
86
93
  </p>
@@ -94,11 +101,11 @@
94
101
 
95
102
 
96
103
  </div>
97
-
104
+
98
105
  <div id="footer">
99
- Generated on Thu Apr 26 01:26:26 2012 by
106
+ Generated on Thu Jun 27 21:59:09 2013 by
100
107
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
101
- 0.7.5 (ruby-1.9.3).
108
+ 0.8.6.1 (ruby-1.9.3).
102
109
  </div>
103
110
 
104
111
  </body>
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "dynamoid"
8
- s.version = "0.6.1"
8
+ s.version = "0.7.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Josh Symonds"]
12
- s.date = "2013-01-09"
12
+ s.date = "2013-06-28"
13
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."
14
14
  s.email = "josh@joshsymonds.com"
15
15
  s.extra_rdoc_files = [
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".rspec",
22
- "Dynamoid.gemspec",
22
+ ".travis.yml",
23
23
  "Gemfile",
24
24
  "Gemfile.lock",
25
25
  "LICENSE.txt",
@@ -46,20 +46,28 @@ Gem::Specification.new do |s|
46
46
  "doc/Dynamoid/Criteria.html",
47
47
  "doc/Dynamoid/Criteria/Chain.html",
48
48
  "doc/Dynamoid/Criteria/ClassMethods.html",
49
+ "doc/Dynamoid/Dirty.html",
50
+ "doc/Dynamoid/Dirty/ClassMethods.html",
49
51
  "doc/Dynamoid/Document.html",
50
52
  "doc/Dynamoid/Document/ClassMethods.html",
51
53
  "doc/Dynamoid/Errors.html",
54
+ "doc/Dynamoid/Errors/ConditionalCheckFailedException.html",
52
55
  "doc/Dynamoid/Errors/DocumentNotValid.html",
53
56
  "doc/Dynamoid/Errors/Error.html",
54
57
  "doc/Dynamoid/Errors/InvalidField.html",
58
+ "doc/Dynamoid/Errors/InvalidQuery.html",
55
59
  "doc/Dynamoid/Errors/MissingRangeKey.html",
56
60
  "doc/Dynamoid/Fields.html",
57
61
  "doc/Dynamoid/Fields/ClassMethods.html",
58
62
  "doc/Dynamoid/Finders.html",
59
63
  "doc/Dynamoid/Finders/ClassMethods.html",
64
+ "doc/Dynamoid/IdentityMap.html",
65
+ "doc/Dynamoid/IdentityMap/ClassMethods.html",
60
66
  "doc/Dynamoid/Indexes.html",
61
67
  "doc/Dynamoid/Indexes/ClassMethods.html",
62
68
  "doc/Dynamoid/Indexes/Index.html",
69
+ "doc/Dynamoid/Middleware.html",
70
+ "doc/Dynamoid/Middleware/IdentityMap.html",
63
71
  "doc/Dynamoid/Persistence.html",
64
72
  "doc/Dynamoid/Persistence/ClassMethods.html",
65
73
  "doc/Dynamoid/Validations.html",
@@ -78,6 +86,7 @@ Gem::Specification.new do |s|
78
86
  "doc/js/jquery.js",
79
87
  "doc/method_list.html",
80
88
  "doc/top-level-namespace.html",
89
+ "dynamoid.gemspec",
81
90
  "lib/dynamoid.rb",
82
91
  "lib/dynamoid/adapter.rb",
83
92
  "lib/dynamoid/adapter/aws_sdk.rb",
@@ -107,12 +116,15 @@ Gem::Specification.new do |s|
107
116
  "lib/dynamoid/validations.rb",
108
117
  "spec/app/models/address.rb",
109
118
  "spec/app/models/camel_case.rb",
119
+ "spec/app/models/car.rb",
110
120
  "spec/app/models/magazine.rb",
111
121
  "spec/app/models/message.rb",
122
+ "spec/app/models/nuclear_submarine.rb",
112
123
  "spec/app/models/sponsor.rb",
113
124
  "spec/app/models/subscription.rb",
114
125
  "spec/app/models/tweet.rb",
115
126
  "spec/app/models/user.rb",
127
+ "spec/app/models/vehicle.rb",
116
128
  "spec/dynamoid/adapter/aws_sdk_spec.rb",
117
129
  "spec/dynamoid/adapter_spec.rb",
118
130
  "spec/dynamoid/associations/association_spec.rb",
@@ -134,12 +146,13 @@ Gem::Specification.new do |s|
134
146
  "spec/dynamoid/persistence_spec.rb",
135
147
  "spec/dynamoid/validations_spec.rb",
136
148
  "spec/dynamoid_spec.rb",
137
- "spec/spec_helper.rb"
149
+ "spec/spec_helper.rb",
150
+ "spec/support/with_partitioning.rb"
138
151
  ]
139
152
  s.homepage = "http://github.com/Veraticus/Dynamoid"
140
153
  s.licenses = ["MIT"]
141
154
  s.require_paths = ["lib"]
142
- s.rubygems_version = "1.8.23"
155
+ s.rubygems_version = "1.8.25"
143
156
  s.summary = "Dynamoid is an ORM for Amazon's DynamoDB"
144
157
 
145
158
  if s.respond_to? :specification_version then
@@ -157,7 +170,7 @@ Gem::Specification.new do |s|
157
170
  s.add_development_dependency(%q<redcarpet>, ["= 1.17.2"])
158
171
  s.add_development_dependency(%q<github-markup>, [">= 0"])
159
172
  s.add_development_dependency(%q<pry>, [">= 0"])
160
- s.add_development_dependency(%q<fake_dynamo>, [">= 0"])
173
+ s.add_development_dependency(%q<fake_dynamo>, ["~> 0.1.3"])
161
174
  s.add_development_dependency(%q<mocha>, ["= 0.10.0"])
162
175
  else
163
176
  s.add_dependency(%q<activemodel>, [">= 0"])
@@ -171,7 +184,7 @@ Gem::Specification.new do |s|
171
184
  s.add_dependency(%q<redcarpet>, ["= 1.17.2"])
172
185
  s.add_dependency(%q<github-markup>, [">= 0"])
173
186
  s.add_dependency(%q<pry>, [">= 0"])
174
- s.add_dependency(%q<fake_dynamo>, [">= 0"])
187
+ s.add_dependency(%q<fake_dynamo>, ["~> 0.1.3"])
175
188
  s.add_dependency(%q<mocha>, ["= 0.10.0"])
176
189
  end
177
190
  else
@@ -186,7 +199,7 @@ Gem::Specification.new do |s|
186
199
  s.add_dependency(%q<redcarpet>, ["= 1.17.2"])
187
200
  s.add_dependency(%q<github-markup>, [">= 0"])
188
201
  s.add_dependency(%q<pry>, [">= 0"])
189
- s.add_dependency(%q<fake_dynamo>, [">= 0"])
202
+ s.add_dependency(%q<fake_dynamo>, ["~> 0.1.3"])
190
203
  s.add_dependency(%q<mocha>, ["= 0.10.0"])
191
204
  end
192
205
  end
@@ -65,25 +65,28 @@ module Dynamoid
65
65
  #
66
66
  # @param [String] table the name of the table to write the object to
67
67
  # @param [Array] ids to fetch, can also be a string of just one id
68
- # @param [Number] range_key the range key of the record
68
+ # @param [Hash] options: Passed to the underlying query. The :range_key option is required whenever the table has a range key,
69
+ # unless multiple ids are passed in and Dynamoid::Config.partitioning? is turned off.
69
70
  #
70
71
  # @since 0.2.0
71
72
  def read(table, ids, options = {})
72
- range_key = options[:range_key]
73
+ range_key = options.delete(:range_key)
74
+
73
75
  if ids.respond_to?(:each)
74
76
  ids = ids.collect{|id| range_key ? [id, range_key] : id}
75
77
  if Dynamoid::Config.partitioning?
76
- results = batch_get_item(table => id_with_partitions(ids))
78
+ results = batch_get_item({table => id_with_partitions(ids)}, options)
77
79
  {table => result_for_partition(results[table],table)}
78
80
  else
79
- batch_get_item(table => ids)
81
+ batch_get_item({table => ids}, options)
80
82
  end
81
83
  else
82
84
  if Dynamoid::Config.partitioning?
83
85
  ids = range_key ? [[ids, range_key]] : ids
84
- results = batch_get_item(table => id_with_partitions(ids))
86
+ results = batch_get_item({table => id_with_partitions(ids)}, options)
85
87
  result_for_partition(results[table],table).first
86
88
  else
89
+ options[:range_key] = range_key if range_key
87
90
  get_item(table, ids, options)
88
91
  end
89
92
  end
@@ -185,7 +188,7 @@ module Dynamoid
185
188
  range_key_name = table.range_key.name.to_sym
186
189
 
187
190
  final_hash = {}
188
-
191
+
189
192
  results.each do |record|
190
193
  test_record = final_hash[record[range_key_name]]
191
194
 
@@ -254,11 +257,9 @@ module Dynamoid
254
257
  modified_options[:hash_value] = id
255
258
 
256
259
  query_result = Dynamoid::Adapter::AwsSdk.query(table_name, modified_options)
257
- query_result = [query_result] if !query_result.is_a?(Array)
258
-
259
- results = results + query_result unless query_result.nil?
260
+ results += query_result.inject([]){|array, result| array += [result]} if query_result.any?
260
261
  end
261
-
262
+
262
263
  result_for_partition results, table_name
263
264
  end
264
265
  end
@@ -53,20 +53,21 @@ module Dynamoid
53
53
  # Get many items at once from DynamoDB. More efficient than getting each item individually.
54
54
  #
55
55
  # @example Retrieve IDs 1 and 2 from the table testtable
56
- # Dynamoid::Adapter::AwsSdk.batch_get_item('table1' => ['1', '2'])
56
+ # Dynamoid::Adapter::AwsSdk.batch_get_item({'table1' => ['1', '2']}, :consistent_read => true)
57
57
  #
58
- # @param [Hash] options the hash of tables and IDs to retrieve
58
+ # @param [Hash] table_ids the hash of tables and IDs to retrieve
59
+ # @param [Hash] options to be passed to underlying BatchGet call
59
60
  #
60
61
  # @return [Hash] a hash where keys are the table names and the values are the retrieved items
61
62
  #
62
63
  # @since 0.2.0
63
- def batch_get_item(options)
64
+ def batch_get_item(table_ids, options = {})
64
65
  hash = Hash.new{|h, k| h[k] = []}
65
- return hash if options.all?{|k, v| v.empty?}
66
- options.each do |t, ids|
66
+ return hash if table_ids.all?{|k, v| v.empty?}
67
+ table_ids.each do |t, ids|
67
68
  Array(ids).in_groups_of(100, false) do |group|
68
69
  batch = AWS::DynamoDB::BatchGet.new(:config => @@connection.config)
69
- batch.table(t, :all, Array(group)) unless group.nil? || group.empty?
70
+ batch.table(t, :all, Array(group), options) unless group.nil? || group.empty?
70
71
  batch.each do |table_name, attributes|
71
72
  hash[table_name] << attributes.symbolize_keys!
72
73
  end
@@ -196,6 +197,8 @@ module Dynamoid
196
197
  object.delete_if{|k, v| v.nil? || (v.respond_to?(:empty?) && v.empty?)},
197
198
  options || {}
198
199
  )
200
+ rescue AWS::DynamoDB::Errors::ConditionalCheckFailedException => e
201
+ raise Dynamoid::Errors::ConditionalCheckFailedException
199
202
  end
200
203
 
201
204
  # Query the DynamoDB table. This employs DynamoDB's indexes so is generally faster than scanning, but is
@@ -211,19 +214,25 @@ module Dynamoid
211
214
  # @option opts [Number] :range_gte find range keys greater than or equal to this
212
215
  # @option opts [Number] :range_lte find range keys less than or equal to this
213
216
  #
214
- # @return [Array] an array of all matching items
217
+ # @return [Enumerator] an iterator of all matching items
215
218
  #
216
219
  # @since 0.2.0
217
220
  def query(table_name, opts = {})
218
221
  table = get_table(table_name)
219
222
 
220
- consistent_opts = { :consistent_read => opts[:consistent_read] || false }
221
- if table.composite_key?
222
- results = []
223
- table.items.query(opts).each {|data| results << data.attributes.to_h(consistent_opts).symbolize_keys!}
224
- results
225
- else
226
- get_item(table_name, opts[:hash_value])
223
+ Enumerator.new do |yielder|
224
+ consistent_opts = { :consistent_read => opts[:consistent_read] || false }
225
+ if table.composite_key?
226
+ table.items.query(opts).each do |data|
227
+ if opts.has_key? :select
228
+ yielder.yield data.attributes.symbolize_keys!
229
+ else
230
+ yielder.yield data.attributes.to_h(consistent_opts).symbolize_keys!
231
+ end
232
+ end
233
+ else
234
+ yielder.yield get_item(table_name, opts[:hash_value])
235
+ end
227
236
  end
228
237
  end
229
238
 
@@ -233,16 +242,16 @@ module Dynamoid
233
242
  # @param [String] table_name the name of the table
234
243
  # @param [Hash] scan_hash a hash of attributes: matching records will be returned by the scan
235
244
  #
236
- # @return [Array] an array of all matching items
245
+ # @return [Enumerator] an iterator of all matching items
237
246
  #
238
247
  # @since 0.2.0
239
248
  def scan(table_name, scan_hash, select_opts)
240
249
  table = get_table(table_name)
241
- results = []
242
- table.items.where(scan_hash).select(select_opts) do |data|
243
- results << data.attributes.symbolize_keys!
250
+ Enumerator.new do |yielder|
251
+ table.items.where(scan_hash).select(select_opts).each do |data|
252
+ yielder.yield data.attributes.symbolize_keys!
253
+ end
244
254
  end
245
- results
246
255
  end
247
256
 
248
257
  # @todo Add an UpdateItem method.
@@ -261,6 +270,18 @@ module Dynamoid
261
270
  def table_cache
262
271
  @table_cache ||= {}
263
272
  end
273
+
274
+ # Number of items from a table
275
+ #
276
+ # @param [String] table_name the name of the table
277
+ #
278
+ # @return [Integer] the number of items from a table
279
+ #
280
+ # @since 0.6.1
281
+ def count(table_name)
282
+ table = get_table(table_name)
283
+ table.items.count
284
+ end
264
285
  end
265
286
  end
266
287
  end
@@ -10,10 +10,11 @@ module Dynamoid
10
10
  extend ActiveModel::Translation
11
11
  extend ActiveModel::Callbacks
12
12
 
13
- define_model_callbacks :create, :save, :destroy, :initialize
13
+ define_model_callbacks :create, :save, :destroy, :initialize, :update
14
14
 
15
15
  before_create :set_created_at
16
16
  before_save :set_updated_at
17
+ after_initialize :set_type
17
18
  end
18
19
 
19
20
  include ActiveModel::AttributeMethods
@@ -31,7 +31,7 @@ module Dynamoid #:nodoc:
31
31
  #
32
32
  # @since 0.2.0
33
33
  def where(args)
34
- args.each {|k, v| query[k] = v}
34
+ args.each {|k, v| query[k.to_sym] = v}
35
35
  self
36
36
  end
37
37
 
@@ -43,7 +43,8 @@ module Dynamoid #:nodoc:
43
43
  # Returns all the records matching the criteria.
44
44
  #
45
45
  # @since 0.2.0
46
- def all
46
+ def all(opts = {})
47
+ batch opts[:batch_size] if opts.has_key? :batch_size
47
48
  records
48
49
  end
49
50
 
@@ -116,6 +117,12 @@ module Dynamoid #:nodoc:
116
117
  records
117
118
  end
118
119
 
120
+ def batch(batch_size)
121
+ raise 'Cannot batch calls when using partitioning' if Dynamoid::Config.partitioning?
122
+ @batch_size = batch_size
123
+ self
124
+ end
125
+
119
126
  def start(start)
120
127
  @start = start
121
128
  self
@@ -141,28 +148,29 @@ module Dynamoid #:nodoc:
141
148
 
142
149
  # The actual records referenced by the association.
143
150
  #
144
- # @return [Array] an array of the found records.
151
+ # @return [Enumerator] an iterator of the found records.
145
152
  #
146
153
  # @since 0.2.0
147
154
  def records
148
- if range?
155
+ results = if range?
149
156
  records_with_range
150
157
  elsif index
151
158
  records_with_index
152
159
  else
153
160
  records_without_index
154
161
  end
162
+ @batch_size ? results : Array(results)
155
163
  end
156
164
 
157
165
  # If the query matches an index on the associated class, then this method will retrieve results from the index table.
158
166
  #
159
- # @return [Array] an array of the found records.
167
+ # @return [Enumerator] an iterator of the found records.
160
168
  #
161
169
  # @since 0.2.0
162
170
  def records_with_index
163
171
  ids = ids_from_index
164
172
  if ids.nil? || ids.empty?
165
- []
173
+ Enumerator.new []
166
174
  else
167
175
  ids = ids.to_a
168
176
 
@@ -171,7 +179,7 @@ module Dynamoid #:nodoc:
171
179
  end
172
180
 
173
181
  ids = ids.take(@limit) if @limit
174
- Array(source.find(ids, consistent_opts))
182
+ source.find(ids, consistent_opts)
175
183
  end
176
184
  end
177
185
 
@@ -190,12 +198,16 @@ module Dynamoid #:nodoc:
190
198
  end
191
199
 
192
200
  def records_with_range
193
- Dynamoid::Adapter.query(source.table_name, range_query).collect {|hash| source.from_database(hash) }
201
+ Enumerator.new do |yielder|
202
+ Dynamoid::Adapter.query(source.table_name, range_query).each do |hash|
203
+ yielder.yield source.from_database(hash)
204
+ end
205
+ end
194
206
  end
195
207
 
196
208
  # If the query does not match an index, we'll manually scan the associated table to find results.
197
209
  #
198
- # @return [Array] an array of the found records.
210
+ # @return [Enumerator] an iterator of the found records.
199
211
  #
200
212
  # @since 0.2.0
201
213
  def records_without_index
@@ -208,7 +220,11 @@ module Dynamoid #:nodoc:
208
220
  raise Dynamoid::Errors::InvalidQuery, 'Consistent read is not supported by SCAN operation'
209
221
  end
210
222
 
211
- Dynamoid::Adapter.scan(source.table_name, query, scan_opts).collect {|hash| source.from_database(hash) }
223
+ Enumerator.new do |yielder|
224
+ Dynamoid::Adapter.scan(source.table_name, query, scan_opts).each do |hash|
225
+ yielder.yield source.from_database(hash)
226
+ end
227
+ end
212
228
  end
213
229
 
214
230
  # Format the provided query so that it can be used to query results from DynamoDB.
@@ -271,8 +287,9 @@ module Dynamoid #:nodoc:
271
287
  query.keys.collect{|k| k.to_s.split('.').first}
272
288
  end
273
289
 
290
+ # Use range query only when [hash_key] or [hash_key, range_key] is specified in query keys.
274
291
  def range?
275
- return false unless source.range_key
292
+ return false unless query_keys.include?(source.hash_key.to_s) or query_keys.include?(source.range_key.to_s)
276
293
  query_keys == [source.hash_key.to_s] || (query_keys.to_set == [source.hash_key.to_s, source.range_key.to_s].to_set)
277
294
  end
278
295
 
@@ -298,6 +315,7 @@ module Dynamoid #:nodoc:
298
315
  opts = {}
299
316
  opts[:limit] = @limit if @limit
300
317
  opts[:next_token] = start_key if @start
318
+ opts[:batch_size] = @batch_size if @batch_size
301
319
  opts
302
320
  end
303
321
  end