dynamini 2.9.5 → 2.9.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f7c910987e834399c78a7c26cf2524d00eb6729
4
- data.tar.gz: 74298b30b3f7994a64ee6d54b0d6d15709eaabd0
3
+ metadata.gz: 394d8fc8fe9d1d1f6a0380d91009868d82eae552
4
+ data.tar.gz: 3b831be32226ae1ccdbc2da1b7b11b2f9a5b3e56
5
5
  SHA512:
6
- metadata.gz: 2aa48e095d7f04867863d9e33571f90e57e8bde41daa5c6c36ddfd6b9428c814187fbb39111b7eca4cfaa0f8c5cc591aaecdd425d6100e4a043a5ee601bda1a6
7
- data.tar.gz: 76fcae444b9633607b5abb142afd5bf2fd2182edc42390a5be61b6fb1adb8d8bc10519c01da1c8b84148563bc10fd8da74bab70786981f8c19571398a51e42f8
6
+ metadata.gz: 4d1db9cfb9c089962b23c11204cc6ef9608a4eb46585d051f7d5a40856c40e49a5e9093546a3028d5a72f601930ee903f863d1bf56a2548df1430c59cf637c74
7
+ data.tar.gz: f9e7a129e6f89b8588520392ccd5509deddf62e4aaaca493f9dca695732775d51a8e113c043f9b3bab8b8fb5aafc5ef5bbef82bc0e23c850cd4e5026002e9750
data/Gemfile.lock CHANGED
@@ -1,29 +1,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamini (2.9.5)
4
+ dynamini (2.9.6)
5
5
  activemodel (>= 3, < 5.0)
6
6
  aws-sdk (~> 2)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (4.2.5.1)
12
- activesupport (= 4.2.5.1)
11
+ activemodel (4.2.9)
12
+ activesupport (= 4.2.9)
13
13
  builder (~> 3.1)
14
- activesupport (4.2.5.1)
14
+ activesupport (4.2.9)
15
15
  i18n (~> 0.7)
16
- json (~> 1.7, >= 1.7.7)
17
16
  minitest (~> 5.1)
18
17
  thread_safe (~> 0.3, >= 0.3.4)
19
18
  tzinfo (~> 1.1)
20
- aws-sdk (2.2.22)
21
- aws-sdk-resources (= 2.2.22)
22
- aws-sdk-core (2.2.22)
19
+ aws-sdk (2.10.45)
20
+ aws-sdk-resources (= 2.10.45)
21
+ aws-sdk-core (2.10.45)
22
+ aws-sigv4 (~> 1.0)
23
23
  jmespath (~> 1.0)
24
- aws-sdk-resources (2.2.22)
25
- aws-sdk-core (= 2.2.22)
26
- builder (3.2.2)
24
+ aws-sdk-resources (2.10.45)
25
+ aws-sdk-core (= 2.10.45)
26
+ aws-sigv4 (1.0.2)
27
+ builder (3.2.3)
27
28
  coderay (1.1.0)
28
29
  diff-lcs (1.2.5)
29
30
  ffi (1.9.10)
@@ -48,15 +49,14 @@ GEM
48
49
  guard-shell (0.7.1)
49
50
  guard (>= 2.0.0)
50
51
  guard-compat (~> 1.0)
51
- i18n (0.7.0)
52
- jmespath (1.1.3)
53
- json (1.8.3)
52
+ i18n (0.8.6)
53
+ jmespath (1.3.1)
54
54
  listen (3.0.3)
55
55
  rb-fsevent (>= 0.9.3)
56
56
  rb-inotify (>= 0.9)
57
57
  lumberjack (1.0.9)
58
58
  method_source (0.8.2)
59
- minitest (5.8.4)
59
+ minitest (5.10.3)
60
60
  nenv (0.2.0)
61
61
  notiffany (0.0.8)
62
62
  nenv (~> 0.1)
@@ -85,8 +85,8 @@ GEM
85
85
  shellany (0.0.1)
86
86
  slop (3.6.0)
87
87
  thor (0.19.1)
88
- thread_safe (0.3.5)
89
- tzinfo (1.2.2)
88
+ thread_safe (0.3.6)
89
+ tzinfo (1.2.3)
90
90
  thread_safe (~> 0.1)
91
91
 
92
92
  PLATFORMS
@@ -99,3 +99,6 @@ DEPENDENCIES
99
99
  guard-shell
100
100
  pry (~> 0)
101
101
  rspec (~> 3)
