dynameek 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +2 -2
- data/lib/dynameek/model/dynamo_db.rb +32 -7
- data/lib/dynameek/model/query.rb +42 -6
- data/lib/dynameek/model/structure.rb +41 -2
- data/lib/dynameek/model.rb +51 -15
- data/lib/dynameek/version.rb +1 -1
- data/test/models/with_index_table.rb +13 -0
- data/test/spec/spec_helper.rb +2 -1
- data/test/spec/with_index_table_spec.rb +47 -0
- metadata +6 -2
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dynameek (0.2.
|
4
|
+
dynameek (0.2.2)
|
5
5
|
aws-sdk
|
6
6
|
|
7
7
|
GEM
|
@@ -22,7 +22,7 @@ GEM
|
|
22
22
|
i18n (0.6.1)
|
23
23
|
json (1.7.7)
|
24
24
|
multi_json (1.7.1)
|
25
|
-
nokogiri (1.5.
|
25
|
+
nokogiri (1.5.9)
|
26
26
|
rack (1.5.2)
|
27
27
|
rack-protection (1.5.0)
|
28
28
|
rack
|
@@ -23,20 +23,38 @@ module Dynameek
|
|
23
23
|
value
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
|
+
def index_table_exists?
|
28
|
+
!index_table.nil? && index_table.exists?
|
29
|
+
end
|
30
|
+
|
27
31
|
def exists?
|
28
32
|
table.exists?
|
29
33
|
end
|
30
34
|
|
31
35
|
def build!
|
32
|
-
return if dynamo_db.tables[table_name].exists?
|
36
|
+
return if dynamo_db.tables[table_name].exists?
|
33
37
|
opts = {:hash_key => { hash_key_info.field => [:datetime, :integer, :float].include?(hash_key_info.type) ? :number : hash_key_info.type }}
|
34
38
|
if range?
|
35
39
|
opts[:range_key] = { range_info.field => [:datetime, :integer, :float].include?(range_info.type) ? :number : range_info.type }
|
36
40
|
end
|
37
|
-
new_table =
|
38
|
-
|
39
|
-
|
41
|
+
new_table = nil
|
42
|
+
idx_table = nil
|
43
|
+
if index_table? && !dynamo_db.tables[index_table_name].exists?
|
44
|
+
dynamo_db.tables.create(
|
45
|
+
table_name+"_INDEX", read_units,
|
46
|
+
write_units, opts)
|
47
|
+
new_table = dynamo_db.tables.create(
|
48
|
+
table_name, read_units, write_units, {
|
49
|
+
hash_key: {
|
50
|
+
hash_key_info.field.to_s+"_"+range_info.field.to_s => :string
|
51
|
+
},
|
52
|
+
range_key: {"dynameek_index" => :number}
|
53
|
+
})
|
54
|
+
else
|
55
|
+
new_table = dynamo_db.tables.create(table_name, read_units, write_units, opts)
|
56
|
+
end
|
57
|
+
while new_table.status == :creating || (!idx_table.nil? && idx_table.status == :creating)
|
40
58
|
sleep 1
|
41
59
|
end
|
42
60
|
end
|
@@ -52,7 +70,14 @@ module Dynameek
|
|
52
70
|
@table.load_schema if !@table.schema_loaded?
|
53
71
|
@table
|
54
72
|
end
|
55
|
-
|
73
|
+
def index_table
|
74
|
+
build!
|
75
|
+
@index_table ||= dynamo_db.tables[index_table_name]
|
76
|
+
if @index_table.exists? && !@index_table.schema_loaded?
|
77
|
+
@index_table.load_schema
|
78
|
+
end
|
79
|
+
@index_table
|
80
|
+
end
|
56
81
|
end
|
57
82
|
end
|
58
|
-
end
|
83
|
+
end
|
data/lib/dynameek/model/query.rb
CHANGED
@@ -28,7 +28,11 @@ module Dynameek
|
|
28
28
|
def all
|
29
29
|
run
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
|
+
def size
|
33
|
+
all.size
|
34
|
+
end
|
35
|
+
|
32
36
|
def each
|
33
37
|
all.each do |item|
|
34
38
|
yield(item)
|
@@ -83,13 +87,45 @@ module Dynameek
|
|
83
87
|
range = range.reduce({}) {|memo, (key, val)| memo[RANGE_QUERY_MAP[key]] = @model.convert_to_dynamodb(@model.range_info.type, val); memo}
|
84
88
|
end
|
85
89
|
query_hash.merge!(range)
|
86
|
-
@model.
|
87
|
-
@model.
|
88
|
-
|
90
|
+
if @model.index_table?
|
91
|
+
rows = @model.index_table.items.query(query_hash).map do |item|
|
92
|
+
item_hsh = @model.aws_item_to_hash(item).reduce({}){|m, (k,v)| m[k.to_sym] = v; m}
|
93
|
+
|
94
|
+
# Need to convert from then back to because of the datetimes being
|
95
|
+
# screwy as number formats
|
96
|
+
hsh_val = @model.convert_from_dynamodb(@model.hash_key_info.type,
|
97
|
+
item_hsh[@model.hash_key_info.field])
|
98
|
+
rng_val = @model.convert_from_dynamodb(@model.range_info.type,
|
99
|
+
item_hsh[@model.range_info.field])
|
100
|
+
hsh_val = @model.convert_to_dynamodb(@model.hash_key_info.type, hsh_val)
|
101
|
+
rng_val = @model.convert_to_dynamodb(@model.range_info.type, rng_val)
|
102
|
+
item_hash_key = [hsh_val, @model.multi_column_join, rng_val].join('')
|
103
|
+
query_hsh_2 = {
|
104
|
+
hash_value: item_hash_key,
|
105
|
+
range_value: (1 .. item_hsh[:current_range_val].to_i),
|
106
|
+
select: :all
|
107
|
+
}
|
108
|
+
# p "QUERYING FOR #{query_hsh_2}"
|
109
|
+
# p "TABLE CONTENTS"
|
110
|
+
# p "--------------"
|
111
|
+
# @model.table.items.each do |i|
|
112
|
+
# p i.inspect
|
113
|
+
# end
|
114
|
+
internal_rows = @model.table.items.query(query_hsh_2).map{|act_item|
|
115
|
+
@model.item_to_instance(act_item)
|
116
|
+
}
|
117
|
+
internal_rows
|
118
|
+
end.flatten
|
119
|
+
rows
|
120
|
+
else
|
121
|
+
@model.table.items.query(query_hash).map{|item|
|
122
|
+
@model.item_to_instance(item)
|
123
|
+
}
|
124
|
+
end
|
89
125
|
end
|
90
126
|
end
|
91
127
|
|
92
|
-
[:query, :where, :all, :each, :each_with_index, :delete].each do |method|
|
128
|
+
[:query, :where, :all, :each, :each_with_index, :size, :delete].each do |method|
|
93
129
|
define_method(method) do |*args|
|
94
130
|
qc = QueryChain.new(self)
|
95
131
|
args = [] if !args
|
@@ -99,4 +135,4 @@ module Dynameek
|
|
99
135
|
|
100
136
|
end
|
101
137
|
end
|
102
|
-
end
|
138
|
+
end
|
@@ -24,6 +24,11 @@ module Dynameek
|
|
24
24
|
@range
|
25
25
|
end
|
26
26
|
|
27
|
+
def index_table?
|
28
|
+
@uses_index_table|| false
|
29
|
+
end
|
30
|
+
|
31
|
+
|
27
32
|
def range?
|
28
33
|
!range_info.field.nil?
|
29
34
|
end
|
@@ -74,7 +79,7 @@ module Dynameek
|
|
74
79
|
hash_key_info.type = fields[fieldname]
|
75
80
|
define_method(:hash_key) { read_attribute(fieldname) }
|
76
81
|
end
|
77
|
-
|
82
|
+
|
78
83
|
def multi_column_hash_key fieldnames
|
79
84
|
fieldnames = fieldnames.map(&:to_sym)
|
80
85
|
fieldnames.each{|f| check_field(f)}
|
@@ -97,6 +102,40 @@ module Dynameek
|
|
97
102
|
check_field(fieldname)
|
98
103
|
range_info.field = fieldname
|
99
104
|
range_info.type = fields[fieldname]
|
105
|
+
define_method(:range) do
|
106
|
+
attributes[self.class.range_info.field]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def aws_item_to_hash(item)
|
111
|
+
(item.is_a?(AWS::DynamoDB::Item) || item.is_a?(AWS::DynamoDB::ItemData) ?
|
112
|
+
item.attributes.to_hash :
|
113
|
+
item
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def current_range(key, range_val)
|
118
|
+
return nil if !index_table?
|
119
|
+
key.join!(multi_column_join) if key.is_a?(Array)
|
120
|
+
item = index_table.items.query(
|
121
|
+
{
|
122
|
+
hash_value: key,
|
123
|
+
select: :all,
|
124
|
+
range_value: range_val
|
125
|
+
}).first
|
126
|
+
return 0 if item.nil?
|
127
|
+
item_hsh = aws_item_to_hash(item)
|
128
|
+
item_hsh['current_range_val'].to_i
|
129
|
+
end
|
130
|
+
|
131
|
+
def use_index_table
|
132
|
+
define_method(:act_hash_key) do
|
133
|
+
[self.class.convert_to_dynamodb(self.class.hash_key_info.type, hash_key),
|
134
|
+
self.class.multi_column_join,
|
135
|
+
self.class.convert_to_dynamodb(self.class.range_info.type, range)].join('')
|
136
|
+
end
|
137
|
+
field :dynameek_index, :number
|
138
|
+
@uses_index_table = true
|
100
139
|
end
|
101
140
|
|
102
141
|
def check_field(fieldname)
|
@@ -107,4 +146,4 @@ module Dynameek
|
|
107
146
|
end
|
108
147
|
end
|
109
148
|
|
110
|
-
|
149
|
+
|
data/lib/dynameek/model.rb
CHANGED
@@ -14,11 +14,38 @@ module Dynameek
|
|
14
14
|
memo
|
15
15
|
end
|
16
16
|
self.class.before_save_callbacks.each{|method| self.send method}
|
17
|
-
self.class.
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
if self.class.index_table?
|
18
|
+
rng = self.class.convert_to_dynamodb(
|
19
|
+
self.class.range_info.type,
|
20
|
+
attributes[self.class.range_info.field]
|
21
|
+
)
|
22
|
+
curr_range = self.class.current_range(hash_key, rng) + 1
|
23
|
+
self.class.index_table.batch_write(
|
24
|
+
:put => [
|
25
|
+
{
|
26
|
+
self.class.hash_key_info.field => attribs[self.class.hash_key_info.field],
|
27
|
+
self.class.range_info.field => attribs[self.class.range_info.field],
|
28
|
+
current_range_val: curr_range
|
29
|
+
}
|
30
|
+
]
|
31
|
+
)
|
32
|
+
attribs[self.class.hash_key_info.field.to_s+"_"+self.class.range_info.field.to_s] =
|
33
|
+
attribs[self.class.hash_key_info.field].to_s +
|
34
|
+
self.class.multi_column_join +
|
35
|
+
attribs[self.class.range_info.field].to_s
|
36
|
+
attribs[:dynameek_index] = curr_range
|
37
|
+
self.class.table.batch_write(
|
38
|
+
:put => [
|
39
|
+
attribs
|
40
|
+
]
|
41
|
+
)
|
42
|
+
else
|
43
|
+
self.class.table.batch_write(
|
44
|
+
:put => [
|
45
|
+
attribs
|
46
|
+
]
|
47
|
+
)
|
48
|
+
end
|
22
49
|
self
|
23
50
|
end
|
24
51
|
|
@@ -37,12 +64,11 @@ module Dynameek
|
|
37
64
|
def delete
|
38
65
|
if self.class.range?
|
39
66
|
range_val = self.class.convert_to_dynamodb(self.class.range_info.type, self.send(self.class.range_info.field))
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
self.class.table.batch_delete([[hash_key, range_val]])
|
67
|
+
if self.class.index_table?
|
68
|
+
self.class.table.batch_delete([[act_hash_key, attributes[:dynameek_index]]])
|
69
|
+
else
|
70
|
+
self.class.table.batch_delete([[hash_key, range_val]])
|
71
|
+
end
|
46
72
|
else
|
47
73
|
self.class.table.batch_delete([hash_key])
|
48
74
|
end
|
@@ -76,11 +102,17 @@ module Dynameek
|
|
76
102
|
end
|
77
103
|
|
78
104
|
def delete_table
|
79
|
-
|
105
|
+
|
106
|
+
index_table.delete if dynamo_db.tables[index_table_name].exists?
|
107
|
+
table.delete if dynamo_db.tables[table_name].exists?
|
108
|
+
while dynamo_db.tables[table_name].exists? &&
|
109
|
+
dynamo_db.tables[index_table_name].exists?
|
110
|
+
sleep 1
|
111
|
+
end
|
80
112
|
end
|
81
113
|
|
82
114
|
def item_to_instance(item)
|
83
|
-
item_hsh = (item
|
115
|
+
item_hsh = aws_item_to_hash(item)
|
84
116
|
instance = self.new
|
85
117
|
fields.each do |field, type|
|
86
118
|
next if multi_column_hash_key? && field == hash_key_info.field
|
@@ -111,7 +143,11 @@ module Dynameek
|
|
111
143
|
def table_name
|
112
144
|
self.to_s
|
113
145
|
end
|
114
|
-
|
146
|
+
|
147
|
+
def index_table_name
|
148
|
+
table_name + "_INDEX"
|
149
|
+
end
|
150
|
+
|
115
151
|
end
|
116
152
|
end
|
117
|
-
end
|
153
|
+
end
|
data/lib/dynameek/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
class WithIndexTable
|
2
|
+
include Dynameek::Model
|
3
|
+
|
4
|
+
field :client_id, :integer
|
5
|
+
field :channel_id, :string
|
6
|
+
field :advert_id, :integer
|
7
|
+
field :time, :datetime
|
8
|
+
|
9
|
+
multi_column_hash_key [:client_id, :channel_id]
|
10
|
+
range :time
|
11
|
+
|
12
|
+
use_index_table
|
13
|
+
end
|
data/test/spec/spec_helper.rb
CHANGED
@@ -10,4 +10,5 @@ AWS.config(:use_ssl => false,
|
|
10
10
|
:secret_access_key => "xxx")
|
11
11
|
|
12
12
|
require File.join(File.dirname(__FILE__), *%w[.. models conversion])
|
13
|
-
require File.join(File.dirname(__FILE__), *%w[.. models
|
13
|
+
require File.join(File.dirname(__FILE__), *%w[.. models with_index_table])
|
14
|
+
require File.join(File.dirname(__FILE__), *%w[.. models simple])
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[spec_helper])
|
2
|
+
|
3
|
+
describe WithIndexTable do
|
4
|
+
before(:all) do
|
5
|
+
WithIndexTable.delete_table
|
6
|
+
end
|
7
|
+
before(:each) do
|
8
|
+
WithIndexTable.query([1, "google"]).delete
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow you to create a new row" do
|
12
|
+
con = nil
|
13
|
+
lambda{
|
14
|
+
con = WithIndexTable.create(:client_id => 1, :channel_id => "google", :advert_id=> 1, :time => DateTime.now)
|
15
|
+
}.should_not raise_error
|
16
|
+
con.hash_key.should == "1|google"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should allow you to have multiple rows for the same hash and range" do
|
20
|
+
row1, row2 = nil
|
21
|
+
time = DateTime.now
|
22
|
+
lambda{
|
23
|
+
row1 = WithIndexTable.create(:client_id => 1, :channel_id => "google",
|
24
|
+
:advert_id=> 1, :time => time)
|
25
|
+
row2 = WithIndexTable.create(:client_id => 1, :channel_id => "google",
|
26
|
+
:advert_id=> 2, :time => time)
|
27
|
+
}.should_not raise_error
|
28
|
+
row1.hash_key.should == "1|google"
|
29
|
+
row2.hash_key.should == "1|google"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should allow you to query for the some hash and range" do
|
33
|
+
row1, row2 = nil
|
34
|
+
time = DateTime.now
|
35
|
+
lambda{
|
36
|
+
row1 = WithIndexTable.create(:client_id => 1, :channel_id => "google",
|
37
|
+
:advert_id=> 1, :time => time)
|
38
|
+
row2 = WithIndexTable.create(:client_id => 1, :channel_id => "google",
|
39
|
+
:advert_id=> 2, :time => time)
|
40
|
+
}.should_not raise_error
|
41
|
+
|
42
|
+
query = WithIndexTable.query([1, "google"]).where(time, :eq)
|
43
|
+
query.size.should == 2
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: dynameek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.3.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Max Dupenois
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-04-02 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: aws-sdk
|
@@ -69,9 +69,11 @@ files:
|
|
69
69
|
- lib/dynameek/version.rb
|
70
70
|
- test/models/conversion.rb
|
71
71
|
- test/models/simple.rb
|
72
|
+
- test/models/with_index_table.rb
|
72
73
|
- test/spec/conversion_spec.rb
|
73
74
|
- test/spec/simple_spec.rb
|
74
75
|
- test/spec/spec_helper.rb
|
76
|
+
- test/spec/with_index_table_spec.rb
|
75
77
|
homepage: http://github.com/maxdupenois/dynameek
|
76
78
|
licenses: []
|
77
79
|
|
@@ -102,6 +104,8 @@ summary: Dynameek - A dynamodb model
|
|
102
104
|
test_files:
|
103
105
|
- test/models/conversion.rb
|
104
106
|
- test/models/simple.rb
|
107
|
+
- test/models/with_index_table.rb
|
105
108
|
- test/spec/conversion_spec.rb
|
106
109
|
- test/spec/simple_spec.rb
|
107
110
|
- test/spec/spec_helper.rb
|
111
|
+
- test/spec/with_index_table_spec.rb
|