dynamini 2.1.2 → 2.2.0

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: f7b0c9b660d05e9791a38a79ee09b2847d9dff48
4
- data.tar.gz: 5262cf9c63c4a2152a1182c2037712855e35ffda
3
+ metadata.gz: bc4e5ccebe6f491cae52d735aa59d15b307e93ab
4
+ data.tar.gz: fc6340c78b0597b67c870e4aa75837c38e2d7c2f
5
5
  SHA512:
6
- metadata.gz: ad4de22dd7dce271017ee4badda3ce8babc3bb3b97592f88bb03c2485af77fc74a621e0bea47c7f9c610d7e39c2b7ac939fdbdf4b67672bccc5cabe20a769952
7
- data.tar.gz: 06646d44a9ae44ae92560f4800ce3a5c6421405b915ab3dbcce0e721d246f3b1eaab59ac7122307025ee2b09246ee27050dea5ea9f2961eb09645f9daaf54e88
6
+ metadata.gz: f75b4504629a2dbcd718c0fb5c150a7c9911cbd255046fbc180704c13f7350d409beb2567939f4c1ace8d808692ed1ac7d835e615c09f8719f3657a81159ecdc
7
+ data.tar.gz: dd4b0336585dfb376f51f4a142a278aaaa8814713aa5c363b50928c70b4ee20ee85d9ec14e6c48ec62581e30789b9e78c54b664b7f4bca1f5b2554943f76fe53
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamini (2.1.2)
4
+ dynamini (2.2.0)
5
5
  activemodel (>= 3, < 5.0)
6
6
  aws-sdk (~> 2)
7
7
 
@@ -99,3 +99,6 @@ DEPENDENCIES
99
99
  guard-shell
100
100
  pry (~> 0)
101
101
  rspec (~> 3)
102
+
103
+ BUNDLED WITH
104
+ 1.11.2
data/dynamini.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'dynamini'
3
- s.version = '2.1.2'
3
+ s.version = '2.2.0'
4
4
  s.summary = 'DynamoDB interface'
5
5
  s.description = 'Lightweight DynamoDB interface gem designed as
6
6
  a drop-in replacement for ActiveRecord.
data/lib/dynamini/base.rb CHANGED
@@ -30,7 +30,7 @@ module Dynamini
30
30
 
31
31
  class << self
32
32
 
33
- attr_reader :range_key
33
+ attr_reader :range_key, :secondary_index
34
34
 
35
35
  def table_name
36
36
  @table_name ||= name.demodulize.tableize
@@ -48,6 +48,11 @@ module Dynamini
48
48
  @range_key = key
49
49
  end
50
50
 
51
+ def set_secondary_index(index_name, args)
52
+ @secondary_index ||= {}
53
+ @secondary_index[index_name.to_s] = {hash_key_name: args[:hash_key] || hash_key, range_key_name: args[:range_key]}
54
+ end
55
+
51
56
  def hash_key
52
57
  @hash_key || :id
53
58
  end
@@ -51,14 +51,14 @@ module Dynamini
51
51
  def dynamo_query(args)
52
52
  expression_attribute_values = build_expression_attribute_values(args)
53
53
  key_condition_expression = build_key_condition_expression(args)
54
-
55
- client.query(set_extra_parameters(
56
- {
57
- table_name: table_name,
58
- key_condition_expression: key_condition_expression,
59
- expression_attribute_values: expression_attribute_values
60
- },
61
- args))
54
+ query = set_extra_parameters(
55
+ {
56
+ table_name: table_name,
57
+ key_condition_expression: key_condition_expression,
58
+ expression_attribute_values: expression_attribute_values
59
+ },
60
+ args)
61
+ client.query(query)
62
62
  end
63
63
 
64
64
  def build_expression_attribute_values(args)
@@ -70,19 +70,28 @@ module Dynamini
70
70
  end
71
71
 
72
72
  def build_key_condition_expression(args)
73
- expression = "#{hash_key} = :h"
73
+ expression = "#{current_index_hash_key(args)} = :h"
74
74
  if args[:start] && args[:end]