102
+
103
+ BUNDLED WITH
104
+ 1.16.0.pre.2
data/README.md CHANGED
@@ -78,7 +78,7 @@ Here's what a sample model looks like. This one includes a range key - sometimes
78
78
 
79
79
  ```ruby
80
80
  class Vehicle < Dynamini::Base
81
- set_table_name 'cars-dev' # must match the table name configured in AWS
81
+ set_table_name 'cars' # must match the table name configured in AWS
82
82
  set_hash_key :model # defaults to :id if not set
83
83
  set_range_key :vin # must be set if your AWS table is configured with a range key
84
84
 
@@ -86,8 +86,16 @@ class Vehicle < Dynamini::Base
86
86
  end
87
87
  ```
88
88
 
89
+ If you don't use set_table_name, Dynamini will try to find a table with the pluralized, downcased class name. For instance, a Dynamini class called PageView would look for a table called 'page_views'. If you use separate DynamoDB tables for development and production, wrap set_table_name in a conditional to determine the appropriate table when your class initializes. In this example, the production table is 'vehicles' and the development table is 'vehicles-dev':
90
+
91
+ ```ruby
92
+ class Vehicle < Dynamini::Base
93
+ set_table_name 'vehicles-dev' unless Rails.env.production?
94
+ end
95
+ ```
96
+
89
97
  ## Datatype Handling
90
- There are a few quirks about how the Dynamo Ruby SDK stores data. It stores numeric values as BigDecimal objects, symbols as strings, and doesn't accept ruby Date or Time objects. To save you from having to convert your data to the correct type before saving and after retrieval, you can use the :handle helper for automatic type conversion. You can also use this to specify default values for your fields. Here's how it works:
98
+ There are a few quirks about how the Dynamo Ruby SDK stores data. It stores numeric values as BigDecimal objects, symbols as strings, and doesn't accept ruby Date or Time objects. To save you from having to convert your data to the correct type before saving and after retrieval, you can use :handle to manage automatic type conversion. This is also where you can specify default values for your attributes. Here's how it works:
91
99
 
92
100
  ```ruby
93
101
  class Vehicle < Dynamini::Base
@@ -98,6 +106,7 @@ end
98
106
  car = Vehicle.new(vin: '43H1R')
99
107
  car.top_speed
100
108
  > 80
109
+
101
110
  car.top_speed = 90
102
111
  car.save
103
112
  Vehicle.find('43H1R').top_speed
@@ -105,9 +114,7 @@ Vehicle.find('43H1R').top_speed
105
114
  # This would return BigDecimal(90) without the handle helper.
106
115
  ```
107
116
 
108
- Defaults are optional - without a default, a handled field without a value assigned to it will return nil like any other field.
109
-
110
- The following datatypes are supported by handle:
117
+ Dynamini lets you :handle the following types:
111
118
  * :integer
112
119
  * :float
113
120
  * :symbol
@@ -118,8 +125,14 @@ The following datatypes are supported by handle:
118
125
  * :array
119
126
  * :set
120
127
 
121
- Booleans and strings don't actually need to be translated, but you can set up defaults for those fields this way.
122
- The magic fields updated_at and created_at are handled as :time by default.
128
+ Default values aren't actually written to the database when saving your instance. Instead, they define what will be returned when reading an unset or nullified attribute. If you don't provide your own default, the "default default" value depends on the specified type:
129
+
130
+ * array: []
131
+ * set: Set.new
132
+ * all other types: nil
133
+ * attribute not handled: nil
134
+
135
+ The auto-generated fields updated_at and created_at are intrinsically handled as :time.
123
136
 
124
137
  ## Enumerable Attributes
125
138
  You can save arrays and sets to your Dynamini model. Optionally, you can have Dynamini perform type conversion on each element of your enumerable. Here's how it works:
@@ -288,35 +301,32 @@ Product.find('qwerty').updated_at
288
301
  ````
289
302
 
290
303
  ## Testing
291
- We've included an optional in-memory test client, so you don't necessarily have to connect to a real Dynamo instance when running tests. You could also use this in your development environment if you don't have a real Dynamo instance yet, but the data saved to it won't persist through a server restart.
292
-
293
- To activate this feature, just require the testing module:
304
+ Dynamini includes an in-memory test client, so you don't have to connect to a real Dynamo instance when running tests. To activate this feature, just require the testing module:
294
305
  ```ruby
295
306
  require 'dynamini/testing'
296
307
  ```
