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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynameek (0.2.1)
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.8)
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 = dynamo_db.tables.create(table_name, read_units, write_units, opts)
38
- puts "Creating table, this may take a few minutes"
39
- while new_table.status == :creating
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
@@ -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.table.items.query(query_hash).map{|item|
87
- @model.item_to_instance(item)
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
+
@@ -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.table.batch_write(
18
- :put => [
19
- attribs
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
- #Rounding errors can be irritating here so if we have the actual item we'll use it's range_val, nope that makes things worse
41
- # range_val = dynamo_item.range_value if !dynamo_item.nil?
42
- # p "TRYING TO DELETE #{[[hash_key, range_val]]}.inspect"
43
- # p "FINDING THAT THING: #{self.class.find(hash_key, self.send(self.class.range_info.field)).inspect}"
44
- # p "VIA BATCH GET #{self.class.table.batch_get(:all, [[hash_key, range_val]]).entries.inspect}"
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
- table.delete
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.is_a?(AWS::DynamoDB::Item) || item.is_a?(AWS::DynamoDB::ItemData) ? item.attributes.to_hash : 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
@@ -1,3 +1,3 @@
1
1
  module Dynameek
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -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
@@ -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 simple])
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.2.2
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-03-20 00:00:00 Z
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