75
- expression += " AND #{range_key} BETWEEN :s AND :e"
75
+ expression += " AND #{current_index_range_key(args)} BETWEEN :s AND :e"
76
76
  elsif args[:start]
77
- expression += " AND #{range_key} >= :s"
77
+ expression += " AND #{current_index_range_key(args)} >= :s"
78
78
  elsif args[:end]
79
- expression += " AND #{range_key} <= :e"
79
+ expression += " AND #{current_index_range_key(args)} <= :e"
80
80
  end
81
81
  expression
82
82
  end
83
83
 
84
+ def current_index_hash_key(args)
85
+ args[:index_name] ? secondary_index[args[:index_name].to_s][:hash_key_name] : hash_key
86
+ end
87
+
88
+ def current_index_range_key(args)
89
+ args[:index_name] ? secondary_index[args[:index_name].to_s][:range_key_name] : range_key
90
+ end
91
+
84
92
  def set_extra_parameters(hash, args)
85
93
  extras = args.select { |k, v| OPTIONAL_QUERY_PARAMS.include? k }
94
+ extras[:index_name] = args[:index_name].to_s if args[:index_name]
86
95
  hash.merge!(extras)
87
96
  end
88
97
 
@@ -4,12 +4,13 @@ module Dynamini
4
4
  # In-memory database client for test purposes.
5
5
  class TestClient
6
6
 
7
- attr_reader :hash_key_attr, :data, :range_key_attr
7
+ attr_reader :hash_key_attr, :data, :range_key_attr, :secondary_index
8
8
 
9
- def initialize(hash_key_attr, range_key_attr = nil)
9
+ def initialize(hash_key_attr, range_key_attr = nil, secondary_index=nil)
10
10
  @data = {}
11
11
  @hash_key_attr = hash_key_attr
12
12
  @range_key_attr = range_key_attr
13
+ @secondary_index = secondary_index
13
14
  end
14
15
 
15
16
  def get_table(table_name)
@@ -28,27 +29,34 @@ module Dynamini
28
29
  hash_key_attr => hash_key_value
29
30
  )
30
31
 
31
- if hash_key_value
32
- if range_key_value
33
- updates.merge!(range_key_attr => range_key_value)
34
- if table[hash_key_value] && table[hash_key_value][range_key_value]
35
- table[hash_key_value][range_key_value].merge! updates
36
- else
37
- table[hash_key_value] ||= {}
38
- table[hash_key_value][range_key_value] = updates
39
- end
32
+ primary_index_insertion(hash_key_value, range_key_value, updates, table) if hash_key_value
40
33
 
41
- else
42
- if table[hash_key_value]
43
- table[hash_key_value].merge!(updates)
44
- else
45
- table[hash_key_value] = updates
46
- end
47
- end
34
+ end
35
+
36
+ def primary_index_insertion(hash_key_value, range_key_value, updates, table)
37
+ if range_key_value
38
+ primary_with_range_insertion(hash_key_value, range_key_value, updates, table)
39
+ else
40
+ primary_only_hash_insertion(hash_key_value, updates, table)
41
+ end
42
+ end
43
+
44
+ def primary_with_range_insertion(hash_key_value, range_key_value, updates, table)
45
+ updates.merge!(range_key_attr => range_key_value)
46
+ if table[hash_key_value] && table[hash_key_value][range_key_value]
47
+ table[hash_key_value][range_key_value].merge! updates
48
+ else
49
+ table[hash_key_value] ||= {}
50
+ table[hash_key_value][range_key_value] = updates
48
51
  end
52
+ end
49
53
 
54
+ def primary_only_hash_insertion(hash_key_value, updates, table)
55
+ table[hash_key_value] ? table[hash_key_value].merge!(updates) : table[hash_key_value] = updates
50
56
  end
51
57
 
58
+
59
+
52
60
  def get_item(args = {})
53
61
  table = get_table(args[:table_name])
54
62
 
@@ -103,39 +111,85 @@ module Dynamini
103
111
  end
104
112
 
105
113
  tokens = args[:key_condition_expression].split(/\s+/)