297
- This module replaces all API calls Dynamini makes to AWS DynamoDB with calls to Dynamini::TestClient.
308
+ Requiring the module replaces all API calls Dynamini makes to AWS with calls to Dynamini::TestClient.
298
309
 
299
- The test client will not reset its database unless you tell it to, like so:
310
+ You probably don't want your data to persist between tests, so you'll have to reset the test client to wipe its data:
300
311
  ```ruby
312
+ # in this case Vehicle is our Dynamini subclass
301
313
  Vehicle.client.reset
302
314
  ```
303
315
 
304
- So, for instance, to get Rspec working with your test suite the way your ActiveRecord model behaved, add these lines to your spec_helper.rb:
316
+ Here's an implementation of the above in a typical spec_helper.rb:
305
317
  ```ruby
306
318
  require 'dynamini/testing'
307
319
 
308
320
  config.after(:each) {
309
- Vehicle.client.reset # Large test suites will be very slow and unpredictable otherwise!
321
+ Vehicle.client.reset
310
322
  }
311
323
  ```
312
324
 
313
325
  ## Things to remember
314
- * Since DynamoDB is schemaless, your model will respond to any method that looks like a reader, meaning model.foo will return nil.
315
- * You can also write any arbitrary attribute to your model.
316
- * Other models in your app cannot have a has_one or has_many relationship with your Dynamini model, since these would require a table scan. Your other models can still use belongs_to.
317
- * If you change the primary key value on an instance of your model, then resave it, you'll have two copies in your database.
318
- * If you use non-numeric strings for your primary key, remember to change your foreign key columns on related ActiveRecord tables to be string type.
319
- * You might want to conditionally set the table name for your model based on the Rails.env, enabling separate tables for development and production.
326
+ * Since DynamoDB is schemaless, Dynamini is designed to allow your instance to respond to any method call that looks like an attribute name, even if you've never referenced it before. For instance, model.i_bet_this_will_raise_an_error will return nil.
327
+ * Similarly, you can write any arbitrarily-named attribute to your instance without defining its name or properties beforehand.
328
+ * If you change the primary key value on an instance of your model, then resave it, you'll have two records in your database.
329
+ * If you have a model with a foreign key attribute that points to your Dynamini model, you can use Rails' :belongs_to association helper normally. (If you use non-numeric strings for your Dynamini hash key, remember to change your foreign key columns on related ActiveRecord tables to be string type.)
320
330
 
321
331
  ## Contributing
322
332
 
data/dynamini.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'dynamini'
3
- s.version = '2.9.5'
3
+ s.version = '2.9.6'
4
4
  s.summary = 'DynamoDB interface'
5
5
  s.description = 'Lightweight DynamoDB interface gem designed as
6
6
  a drop-in replacement for ActiveRecord.
@@ -53,7 +53,8 @@ module Dynamini
53
53
 
54
54
  def attribute_updates
55
55
  changes.reduce({}) do |updates, (key, value)|
56
- current_value = value[1]
56
+ # TODO: remove this ternary once aws-sdk accepts empty set pull request
57
+ current_value = value[1].is_a?(Set) && value[1].empty? ? nil : value[1]
57
58
  updates[key] = { action: value[2] || 'PUT' }
58
59
  updates[key][:value] = current_value unless current_value == DELETED_TOKEN
59
60
  updates
data/lib/dynamini/base.rb CHANGED
@@ -53,7 +53,7 @@ module Dynamini
53
53
  handle(key, format) if format
54
54
  end
55
55
 
56
- def set_secondary_index(index_name, args)
56
+ def set_secondary_index(index_name, args = {})
57
57
  @secondary_index ||= {}
58
58
  @secondary_index[index_name.to_s] = {hash_key_name: args[:hash_key] || hash_key, range_key_name: args[:range_key]}
59
59
  end
@@ -69,7 +69,8 @@ module Dynamini
69
69
  def dynamo_scan(options)
70
70
  if options[:start_key] && !options[:start_key].is_a?(Hash)
71
71
  if options[:index_name]
72
- start_key = { options[:index_name].to_s => options[:start_key] }
72
+ attr = secondary_index[options[:index_name]][:hash_key_name].to_s
73
+ start_key = { attr => options[:start_key] }
73
74
  else
74
75
  start_key = { hash_key.to_s => options[:start_key] }
75
76
  end
@@ -92,7 +93,7 @@ module Dynamini
92
93
  raise ArgumentError, 'Must specify segment if specifying total_segments'
