dynameek 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|