106
- hash_key = args[:expression_attribute_values][":h"].is_a?(Integer) ? tokens[2].to_i : tokens[2]
114
+ start_val, end_val = range_key_limits(tokens)
115
+
116
+ if args[:index_name]
117
+ secondary_index_query(args)
118
+ else
119
+ hash_key = hash_key_value(args).is_a?(Integer) ? tokens[2].to_i : tokens[2]
120
+
121
+ parent = get_table(args[:table_name])[hash_key]
122
+ return OpenStruct.new(items: []) unless parent
123
+ selected = apply_filter_options(parent, args, start_val, end_val)
124
+
125
+ OpenStruct.new(items: selected)
126
+ end
127
+ end
128
+
129
+ def range_key_limits(tokens)
107
130
  case tokens[5]
108
- when ">="
109
- start_val = tokens[6]
110
- end_val = nil
111
- when "<="
112
- start_val = nil
113
- end_val = tokens[6]
114
- when "BETWEEN"
115
- start_val = tokens[6]
116
- end_val = tokens[8]
117
- else
118
- start_val = nil
119
- end_val = nil
131
+ when ">=" then [tokens[6], nil]
132
+ when "<=" then [nil, tokens[6]]
133
+ when "BETWEEN" then [tokens[6], tokens[8]]
134
+ else [nil, nil]
120
135
  end
121
- parent = get_table(args[:table_name])[hash_key]
122
- return OpenStruct.new(items:[]) unless parent
136
+ end
123
137
 
124
- selected = parent.values
125
- selected = selected.select{ |item| item[@range_key_attr] >= start_val.to_f } if start_val
126
- selected = selected.select{ |item| item[@range_key_attr] <= end_val.to_f } if end_val
127
- selected = selected.sort! { |a,b| b[@range_key_attr] <=> a[@range_key_attr] } if args[:scan_index_forward] == false
128
- selected = selected[0...args[:limit]] if args[:limit]
138
+ def apply_filter_options(parent, args, start_val, end_val)
139
+ records = parent.values
140
+ records = records.select { |record| record[@range_key_attr] >= start_val.to_f } if start_val
141
+ records = records.select { |record| record[@range_key_attr] <= end_val.to_f } if end_val
142
+ records = records.sort! { |a, b| b[@range_key_attr] <=> a[@range_key_attr] } if args[:scan_index_forward] == false
143
+ records = records[0...args[:limit]] if args[:limit]
144
+ records
145
+ end
129
146
 
147
+
148
+ def secondary_index_query(args = {})
149
+ index = secondary_index[args[:index_name]]
150
+ table = get_table(args[:table_name])
151
+
152
+ tokens = args[:key_condition_expression].split(/\s+/)
153
+ start_val, end_val = range_key_limits(tokens)
154
+
155
+ records = @range_key_attr ? get_values(table) : table.values
156
+ selected = sort_records(records, index, args, start_val, end_val)
130
157
  OpenStruct.new(items: selected)
131
158
  end
132
159
 
160
+ def sort_records(records, index, args, start_val, end_val)
161
+ records = records.select { |record| record[get_secondary_hash_key(index)] == hash_key_value(args) }
162
+ records = records.select { |record| record[get_secondary_range_key(index)] >= start_val.to_f } if start_val
163
+ records = records.select { |record| record[get_secondary_range_key(index)] <= end_val.to_f } if end_val
164
+ records = records.sort { |a, b| a[get_secondary_range_key(index)] <=> b[get_secondary_range_key(index)] }
165
+ records = records.reverse if args[:scan_index_forward] == false
166
+ records = records[0...args[:limit]] if args[:limit]
167
+ records
168
+ end
169
+
170
+ def get_secondary_hash_key(index)
171
+ index[:hash_key_name] == @hash_key_attr ? index[:hash_key_name] : index[:hash_key_name].to_s
172
+ end
173
+
174
+ def get_secondary_range_key(index)
175
+ index[:range_key_name] == @range_key_attr ? index[:range_key_name] : index[:range_key_name].to_s
176
+ end
177
+
133
178
  def reset
134
179
  @data = {}
135
180
  end
136
181
 
137
182
  private