93
94
  elsif options[:segment] && !options[:total_segments]
94
95
  raise ArgumentError, 'Must specify total_segments if specifying segment'
95
- elsif options[:index_name] && !self.secondary_index[options[:index_name]]
96
+ elsif options[:index_name] && (!self.secondary_index || !self.secondary_index[options[:index_name]])
96
97
  raise ArgumentError, "Secondary index of #{options[:index_name]} does not exist"
97
98
  end
98
99
  end
@@ -86,7 +86,6 @@ module Dynamini
86
86
 
87
87
  def scan(args = {})
88
88
  records = get_table(args[:table_name]).values
89
-
90
89
  sort_scanned_records!(records, args[:secondary_index_name]) if args[:secondary_index_name]
91
90
  start_index = index_of_start_key(args, records)
92
91
  items = limit_scanned_records(args[:limit], records, start_index)
@@ -185,6 +185,40 @@ describe Dynamini::Base do
185
185
  model.save
186
186
  end
187
187
  end
188
+ context 'when an empty set is submitted' do
189
+ it 'should nullify the set' do
190
+ expect(model.class.client).to receive(:update_item).with(
191
+ table_name: 'bases',
192
+ key: {id: model_attributes[:id]},
193
+ attribute_updates: hash_including(
194
+ "foo" => {
195
+ value: nil,
196
+ action: 'PUT'
197
+ }
198
+ )
199
+ )
200
+ model.foo = Set.new
201
+ model.bar = 4
202
+ model.save
203
+ end
204
+ end
205
+ context 'when a populated set is submitted' do
206
+ it 'should submit the set' do
207
+ expect(model.class.client).to receive(:update_item).with(
208
+ table_name: 'bases',
209
+ key: {id: model_attributes[:id]},
210
+ attribute_updates: hash_including(
211
+ "foo" => {
212
+ value: Set.new([1]),
213
+ action: 'PUT'
214
+ }
215
+ )
216
+ )
217
+ model.foo = Set.new([1])
218
+ model.bar = 4
219
+ model.save
220
+ end
221
+ end
188
222
  end
189
223
 
190
224
  context 'when failing validation' do
@@ -127,103 +127,84 @@ describe Dynamini::BatchOperations do
127
127
  end
128
128
  end
129
129
 
130
- class SecBase < Dynamini::Base
131
- set_hash_key :id
132
- set_secondary_index :sec, hash_key: :sec
133
- set_secondary_index :rev, hash_key: :sec
134
- end
135
-
136
130
  describe '.scan' do
131
+ let(:response) { OpenStruct.new(items: [], last_evaluated_key: {'foo' => 'bar'}) }
137
132
 
138
133
  before do
139
- SecBase.create(id: '123', sec: 'D')
140
- SecBase.create(id: '124', sec: 'C')
141
- SecBase.create(id: '125', sec: 'B')
142
- SecBase.create(id: '126', sec: 'A')
134
+ subject.set_secondary_index :foo_index, hash_key: :foo_attr
143
135
  end
144
136
 
145
- context 'scanning the primary key' do
146
- context 'with an exclusive_start_key' do
147
- context 'with a limit' do
148
- it 'retrieves the correct items' do
149
- response = SecBase.scan(start_key: '124', limit: 1)
150
- expect(response.items.map { |i| i.id }).to eq(['124'])
151
- expect(response.last_evaluated_key).to eq('id' => '124')
152
- end
153
- end
154
- context 'without a limit' do
155
- it 'retrieves the correct items' do
156
- response = SecBase.scan(start_key: '124')
157
- expect(response.items.map { |i| i.id }).to eq(%w(124 125 126))
158
- expect(response.last_evaluated_key).to be_nil
159
- end
160
- end
161
-
162
- context 'the start key is in aws sdk style' do
163
- it 'retrieves the correct items' do
164
- response = SecBase.scan(start_key: {'id' => '124'}, limit: 2)
165
- expect(response.items.map { |i| i.id }).to eq(['124', '125'])
166
- expect(response.last_evaluated_key).to eq('id' => '125')
167
- end
168
- end
137
+ context 'no start key is provided' do
138
+ it 'passes the request to the client in the correct shape and returns the correct shape' do
139
+ expect(subject.client).to receive(:scan).with(
140
+ consistent_read: false,
141
+ secondary_index_name: 'foo_index',
142
+ limit: 100,
143
+ segment: 1,
144
+ total_segments: 2,
145
+ table_name: 'bases'
146
+ ).and_return(response)
147
+
148
+ expect(
149
+ subject.scan(
150
+ consistent_read: false,
151
+ index_name: 'foo_index',
152
+ limit: 100,
153
+ segment: 1,
154
+ total_segments: 2
155
+ )
156
+ ).to eq(response)
169
157
  end
