dynamini 2.12.1 → 2.12.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/dynamini.gemspec +1 -1
- data/lib/dynamini.rb +1 -0
- data/lib/dynamini/base.rb +4 -1
- data/lib/dynamini/client_interface.rb +1 -1
- data/lib/dynamini/item_splitter.rb +63 -0
- data/spec/dynamini/item_splitter_spec.rb +40 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c5532b785f0dddd894eb863f17905aab853a124fb3aeb6b046e67b3f0574b2a
|
4
|
+
data.tar.gz: 6b897e7501ec92aab731f94dbf93ee830ed4e0b615103e4b268c38f2796fbb05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11dd2f65ba75d324367f9493fa8a0f2bdfca46526d2647cc738e3c7661303ada53e1aac4409770b393c837a90a892988296e20645374fff4d9d87ad513ac701c
|
7
|
+
data.tar.gz: fdb2715ba459b388db428af4b46a2cb11281fd6efd8a3471f3e9ce13c7c0893ed5424a6b422c20797eec8c57ba01fbf06284edd634abd311b587db4e78b05ab8
|
data/README.md
CHANGED
@@ -259,14 +259,14 @@ page_two = Product.scan(start_key: products_page_one.last_evaluated_key)
|
|
259
259
|
```
|
260
260
|
|
261
261
|
## Secondary Indices
|
262
|
-
To define a secondary index (so that you can .scan or .query it), you can set them at the top of your Dynamini subclass. The index names have to match the names you've set up through the DynamoDB console.
|
262
|
+
To define a secondary index (so that you can .scan or .query it), you can set them at the top of your Dynamini subclass. The index names have to match the names you've set up through the DynamoDB console. You'll need to specify which attribute your index is keyed to, and if your secondary index uses a range key, specify it here as well.
|
263
263
|
|
264
264
|
```ruby
|
265
265
|
class Comment < Dynamini::Base
|
266
266
|
set_hash_key :id
|
267
|
-
set_range_key :comment_date
|
268
|
-
set_secondary_index :score_index
|
269
|
-
set_secondary_index :
|
267
|
+
set_range_key :comment_date # filter comments by date
|
268
|
+
set_secondary_index :score_index, hash_key: :score # lookup comments by score
|
269
|
+
set_secondary_index :user_index, hash_key: :user_id, range_key: :comment_date # lookup comments by user, filtering by date
|
270
270
|
end
|
271
271
|
```
|
272
272
|
For more information on how and why to use secondary indices, see http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html
|
data/dynamini.gemspec
CHANGED
data/lib/dynamini.rb
CHANGED
data/lib/dynamini/base.rb
CHANGED
@@ -130,7 +130,10 @@ module Dynamini
|
|
130
130
|
|
131
131
|
def trigger_save(options = {})
|
132
132
|
generate_timestamps! unless options[:skip_timestamps]
|
133
|
-
|
133
|
+
updates = ItemSplitter.split(attribute_updates)
|
134
|
+
updates.each do |u|
|
135
|
+
save_to_dynamo(u)
|
136
|
+
end
|
134
137
|
clear_changes
|
135
138
|
@new_record = false
|
136
139
|
true
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Dynamini
|
2
|
+
class ItemSplitter
|
3
|
+
|
4
|
+
MAX_SIZE = 380_000
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def split(attribute_updates)
|
9
|
+
unprocessed_au = attribute_updates.map { |k, v| {k => v} }
|
10
|
+
updates = []
|
11
|
+
current_update_size = 0
|
12
|
+
current_update = {}
|
13
|
+
|
14
|
+
while unprocessed_au.length > 0 do
|
15
|
+
size = au_size(unprocessed_au[0])
|
16
|
+
if size > MAX_SIZE
|
17
|
+
part_one, part_two = split_au(unprocessed_au[0])
|
18
|
+
unprocessed_au.shift
|
19
|
+
unprocessed_au.unshift(part_two)
|
20
|
+
unprocessed_au.unshift(part_one)
|
21
|
+
else
|
22
|
+
current_update_size += size
|
23
|
+
if current_update_size > MAX_SIZE
|
24
|
+
updates.push(current_update)
|
25
|
+
current_update_size = 0
|
26
|
+
current_update = {}
|
27
|
+
else
|
28
|
+
current_update_size += size
|
29
|
+
key, value = key_and_value(unprocessed_au[0])
|
30
|
+
current_update[key] = value
|
31
|
+
unprocessed_au.shift
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
updates.push(current_update) unless current_update.empty?
|
37
|
+
updates
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def au_size(au)
|
43
|
+
au.to_s.bytesize
|
44
|
+
end
|
45
|
+
|
46
|
+
def split_au(au)
|
47
|
+
attribute_name = au.keys[0]
|
48
|
+
attribute_action = au.values[0][:action]
|
49
|
+
attribute_value = au.values[0][:value]
|
50
|
+
|
51
|
+
raise "#{attribute_name} is too large to save and is not splittable (not enumerable)." unless attribute_value.is_a?(Enumerable)
|
52
|
+
|
53
|
+
part_one = {attribute_name => {action: attribute_action, value: attribute_value[0..(attribute_value.length / 2) - 1]}}
|
54
|
+
part_two = {attribute_name => {action: "ADD", value: attribute_value[attribute_value.length / 2..-1]}}
|
55
|
+
[part_one, part_two]
|
56
|
+
end
|
57
|
+
|
58
|
+
def key_and_value(au)
|
59
|
+
[au.keys[0], au.values[0]]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dynamini::ItemSplitter do
|
4
|
+
before do
|
5
|
+
stub_const("Dynamini::ItemSplitter::MAX_SIZE", 200)
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'regular update' do
|
9
|
+
it 'returns one update' do
|
10
|
+
input = {foo: {action: 'PUT', value: '123'}, bar: {action: 'ADD', value: [4,5,6]}}
|
11
|
+
expect(Dynamini::ItemSplitter.split(input)).to eq([input])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'large update' do
|
16
|
+
it 'returns multiple updates' do
|
17
|
+
input = {foo: {action: 'PUT', value: '123'}, bar: {action: 'ADD', value: [4,5,6]}, baz: {action: 'PUT', value: 'hello'}}
|
18
|
+
output = Dynamini::ItemSplitter.split(input)
|
19
|
+
expect(output[0]).to eq({foo: {action: 'PUT', value: '123'}, bar: {action: 'ADD', value: [4,5,6]}})
|
20
|
+
expect(output[1]).to eq(baz: {action: 'PUT', value: 'hello'})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'large enumerable attribute' do
|
25
|
+
it 'splits the attribute' do
|
26
|
+
input = {enum: {action: 'PUT', value: Array.new(20, 'hello')}}
|
27
|
+
output = Dynamini::ItemSplitter.split(input)
|
28
|
+
expect(output.length).to eq(2)
|
29
|
+
expect(output[0][:enum][:action]).to eq('PUT')
|
30
|
+
expect(output[1][:enum][:action]).to eq('ADD')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'large non-enumerable attribute' do
|
35
|
+
it 'raises an error' do
|
36
|
+
input = {not_enum: {action: 'PUT', value: Array.new(20, 'hello').to_s}}
|
37
|
+
expect{ Dynamini::ItemSplitter.split(input) }.to raise_error("not_enum is too large to save and is not splittable (not enumerable).")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
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.12.
|
4
|
+
version: 2.12.2
|
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: 2018-
|
18
|
+
date: 2018-07-04 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activemodel
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- lib/dynamini/errors.rb
|
151
151
|
- lib/dynamini/global_id.rb
|
152
152
|
- lib/dynamini/increment.rb
|
153
|
+
- lib/dynamini/item_splitter.rb
|
153
154
|
- lib/dynamini/querying.rb
|
154
155
|
- lib/dynamini/test_client.rb
|
155
156
|
- lib/dynamini/testing.rb
|
@@ -160,6 +161,7 @@ files:
|
|
160
161
|
- spec/dynamini/dirty_spec.rb
|
161
162
|
- spec/dynamini/global_id_spec.rb
|
162
163
|
- spec/dynamini/increment_spec.rb
|
164
|
+
- spec/dynamini/item_splitter_spec.rb
|
163
165
|
- spec/dynamini/querying_spec.rb
|
164
166
|
- spec/dynamini/test_client_spec.rb
|
165
167
|
- spec/dynamini/type_handler_spec.rb
|
@@ -195,6 +197,7 @@ test_files:
|
|
195
197
|
- spec/dynamini/dirty_spec.rb
|
196
198
|
- spec/dynamini/global_id_spec.rb
|
197
199
|
- spec/dynamini/increment_spec.rb
|
200
|
+
- spec/dynamini/item_splitter_spec.rb
|
198
201
|
- spec/dynamini/querying_spec.rb
|
199
202
|
- spec/dynamini/test_client_spec.rb
|
200
203
|
- spec/dynamini/type_handler_spec.rb
|