138
183
 
184
+ def hash_key_value(args)
185
+ args[:expression_attribute_values][":h"]
186
+ end
187
+
188
+ def get_values(table, records=[])
189
+ table.values.each { |value| records += value.values }
190
+ records
191
+ end
192
+
139
193
  def flatten_attribute_updates(args = {})
140
194
  attribute_hash = {}
141
195
 
@@ -4,7 +4,7 @@ module Dynamini
4
4
  module ClientInterface
5
5
  module ClassMethods
6
6
  def client
7
- @client ||= Dynamini::TestClient.new(hash_key, range_key)
7
+ @client ||= Dynamini::TestClient.new(hash_key, range_key, secondary_index)
8
8
  end
9
9
  end
10
10
  end
@@ -481,6 +481,28 @@ describe Dynamini::Base do
481
481
  end
482
482
  end
483
483
  end
484
+
485
+ describe '#set_secondary_index' do
486
+ it 'should return an hash containing only the hash_key and range_key name and value when setting a global secondary index' do
487
+ class TestClass < Dynamini::Base
488
+ set_hash_key :foo
489
+ set_range_key :bar
490
+ set_secondary_index :git_baz, hash_key: :git, range_key: :baz
491
+ end
492
+
493
+ expect(TestClass.secondary_index['git_baz']).to eq(hash_key_name: :git, range_key_name: :baz)
494
+ end
495
+
496
+ it 'should return an hash containing only the hash_key and range_key name and value when setting a local secondary index' do
497
+ class TestClass < Dynamini::Base
498
+ set_hash_key :foo
499
+ set_range_key :bar
500
+ set_secondary_index :foo_baz, range_key: :baz
501
+ end
502
+
503
+ expect(TestClass.secondary_index['foo_baz']).to eq(hash_key_name: :foo, range_key_name: :baz)
504
+ end
505
+ end
484
506
  end
485
507
  end
486
508
 
@@ -16,7 +16,9 @@ describe Dynamini::Querying do
16
16
  class TestClassWithRange < Dynamini::Base
17
17
  set_hash_key :foo
18
18
  set_range_key :bar
19
+ set_secondary_index :secondary_index, hash_key: :secondary_hash_key, range_key: :secondary_range_key
19
20
  handle :bar, :integer
21
+ handle :secondary_range_key, :integer
20
22
  end
21
23
 
22
24
  describe '.find' do
@@ -53,8 +55,10 @@ describe Dynamini::Querying do
53
55
  describe '.query' do
54
56
  before do
55
57
  4.times do |i|
56
- TestClassWithRange.create(foo: 'foo', bar: i + 1)
58
+ TestClassWithRange.create(foo: 'foo', bar: i + 1, secondary_hash_key: 'secondary_hash_key', secondary_range_key: 10 - i)
57
59
  end
60
+ TestClassWithRange.create(foo: 'foo2', bar: 5, secondary_hash_key: 'secondary_hash_key', secondary_range_key: 6)
61
+
58
62
  end
59
63
  context 'start value provided' do
60
64
  it 'should return records with a range key greater than or equal to the start value' do
@@ -129,6 +133,55 @@ describe Dynamini::Querying do
129
133
  expect(records.last.bar).to eq 1
130
134
  end
131
135
  end