170
- context 'without an exclusive_start_key' do
171
- context 'with a limit' do
172
- it 'retrieves the correct items' do
173
- response = SecBase.scan(limit: 2)
174
- expect(response.items.map { |i| i.id }).to eq(%w(123 124))
175
- expect(response.last_evaluated_key).to eq('id' => '124')
176
- end
177
- end
178
- context 'without a limit' do
179
- it 'retrieves the correct items' do
180
- response = SecBase.scan
181
- expect(response.items.map { |i| i.id }).to eq(%w(123 124 125 126))
182
- expect(response.last_evaluated_key).to be_nil
183
- end
184
- end
158
+ end
159
+ context 'a literal start key is provided' do
160
+ it 'passes the request to the client in the correct shape and returns the correct shape' do
161
+ expect(subject.client).to receive(:scan).with(
162
+ consistent_read: false,
163
+ secondary_index_name: 'foo_index',
164
+ exclusive_start_key: { 'foo_attr' => 'abc' },
165
+ limit: 100,
166
+ segment: 1,
167
+ total_segments: 2,
168
+ table_name: 'bases'
169
+ ).and_return(response)
170
+
171
+ expect(subject.scan(
172
+ consistent_read: false,
173
+ index_name: 'foo_index',
174
+ start_key: 'abc',
175
+ limit: 100,
176
+ segment: 1,
177
+ total_segments: 2
178
+ )).to eq(response)
185
179
  end
186
180
  end
187
- context 'scanning a secondary index' do
188
- context 'with an exclusive_start_key' do
189
- context 'with a limit' do
190
- it 'retrieves the correct items' do
191
- response = SecBase.scan(index_name: 'sec', start_key: 'B', limit: 2)
192
- expect(response.items.map { |i| i.sec }).to eq(%w(B C))
193
- expect(response.last_evaluated_key).to eq('sec' => 'C')
194
- end
195
- end
196
- context 'without a limit' do
197
- it 'retrieves the correct items' do
198
- response = SecBase.scan(index_name: 'sec', start_key: 'B')
199
- expect(response.items.map { |i| i.sec }).to eq(%w(B C D))
200
- expect(response.last_evaluated_key).to be_nil
201
- end
202
- end
181
+ context 'a complex start key is provided' do
182
+ it 'passes the request to the client in the correct shape and returns the correct shape' do
183
+ expect(subject.client).to receive(:scan).with(
184
+ consistent_read: false,
185
+ secondary_index_name: 'foo_index',
186
+ exclusive_start_key: { 'foo_attr' => 'abc' },
187
+ limit: 100,
188
+ segment: 1,
189
+ total_segments: 2,
190
+ table_name: 'bases'
191
+ ).and_return(response)
192
+
193
+ expect(subject.scan(
194
+ consistent_read: false,
195
+ index_name: 'foo_index',
196
+ start_key: { 'foo_attr' => 'abc' },
197
+ limit: 100,
198
+ segment: 1,
199
+ total_segments: 2
200
+ )).to eq(response)
203
201
  end
204
- context 'without an exclusive_start_key' do
205
- context 'with a limit' do
206
- it 'retrieves the correct items' do
207
- response = SecBase.scan(index_name: 'sec', limit: 3)
208
- expect(response.items.map { |i| i.sec }).to eq(%w(A B C))
209
- expect(response.last_evaluated_key).to eq('sec' => 'C')
210
- end
211
- end
212
- context 'without a limit' do
213
- it 'retrieves the correct items' do
214
- response = SecBase.scan(index_name: 'sec')
215
- expect(response.items.map { |i| i.sec }).to eq(%w(A B C D))
216
- expect(response.last_evaluated_key).to be_nil
217
- end
202
+ context 'with a model with no secondary indices but with a start key' do
203
+ it 'behaves' do
204
+ expect{ Dynamini::Base.scan(start_key: '123') }.to_not raise_error
218
205
  end
219
206
  end
220
207
  end
221
-
222
- context 'with a model with no secondary indices but with a start key' do
223
- it 'behaves' do
224
- expect{ Dynamini::Base.scan(start_key: '123') }.to_not raise_error
225
- end
226
- end
227
208
  end