136
+
137
+ context 'using secondary index' do
138
+ it 'should be able to query using the secondary index' do
139
+ records = TestClassWithRange.query(hash_key: 'secondary_hash_key', index_name: :secondary_index)
140
+ expect(records.length).to eq(5)
141
+ expect(records.first.secondary_range_key).to eq(6)
142
+ expect(records.last.secondary_range_key).to eq(10)
143
+ end
144
+
145
+ it 'should be able to sort backwards' do
146
+ records = TestClassWithRange.query(hash_key: 'secondary_hash_key', index_name: :secondary_index, scan_index_forward: false)
147
+ expect(records.length).to eq(5)
148
+ expect(records.first.secondary_range_key).to eq(10)
149
+ expect(records.last.secondary_range_key).to eq(6)
150
+ end
151
+
152
+ it 'should be able to limit number of results' do
153
+ records = TestClassWithRange.query(hash_key: 'secondary_hash_key', index_name: :secondary_index, limit: 3)
154
+ expect(records.length).to eq(3)
155
+ expect(records.first.secondary_range_key).to eq(6)
156
+ expect(records.last.secondary_range_key).to eq(8)
157
+ end
158
+
159
+ it 'should be able to give a minimum value for the range key' do
160
+ records = TestClassWithRange.query(hash_key: 'secondary_hash_key', index_name: :secondary_index, start: 8)
161
+ expect(records.length).to eq(3)
162
+ expect(records.first.secondary_range_key).to eq(8)
163
+ expect(records.last.secondary_range_key).to eq(10)
164
+ end
165
+
166
+ it 'should be able to give a maximum for the range key' do
167
+ records = TestClassWithRange.query(hash_key: 'secondary_hash_key', index_name: :secondary_index, end: 8)
168
+ expect(records.length).to eq(3)
169
+ expect(records.first.secondary_range_key).to eq(6)
170
+ expect(records.last.secondary_range_key).to eq(8)
171
+ end
172
+
173
+ it 'should be able to give a maximum for the range key' do
174
+ records = TestClassWithRange.query(hash_key: 'secondary_hash_key', index_name: :secondary_index, start: 7, end: 9)
175
+ expect(records.length).to eq(3)
176
+ expect(records.first.secondary_range_key).to eq(7)
177
+ expect(records.last.secondary_range_key).to eq(9)
178
+ end
179
+
180
+ it 'should return no results if none are found with the secondary index' do
181
+ expect(TestClassWithRange.query(hash_key: 'non-existent key', index_name: :secondary_index)).to eq([])
182
+ end
183
+
184
+ end
132
185
  end
133
186
 
134
187
  describe '.exists?' do
@@ -99,11 +99,11 @@ describe Dynamini::TestClient do
99
99
 
100
100
  describe '#query' do
101
101
 
102
- let(:test_client) { Dynamini::TestClient.new(:hash_key_field, :range_key_field) }
102
+ let(:test_client) { Dynamini::TestClient.new(:hash_key_field, :range_key_field, {'secondary_index' => {hash_key_name: 'abc', range_key_name: :secondary_range_key }})}
103
103
 
104
104
  before do
105
105
  4.times do |i|
106
- test_client.update_item(table_name: table_name, key: {hash_key_field: 'foo', range_key_field: i + 1}, attribute_updates: {abc: {value: 'abc', action: 'PUT'}})
106
+ test_client.update_item(table_name: table_name, key: {hash_key_field: 'foo', range_key_field: i + 1}, attribute_updates: {'abc' => {value: 'abc', action: 'PUT'}, 'secondary_range_key' => {value: 10 - i, action: 'PUT'}})
107
107
  end
108
108
  end
109
109
 
@@ -126,63 +126,146 @@ describe Dynamini::TestClient do
126
126
  context 'with LE operator' do
127
127
  it 'should return all items with range key less than or equal to the provided value' do
128
128
  response = test_client.query(
129
- table_name: table_name,
130
- key_condition_expression: "hash_key_field = :h AND user_id <= :e",
131
- expression_attribute_values: {
132
- ":h" => 'foo',
133
- ":e" => 2
134
- }
129
+ table_name: table_name,
130
+ key_condition_expression: "hash_key_field = :h AND user_id <= :e",
131
+ expression_attribute_values: {
132
+ ":h" => 'foo',
133
+ ":e" => 2
134
+ }
135
135
  )
136
136
  expect(response.items.length).to eq(2)
137
137
  expect(response.items.first[:range_key_field]).to eq(1)
138
138
  expect(response.items.last[:range_key_field]).to eq(2)
139
139
  end
140
140
  end
141
+
141
142
  context 'with GE operator' do
142
143
  it 'should return all items with range key greater than or equal to the provided value' do