228
209
  end
229
210
 
@@ -470,4 +470,87 @@ describe Dynamini::TestClient do
470
470
  expect(result.responses[table_name].first[:id]).to eq('foo')
471
471
  end
472
472
  end
473
+
474
+ class SecBase < Dynamini::Base
475
+ set_hash_key :id
476
+ set_secondary_index :sec_index, hash_key: :sec
477
+ end
478
+
479
+ describe '.scan' do
480
+ before do
481
+ SecBase.create(id: '123', sec: 'D')
482
+ SecBase.create(id: '124', sec: 'C')
483
+ SecBase.create(id: '125', sec: 'B')
484
+ SecBase.create(id: '126', sec: 'A')
485
+ end
486
+
487
+ context 'scanning the primary key' do
488
+ context 'with an exclusive_start_key' do
489
+ context 'with a limit' do
490
+ it 'retrieves the correct items' do
491
+ response = SecBase.client.scan(exclusive_start_key: {'id' => '124'}, limit: 1, table_name: 'sec_bases')
492
+ expect(response.items.map { |i| i[:id] }).to eq(['124'])
493
+ expect(response.last_evaluated_key).to eq('id' => '124')
494
+ end
495
+ end
496
+ context 'without a limit' do
497
+ it 'retrieves the correct items' do
498
+ response = SecBase.client.scan(exclusive_start_key: {'id' => '124'}, table_name: 'sec_bases')
499
+ expect(response.items.map { |i| i[:id] }).to eq(%w(124 125 126))
500
+ expect(response.last_evaluated_key).to be_nil
501
+ end
502
+ end
503
+ end
504
+ context 'without an exclusive_start_key' do
505
+ context 'with a limit' do
506
+ it 'retrieves the correct items' do
507
+ response = SecBase.client.scan(limit: 2, table_name: 'sec_bases')
508
+ expect(response.items.map { |i| i[:id] }).to eq(%w(123 124))
509
+ expect(response.last_evaluated_key).to eq('id' => '124')
510
+ end
511
+ end
512
+ context 'without a limit' do
513
+ it 'retrieves the correct items' do
514
+ response = SecBase.client.scan(table_name: 'sec_bases')
515
+ expect(response.items.map { |i| i[:id] }).to eq(%w(123 124 125 126))
516
+ expect(response.last_evaluated_key).to be_nil
517
+ end
518
+ end
519
+ end
520
+ end
521
+ context 'scanning a secondary index' do
522
+ context 'with an exclusive_start_key' do
523
+ context 'with a limit' do
524
+ it 'retrieves the correct items' do
525
+ response = SecBase.client.scan(secondary_index_name: 'sec_index', exclusive_start_key: {'sec' => 'B'}, limit: 2, table_name: 'sec_bases')
526
+ expect(response.items.map { |i| i['sec'] }).to eq(%w(B C))
527
+ expect(response.last_evaluated_key).to eq('sec' => 'C')
528
+ end
529
+ end
530
+ context 'without a limit' do
531
+ it 'retrieves the correct items' do
532
+ response = SecBase.client.scan(secondary_index_name: 'sec_index', exclusive_start_key: {'sec' => 'B'}, table_name: 'sec_bases')
533
+ expect(response.items.map { |i| i['sec'] }).to eq(%w(B C D))
534
+ expect(response.last_evaluated_key).to be_nil
535
+ end
536
+ end
537
+ end
538
+ context 'without an exclusive_start_key' do
539
+ context 'with a limit' do
540
+ it 'retrieves the correct items' do
541
+ response = SecBase.client.scan(secondary_index_name: 'sec_index', limit: 3, table_name: 'sec_bases')
542
+ expect(response.items.map { |i| i['sec'] }).to eq(%w(A B C))
543
+ expect(response.last_evaluated_key).to eq('sec' => 'C')
544
+ end
545
+ end
546
+ context 'without a limit' do
547
+ it 'retrieves the correct items' do
548
+ response = SecBase.client.scan(secondary_index_name: 'sec_index', table_name: 'sec_bases')
549
+ expect(response.items.map { |i| i['sec'] }).to eq(%w(A B C D))
550
+ expect(response.last_evaluated_key).to be_nil
551
+ end
552
+ end
553
+ end
554
+ end
555
+ end
473
556
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamini
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.5
4
+ version: 2.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Ward
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2017-09-14 00:00:00.000000000 Z
18
+ date: 2017-09-15 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activemodel