143
144
  response = test_client.query(
144
- table_name: table_name,
145
- key_condition_expression: "hash_key_field = :h AND user_id >= :s",
146
- expression_attribute_values: {
147
- ":h" => 'foo',
148
- ":s" => 2
149
- }
145
+ table_name: table_name,
146
+ key_condition_expression: "hash_key_field = :h AND user_id >= :s",
147
+ expression_attribute_values: {
148
+ ":h" => 'foo',
149
+ ":s" => 2
150
+ }
150
151
  )
151
152
  expect(response.items.length).to eq(3)
152
153
  expect(response.items.first[:range_key_field]).to eq(2)
153
154
  expect(response.items.last[:range_key_field]).to eq(4)
154
155
  end
155
156
  end
157
+
156
158
  context 'with BETWEEN operator' do
157
159
  it 'should return all items with range key between the provided values' do
158
160
  response = test_client.query(
159
- table_name: table_name,
160
- key_condition_expression: "hash_key_field = :h AND user_id BETWEEN :s AND :e",
161
- expression_attribute_values: {
162
- ":h" => 'foo',
163
- ":s" => 2,
164
- ":e" => 3
165
- }
161
+ table_name: table_name,
162
+ key_condition_expression: "hash_key_field = :h AND user_id BETWEEN :s AND :e",
163
+ expression_attribute_values: {
164
+ ":h" => 'foo',
165
+ ":s" => 2,
166
+ ":e" => 3
167
+ }
166
168
  )
167
169
  expect(response.items.length).to eq(2)
168
170
  expect(response.items.first[:range_key_field]).to eq(2)
169
171
  expect(response.items.last[:range_key_field]).to eq(3)
170
172
  end
171
173
  end
174
+
172
175
  context 'with no operator' do
173
176
  it 'should return all items with range key between the provided values' do
174
177
  response = test_client.query(
175
- table_name: table_name,
176
- key_condition_expression: "hash_key_field = :h",
177
- expression_attribute_values: {
178
- ":h" => 'foo'
179
- }
178
+ table_name: table_name,
179
+ key_condition_expression: "hash_key_field = :h",
180
+ expression_attribute_values: {
181
+ ":h" => 'foo'
182
+ }
180
183
  )
181
184
  expect(response.items.length).to eq(4)
182
185
  expect(response.items.first[:range_key_field]).to eq(1)
183
186
  expect(response.items.last[:range_key_field]).to eq(4)
184
187
  end
185
188
  end
189
+
190
+ context 'with secondary index' do
191
+ before do
192
+ test_client.update_item(table_name: table_name,
193
+ key: {hash_key_field: 'bar', range_key_field: 10},
194
+ attribute_updates: {'abc' => {value: 'abc', action: 'PUT'},
195
+ 'secondary_range_key' => {value: 11, action: 'PUT'}})
196
+ end
197
+
198
+ context 'with LE operator' do
199
+ it 'should return all items with secondary range key less than or equal to the provided value' do
200
+ response = test_client.query(
201
+ table_name: table_name,
202
+ key_condition_expression: "abc = :h AND secondary_range_key <= :e",
203
+ expression_attribute_values: {
204
+ ":h" => 'abc',
205
+ ":e" => 8
206
+ },
207
+ index_name: 'secondary_index'
208
+ )
209
+ expect(response.items.length).to eq(2)
210
+ expect(response.items.first['secondary_range_key']).to eq(7)
211
+ expect(response.items.last['secondary_range_key']).to eq(8)
212
+ end
213
+ end
214
+
215
+ context 'with GE operator' do
216
+ it 'should return all items with secondary range key greater than or equal to the provided value' do
217
+ response = test_client.query(
218
+ table_name: table_name,
219
+ key_condition_expression: "abc = :h AND secondary_range_key >= :s",
220
+ expression_attribute_values: {
221
+ ":h" => 'abc',
222
+ ":s" => 8
223
+ },
224
+ index_name: 'secondary_index'
225
+ )
226
+ expect(response.items.length).to eq(4)
227
+ expect(response.items.first['secondary_range_key']).to eq(8)
228
+ expect(response.items.last['secondary_range_key']).to eq(11)
229
+ end
230
+ end
231
+
232
+ context 'with BETWEEN operator' do
233
+ it 'should return all items with secondary range key between the provided values' do
234
+ response = test_client.query(
235
+ table_name: table_name,
236
+ key_condition_expression: "abc = :h AND secondary_range_key BETWEEN :s AND :e",
237
+ expression_attribute_values: {
238
+ ":h" => 'abc',
239
+ ":s" => 8,
240
+ ":e" => 9
241
+ },
242
+ index_name: 'secondary_index'
243
+ )
244
+ expect(response.items.length).to eq(2)
245
+ expect(response.items.first['secondary_range_key']).to eq(8)
246
+ expect(response.items.last['secondary_range_key']).to eq(9)
247
+ end
248
+ end
249
+
250
+ context 'with no operator' do
251
+ it 'should return all items sorted by their secondary index' do
252
+ response = test_client.query(
253
+ table_name: table_name,
254
+ key_condition_expression: "abc = :h",
255
+ expression_attribute_values: {
256
+ ":h" => 'abc'
257
+ },
258
+ index_name: 'secondary_index'
259
+ )
260
+
261
+ expect(response.items.length).to eq(5)
262
+ expect(response.items.first['secondary_range_key']).to eq(7)
263
+ expect(response.items.last['secondary_range_key']).to eq(11)
264
+ end
265
+ end
266
+
267
+ end
268
+
186
269
  end
187
270
 
188
271
  describe '#batch_write_item' do
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.1.2
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Ward
@@ -15,110 +15,110 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2016-03-03 00:00:00.000000000 Z
18
+ date: 2016-03-08 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activemodel
22
22
  requirement: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3'
27
- - - <
27
+ - - "<"
28
28
  - !ruby/object:Gem::Version
29
29
  version: '5.0'
30
30
  type: :runtime
31
31
  prerelease: false
32
32
  version_requirements: !ruby/object:Gem::Requirement
33
33
  requirements:
34
- - - '>='
34
+ - - ">="
35
35
  - !ruby/object:Gem::Version
36
36
  version: '3'
37
- - - <
37
+ - - "<"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '5.0'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: aws-sdk
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ~>
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '2'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ~>
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '2'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: rspec
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ~>
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '3'
61
61
  type: :development
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ~>
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '3'
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: pry
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - ~>
72
+ - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
75
  type: :development
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - ~>
79
+ - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '0'
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: fuubar
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - ~>
86
+ - - "~>"
87
87
  - !ruby/object:Gem::Version
88
88
  version: '2'
89
89
  type: :development
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ~>
93
+ - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '2'
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: guard-rspec
98
98
  requirement: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - '>='
100
+ - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  type: :development
104
104
  prerelease: false
105
105
  version_requirements: !ruby/object:Gem::Requirement
106
106
  requirements:
107
- - - '>='
107
+ - - ">="
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0'
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: guard-shell
112
112
  requirement: !ruby/object:Gem::Requirement
113
113
  requirements:
114
- - - '>='
114
+ - - ">="
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  type: :development
118
118
  prerelease: false
119
119
  version_requirements: !ruby/object:Gem::Requirement
120
120
  requirements:
121
- - - '>='
121
+ - - ">="
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
124
  description: |-
@@ -130,9 +130,9 @@ executables: []
130
130
  extensions: []
131
131
  extra_rdoc_files: []
132
132
  files:
133
- - .gitignore
134
- - .rspec
135
- - .travis.yml
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
136
  - Gemfile
137
137
  - Gemfile.lock
138
138
  - Guardfile
@@ -170,17 +170,17 @@ require_paths:
170
170
  - lib
171
171
  required_ruby_version: !ruby/object:Gem::Requirement
172
172
  requirements:
173
- - - '>='
173
+ - - ">="
174
174
  - !ruby/object:Gem::Version
175
175
  version: '0'
176
176
  required_rubygems_version: !ruby/object:Gem::Requirement
177
177
  requirements:
178
- - - '>='
178
+ - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
181
  requirements: []
182
182
  rubyforge_project:
183
- rubygems_version: 2.2.2
183
+ rubygems_version: 2.4.6
184
184
  signing_key:
185
185
  specification_version: 4
186
186
  summary: DynamoDB interface