dynameek 0.1.1a → 0.2.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 +4 -0
- data/Gemfile.lock +50 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/dynameek.gemspec +23 -0
- data/lib/dynameek/model/dynamo_db.rb +5 -4
- data/lib/dynameek/model/query.rb +22 -7
- data/lib/dynameek/model/structure.rb +76 -58
- data/lib/dynameek/model.rb +115 -78
- data/lib/dynameek/version.rb +3 -0
- data/lib/dynameek.rb +3 -0
- data/test/models/conversion.rb +13 -0
- data/test/models/simple.rb +13 -0
- data/test/spec/conversion_spec.rb +58 -0
- data/test/spec/simple_spec.rb +26 -0
- data/test/spec/spec_helper.rb +13 -0
- metadata +61 -12
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
dynameek (0.2.1)
|
5
|
+
aws-sdk
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (3.2.13)
|
11
|
+
i18n (= 0.6.1)
|
12
|
+
multi_json (~> 1.0)
|
13
|
+
aws-sdk (1.8.5)
|
14
|
+
json (~> 1.4)
|
15
|
+
nokogiri (>= 1.4.4)
|
16
|
+
uuidtools (~> 2.1)
|
17
|
+
diff-lcs (1.2.1)
|
18
|
+
fake_dynamo (0.1.1)
|
19
|
+
activesupport
|
20
|
+
json
|
21
|
+
sinatra
|
22
|
+
i18n (0.6.1)
|
23
|
+
json (1.7.7)
|
24
|
+
multi_json (1.7.1)
|
25
|
+
nokogiri (1.5.8)
|
26
|
+
rack (1.5.2)
|
27
|
+
rack-protection (1.5.0)
|
28
|
+
rack
|
29
|
+
rspec (2.13.0)
|
30
|
+
rspec-core (~> 2.13.0)
|
31
|
+
rspec-expectations (~> 2.13.0)
|
32
|
+
rspec-mocks (~> 2.13.0)
|
33
|
+
rspec-core (2.13.1)
|
34
|
+
rspec-expectations (2.13.0)
|
35
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
36
|
+
rspec-mocks (2.13.0)
|
37
|
+
sinatra (1.4.1)
|
38
|
+
rack (~> 1.5, >= 1.5.2)
|
39
|
+
rack-protection (~> 1.4)
|
40
|
+
tilt (~> 1.3, >= 1.3.4)
|
41
|
+
tilt (1.3.6)
|
42
|
+
uuidtools (2.1.3)
|
43
|
+
|
44
|
+
PLATFORMS
|
45
|
+
ruby
|
46
|
+
|
47
|
+
DEPENDENCIES
|
48
|
+
dynameek!
|
49
|
+
fake_dynamo
|
50
|
+
rspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Max Dupenois
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Dynameek
|
2
|
+
A very lightweight ORMish model thing for amazon's dynamo db, after initialising the aws-sdk with something like:
|
3
|
+
|
4
|
+
amazon_config_path = File.join(File.dirname(__FILE__), *%w[.. config amazon.config.yml])
|
5
|
+
amazon_config = YAML.load(File.read(amazon_config_path))
|
6
|
+
AWS.config(amazon_config[ENVIRONMENT])
|
7
|
+
|
8
|
+
##Models
|
9
|
+
|
10
|
+
You can create table models with this kind of syntax:
|
11
|
+
|
12
|
+
class Conversion < Dynameek::Model
|
13
|
+
|
14
|
+
field :client_id, :integer
|
15
|
+
field :channel_id, :string
|
16
|
+
field :goal_name, :string
|
17
|
+
field :time, :datetime
|
18
|
+
|
19
|
+
multi_column_hash_key [:client_id, :channel_id]
|
20
|
+
range :time
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
Creation of the models is as you'd expect
|
25
|
+
|
26
|
+
con = Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "Some Goal", :time => DateTime.now)
|
27
|
+
|
28
|
+
The models can be edited like normal (_no update\_attributes yet though_)
|
29
|
+
|
30
|
+
con.goal_name="hello"
|
31
|
+
con.save
|
32
|
+
|
33
|
+
These models can be queried by find and query, although this is still undergoing some refactoring at the moment it currently looks something like this:
|
34
|
+
|
35
|
+
Conversion.find([1, "google"], DateTime.new([Some existing datetime]))
|
36
|
+
|
37
|
+
Conversion.query(["1", "google"]).where(DateTime.now, :lt).where(DateTime.now - 10, :gte).all
|
38
|
+
|
39
|
+
NB. The where clauses are only for referencing the range part of the composite hash key, there is currently no way to search by
|
40
|
+
hash contents as that felt like it was against the point of a document store.
|
41
|
+
|
42
|
+
### Disclaimery Bit
|
43
|
+
|
44
|
+
Delete is not currently supported because I don't need it. The gem is dynameek (unsurprisingly) but I wouldn't use it yet, far better
|
45
|
+
to clone down the project and modify it for your own use.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/dynameek.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dynameek/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "dynameek"
|
8
|
+
gem.version = Dynameek::VERSION
|
9
|
+
gem.authors = ["Max Dupenois"]
|
10
|
+
gem.email = ["max.dupenois@forward.co.uk"]
|
11
|
+
gem.description = %q{A very lightweight model for DynamoDB tables in, certainly not in a finished state}
|
12
|
+
gem.summary = %q{Dynameek - A dynamodb model}
|
13
|
+
gem.homepage = "http://github.com/maxdupenois/dynameek"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency("aws-sdk")
|
21
|
+
gem.add_development_dependency("rspec")
|
22
|
+
gem.add_development_dependency("fake_dynamo")
|
23
|
+
end
|
@@ -30,10 +30,11 @@ module Dynameek
|
|
30
30
|
|
31
31
|
def build!
|
32
32
|
return if dynamo_db.tables[table_name].exists?
|
33
|
-
|
34
|
-
|
35
|
-
:range_key
|
36
|
-
|
33
|
+
opts = {:hash_key => { hash_key_info.field => [:datetime, :integer, :float].include?(hash_key_info.type) ? :number : hash_key_info.type }}
|
34
|
+
if range?
|
35
|
+
opts[:range_key] = { range_info.field => [:datetime, :integer, :float].include?(range_info.type) ? :number : range_info.type }
|
36
|
+
end
|
37
|
+
new_table = dynamo_db.tables.create(table_name, read_units, write_units, opts)
|
37
38
|
puts "Creating table, this may take a few minutes"
|
38
39
|
while new_table.status == :creating
|
39
40
|
sleep 1
|
data/lib/dynameek/model/query.rb
CHANGED
@@ -17,21 +17,29 @@ module Dynameek
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def where(value, op=:eq)
|
20
|
-
raise Exception("Op #{op.to_s} not recognised") if(!@range.keys.include?(op))
|
20
|
+
raise Exception.new("Op #{op.to_s} not recognised") if(!@range.keys.include?(op))
|
21
21
|
@range[op] = value
|
22
22
|
self
|
23
23
|
end
|
24
24
|
|
25
|
+
def delete
|
26
|
+
each(&:delete)
|
27
|
+
end
|
28
|
+
|
25
29
|
def all
|
26
30
|
run
|
27
31
|
end
|
28
32
|
|
29
33
|
def each
|
30
|
-
all.each
|
34
|
+
all.each do |item|
|
35
|
+
yield(item)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
def each_with_index
|
34
|
-
all.each_with_index
|
40
|
+
all.each_with_index do |item, index|
|
41
|
+
yield(item, index)
|
42
|
+
end
|
35
43
|
end
|
36
44
|
|
37
45
|
RANGE_QUERY_MAP =
|
@@ -51,22 +59,29 @@ module Dynameek
|
|
51
59
|
hash_key = @hash_key
|
52
60
|
hash_key = hash_key.join(@model.multi_column_join) if(hash_key.is_a?(Array))
|
53
61
|
|
54
|
-
query_hash = {:hash_value => hash_key}
|
62
|
+
query_hash = {:hash_value => hash_key, :select => :all}
|
55
63
|
|
56
64
|
query_hash = @range.reduce(query_hash) do |hsh, (key, val)|
|
57
65
|
if(!val.nil?)
|
58
|
-
hsh[RANGE_QUERY_MAP[key]] = @model.convert_to_dynamodb(@model.
|
66
|
+
hsh[RANGE_QUERY_MAP[key]] = @model.convert_to_dynamodb(@model.range_info.type, val)
|
59
67
|
end
|
60
68
|
hsh
|
61
69
|
end
|
62
|
-
|
70
|
+
# p query_hash
|
71
|
+
# "CONTENTS"
|
72
|
+
# @model.table.items.each{|item| p item.inspect}
|
73
|
+
# p "_________"
|
74
|
+
@model.table.items.query(query_hash).map{|item|
|
75
|
+
# p item.inspect
|
76
|
+
@model.item_to_instance(item)
|
77
|
+
}
|
63
78
|
|
64
79
|
end
|
65
80
|
|
66
81
|
|
67
82
|
end
|
68
83
|
|
69
|
-
[:query, :where, :all, :each, :each_with_index].each do |method|
|
84
|
+
[:query, :where, :all, :each, :each_with_index, :delete].each do |method|
|
70
85
|
define_method(method) do |*args|
|
71
86
|
qc = QueryChain.new(self)
|
72
87
|
args = [] if !args
|
@@ -1,26 +1,70 @@
|
|
1
1
|
module Dynameek
|
2
2
|
module Model
|
3
3
|
module Structure
|
4
|
-
|
5
|
-
@fields = {}
|
6
|
-
@hash_key = OpenStruct.new
|
7
|
-
@hash_key.field = nil
|
8
|
-
@hash_key.type = nil
|
9
|
-
|
10
|
-
@range = OpenStruct.new
|
11
|
-
@range.field = nil
|
12
|
-
@range.type = nil
|
13
|
-
|
14
|
-
@read_write = [10, 5]
|
4
|
+
|
15
5
|
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
def fields
|
7
|
+
@fields ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def hash_key_info
|
11
|
+
if(@hash_key.nil?)
|
12
|
+
@hash_key = OpenStruct.new
|
13
|
+
@hash_key.field = nil
|
14
|
+
@hash_key.type = nil
|
15
|
+
end
|
16
|
+
@hash_key
|
17
|
+
end
|
18
|
+
|
19
|
+
def range_info
|
20
|
+
if(@range.nil?)
|
21
|
+
@range = OpenStruct.new
|
22
|
+
@range.field = nil
|
23
|
+
@range.type = nil
|
24
|
+
end
|
25
|
+
@range
|
26
|
+
end
|
27
|
+
|
28
|
+
def range?
|
29
|
+
!range_info.field.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def read_units
|
33
|
+
@read_write ||= [10, 5]
|
34
|
+
@read_write[0]
|
35
|
+
end
|
36
|
+
|
37
|
+
def write_units
|
38
|
+
@read_write ||= [10, 5]
|
39
|
+
@read_write[1]
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_write(vals)
|
43
|
+
@read_write = vals
|
44
|
+
end
|
45
|
+
|
46
|
+
def multi_column_hash_key_fields
|
47
|
+
@multi_column_hash_key_fields ||= []
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
def multi_column_join
|
53
|
+
@multi_column_join ||= "|"
|
54
|
+
end
|
55
|
+
|
56
|
+
def multi_column_join=(join)
|
57
|
+
@multi_column_join =join
|
58
|
+
end
|
59
|
+
|
60
|
+
def multi_column_hash_key?
|
61
|
+
!multi_column_hash_key_fields.empty?
|
62
|
+
end
|
19
63
|
|
20
64
|
def field fieldname, type
|
21
65
|
fieldname = fieldname.to_sym
|
22
66
|
type = type.to_sym
|
23
|
-
|
67
|
+
fields[fieldname] = type
|
24
68
|
define_method(fieldname.to_s) { read_attribute(fieldname) }
|
25
69
|
define_method("#{fieldname.to_s}?") { !read_attribute(fieldname).nil? }
|
26
70
|
define_method("#{fieldname.to_s}=") {|value| write_attribute(fieldname, value) }
|
@@ -31,74 +75,48 @@ module Dynameek
|
|
31
75
|
def hash_key fieldname
|
32
76
|
fieldname = fieldname.to_sym
|
33
77
|
check_field(fieldname)
|
34
|
-
|
35
|
-
|
78
|
+
hash_key_info.field = fieldname
|
79
|
+
hash_key_info.type = fields[fieldname]
|
36
80
|
define_method(:hash_key) { read_attribute(fieldname) }
|
37
81
|
end
|
38
82
|
|
39
|
-
|
40
|
-
@multi_column_join
|
41
|
-
end
|
42
|
-
def multi_column_join=(join)
|
43
|
-
@multi_column_join = join
|
44
|
-
end
|
45
|
-
def multi_column_hash_key?
|
46
|
-
!@multi_column_hash_key_fields.empty?
|
47
|
-
end
|
83
|
+
|
48
84
|
|
49
85
|
|
50
86
|
|
51
87
|
def multi_column_hash_key fieldnames
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
fieldname = fieldnames.map(&:to_s).join("_").
|
56
|
-
|
88
|
+
fieldnames = fieldnames.map(&:to_sym)
|
89
|
+
fieldnames.each{|f| check_field(f)}
|
90
|
+
fieldnames.each {|f| multi_column_hash_key_fields << f}
|
91
|
+
fieldname = fieldnames.map(&:to_s).join("_").to_sym
|
92
|
+
fields[fieldname] = :string
|
57
93
|
define_method(:hash_key) do
|
58
|
-
|
94
|
+
self.class.multi_column_hash_key_fields.reduce([]) do |memo, field|
|
59
95
|
memo << attributes[field]
|
60
96
|
memo
|
61
|
-
end.join(
|
97
|
+
end.join(self.class.multi_column_join)
|
62
98
|
end
|
63
99
|
alias_method fieldname, :hash_key
|
64
|
-
|
65
|
-
|
100
|
+
hash_key_info.field = fieldname
|
101
|
+
hash_key_info.type = :string
|
66
102
|
end
|
67
103
|
|
68
|
-
def hash_key_field
|
69
|
-
@hash_key
|
70
|
-
end
|
71
104
|
|
72
105
|
|
73
106
|
def range fieldname
|
74
107
|
fieldname = fieldname.to_sym
|
75
108
|
check_field(fieldname)
|
76
|
-
|
77
|
-
|
109
|
+
range_info.field = fieldname
|
110
|
+
range_info.type = fields[fieldname]
|
78
111
|
end
|
79
112
|
|
80
113
|
|
81
|
-
def read_write read, write
|
82
|
-
@read_write = [read, write]
|
83
|
-
end
|
84
114
|
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
def write_units
|
89
|
-
@read_write[1]
|
90
|
-
end
|
91
|
-
|
92
|
-
def fields
|
93
|
-
@fields
|
94
|
-
end
|
95
|
-
|
96
|
-
def range_field
|
97
|
-
@range.field.nil? ? nil : @range
|
98
|
-
end
|
115
|
+
|
116
|
+
|
99
117
|
|
100
118
|
def check_field(fieldname)
|
101
|
-
raise
|
119
|
+
raise ("#{fieldname} is not a recognised field") if fields[fieldname].nil?
|
102
120
|
end
|
103
121
|
|
104
122
|
end
|
data/lib/dynameek/model.rb
CHANGED
@@ -1,95 +1,132 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
attr_accessor :attributes
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def self.find(hash_key, range_val=nil)
|
14
|
-
raise Exception("This has a composite hash with a range, the range val is required") if(range_val.nil? && !range_field.field.nil?)
|
15
|
-
#multicolumn
|
16
|
-
hash_key = hash_key.join(multi_column_join) if(hash_key.is_a?(Array))
|
17
|
-
|
18
|
-
items = if !range_val.nil?
|
19
|
-
range_val = convert_to_dynamodb(range_field.type, range_val)
|
20
|
-
table.batch_get(:all, [[hash_key, range_val]])
|
21
|
-
else
|
22
|
-
table.batch_get(:all, [hash_key])
|
1
|
+
module Dynameek
|
2
|
+
module Model
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
23
5
|
end
|
24
|
-
# p items.methods - Object.new.methods
|
25
|
-
return nil if(items.entries.size == 0)
|
26
|
-
item_to_instance(items.first)
|
27
6
|
|
28
|
-
|
29
|
-
|
7
|
+
def save
|
8
|
+
attribs = self.class.fields.reduce({}) do |memo, (field, type)|
|
9
|
+
#Always call the read method in case it has been overwritten
|
10
|
+
#Note that this is required for the multicolumn key
|
11
|
+
val = self.send field
|
12
|
+
val = self.class.convert_to_dynamodb(type, val)
|
13
|
+
memo[field] = val
|
14
|
+
memo
|
15
|
+
end
|
16
|
+
self.class.before_save_callbacks.each{|method| self.send method}
|
17
|
+
self.class.table.batch_write(
|
18
|
+
:put => [
|
19
|
+
attribs
|
20
|
+
]
|
21
|
+
)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
def attributes
|
28
|
+
@attributes ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_attribute(fieldname)
|
32
|
+
attributes[fieldname]
|
33
|
+
end
|
30
34
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
def write_attribute(fieldname, value)
|
36
|
+
attributes[fieldname] = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete
|
40
|
+
if self.class.range?
|
41
|
+
range_val = self.class.convert_to_dynamodb(self.class.range_info.type, self.send(self.class.range_info.field))
|
42
|
+
#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
|
43
|
+
# range_val = dynamo_item.range_value if !dynamo_item.nil?
|
44
|
+
# p "TRYING TO DELETE #{[[hash_key, range_val]]}.inspect"
|
45
|
+
# p "FINDING THAT THING: #{self.class.find(hash_key, self.send(self.class.range_info.field)).inspect}"
|
46
|
+
# p "VIA BATCH GET #{self.class.table.batch_get(:all, [[hash_key, range_val]]).entries.inspect}"
|
47
|
+
self.class.table.batch_delete([[hash_key, range_val]])
|
48
|
+
else
|
49
|
+
self.class.table.batch_delete([hash_key])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def dynamo_item
|
54
|
+
@dynamo_item ||= nil
|
38
55
|
end
|
39
|
-
instance
|
40
|
-
end
|
41
|
-
|
42
56
|
|
43
|
-
|
44
|
-
|
45
|
-
instance = self.new
|
46
|
-
attrib.each do |key, val|
|
47
|
-
instance.send "#{key.to_s}=", val
|
57
|
+
def dynamo_item=(item)
|
58
|
+
@dynamo_item = item
|
48
59
|
end
|
49
|
-
instance.save
|
50
|
-
end
|
51
60
|
|
52
|
-
|
53
|
-
|
54
|
-
|
61
|
+
module ClassMethods
|
62
|
+
include Dynameek::Model::DynamoDb
|
63
|
+
include Dynameek::Model::Structure
|
64
|
+
include Dynameek::Model::Query
|
65
|
+
|
55
66
|
|
56
|
-
def self.before_save method
|
57
|
-
before_save_callbacks << method.to_sym
|
58
|
-
end
|
59
67
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
val = self.class.convert_to_dynamodb(type, val)
|
66
|
-
memo[field] = val
|
67
|
-
memo
|
68
|
-
end
|
69
|
-
self.class.before_save_callbacks.each{|method| self.send method}
|
70
|
-
self.class.table.batch_write(
|
71
|
-
:put => [
|
72
|
-
attribs
|
73
|
-
]
|
74
|
-
)
|
75
|
-
self
|
76
|
-
end
|
68
|
+
|
69
|
+
def find(hash_key, range_val=nil)
|
70
|
+
raise Exception("This has a composite hash with a range, the range val is required") if(range_val.nil? && range?)
|
71
|
+
#multicolumn
|
72
|
+
hash_key = hash_key.join(multi_column_join) if(hash_key.is_a?(Array))
|
77
73
|
|
74
|
+
items = if range?
|
75
|
+
range_val = convert_to_dynamodb(range_info.type, range_val)
|
76
|
+
table.batch_get(:all, [[hash_key, range_val]])
|
77
|
+
else
|
78
|
+
table.batch_get(:all, [hash_key])
|
79
|
+
end
|
80
|
+
# p items.methods - Object.new.methods
|
81
|
+
return nil if(items.entries.size == 0)
|
82
|
+
item_to_instance(items.first)
|
83
|
+
|
84
|
+
end
|
78
85
|
|
79
|
-
|
80
|
-
|
81
|
-
|
86
|
+
def delete_table
|
87
|
+
table.delete
|
88
|
+
end
|
89
|
+
|
82
90
|
|
83
|
-
|
91
|
+
def item_to_instance(item)
|
92
|
+
item_hsh = (item.is_a?(AWS::DynamoDB::Item) || item.is_a?(AWS::DynamoDB::ItemData) ? item.attributes.to_hash : item)
|
93
|
+
instance = self.new
|
94
|
+
fields.each do |field, type|
|
95
|
+
next if multi_column_hash_key? && field == hash_key_info.field
|
96
|
+
instance.send "#{field.to_s}=", convert_from_dynamodb(type, item_hsh[field.to_s])
|
97
|
+
end
|
98
|
+
if item.is_a?(AWS::DynamoDB::Item) || item.is_a?(AWS::DynamoDB::ItemData)
|
99
|
+
instance.dynamo_item = item.is_a?(AWS::DynamoDB::ItemData) ? item.item : item
|
100
|
+
end
|
101
|
+
instance
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
def create(attrib)
|
107
|
+
instance = self.new
|
108
|
+
attrib.each do |key, val|
|
109
|
+
instance.send "#{key.to_s}=", val
|
110
|
+
end
|
111
|
+
instance.save
|
112
|
+
end
|
113
|
+
|
114
|
+
def before_save_callbacks
|
115
|
+
@before_save_callbacks ||= Set.new
|
116
|
+
end
|
117
|
+
|
118
|
+
def before_save method
|
119
|
+
before_save_callbacks << method.to_sym
|
120
|
+
end
|
84
121
|
|
85
|
-
def read_attribute(fieldname)
|
86
|
-
@attributes[fieldname]
|
87
|
-
end
|
88
122
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
123
|
+
|
124
|
+
def table_name
|
125
|
+
self.to_s
|
126
|
+
end
|
127
|
+
|
93
128
|
|
94
129
|
|
130
|
+
end
|
131
|
+
end
|
95
132
|
end
|
data/lib/dynameek.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[spec_helper])
|
2
|
+
|
3
|
+
describe Conversion do
|
4
|
+
before(:all) do
|
5
|
+
Conversion.delete_table
|
6
|
+
end
|
7
|
+
before(:each) do
|
8
|
+
Conversion.query([1, "google"]).delete
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow you to create a new conversion" do
|
12
|
+
con = nil
|
13
|
+
lambda{
|
14
|
+
con = Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "new conversion", :time => DateTime.now)
|
15
|
+
}.should_not raise_error
|
16
|
+
con.hash_key.should == "1|google"
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
it "should allow you to find a conversion" do
|
21
|
+
now = DateTime.now
|
22
|
+
Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "find conversion", :time => now)
|
23
|
+
con = Conversion.find([1, "google"], now)
|
24
|
+
con.should_not be_nil
|
25
|
+
con.hash_key.should == "1|google"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
it "should allow you to delete a range" do
|
30
|
+
now = DateTime.now
|
31
|
+
Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "delete conversion", :time => now)
|
32
|
+
Conversion.query([1, "google"]).all.size.should == 1
|
33
|
+
Conversion.query([1, "google"]).delete
|
34
|
+
Conversion.query([1, "google"]).all.size.should == 0
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should allow you to find a query a conversion" do
|
38
|
+
now = DateTime.now
|
39
|
+
Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "Some Goal", :time => now)
|
40
|
+
cons = Conversion.query([1, "google"]).all
|
41
|
+
cons.size.should == 1
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should allow you to find a query a conversion within a range" do
|
45
|
+
now = DateTime.now
|
46
|
+
Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "Some Goal", :time => now - 3)
|
47
|
+
Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "Some Goal", :time => now - 2)
|
48
|
+
Conversion.create(:client_id => 1, :channel_id => "google", :goal_name=> "Some Goal", :time => now - 1)
|
49
|
+
cons = Conversion.query([1, "google"]).where(now, :lt).all
|
50
|
+
cons.size.should == 3
|
51
|
+
cons = Conversion.query([1, "google"]).where(now - 4, :gt).all
|
52
|
+
cons.size.should == 3
|
53
|
+
# cons = Conversion.query([1, "google"]).where(now - 2, :gte).where(now, :lt).all
|
54
|
+
# cons.size.should == 2 Not supported on fake_dynamo, seems to work fine on prod
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[spec_helper])
|
2
|
+
|
3
|
+
describe Simple do
|
4
|
+
before(:all) do
|
5
|
+
Simple.delete_table
|
6
|
+
end
|
7
|
+
before(:each) do
|
8
|
+
simple = Simple.find(1)
|
9
|
+
simple.delete if(!simple.nil?)
|
10
|
+
end
|
11
|
+
it "should allow you to create a new simple" do
|
12
|
+
con = nil
|
13
|
+
lambda{
|
14
|
+
con = Simple.create(:my_id => 1, :some_value => "hello")
|
15
|
+
}.should_not raise_error
|
16
|
+
con.hash_key.should == 1
|
17
|
+
con.some_value.should == "hello"
|
18
|
+
end
|
19
|
+
it "should allow you to find a created simple" do
|
20
|
+
Simple.create(:my_id => 1, :some_value => "hello")
|
21
|
+
con = Simple.find(1)
|
22
|
+
con.hash_key.should == 1
|
23
|
+
con.some_value.should == "hello"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'aws-sdk'
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. lib dynameek])
|
5
|
+
|
6
|
+
AWS.config(:use_ssl => false,
|
7
|
+
:dynamo_db_endpoint => 'localhost',
|
8
|
+
:dynamo_db_port => 4567,
|
9
|
+
:access_key_id => "xxx",
|
10
|
+
:secret_access_key => "xxx")
|
11
|
+
|
12
|
+
require File.join(File.dirname(__FILE__), *%w[.. models conversion])
|
13
|
+
require File.join(File.dirname(__FILE__), *%w[.. models simple])
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynameek
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 0.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.2.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Max Dupenois
|
@@ -11,10 +11,43 @@ bindir: bin
|
|
11
11
|
cert_chain: []
|
12
12
|
|
13
13
|
date: 2013-03-20 00:00:00 Z
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: aws-sdk
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: fake_dynamo
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id003
|
16
48
|
description: A very lightweight model for DynamoDB tables in, certainly not in a finished state
|
17
|
-
email:
|
49
|
+
email:
|
50
|
+
- max.dupenois@forward.co.uk
|
18
51
|
executables: []
|
19
52
|
|
20
53
|
extensions: []
|
@@ -22,11 +55,23 @@ extensions: []
|
|
22
55
|
extra_rdoc_files: []
|
23
56
|
|
24
57
|
files:
|
58
|
+
- Gemfile
|
59
|
+
- Gemfile.lock
|
60
|
+
- LICENSE.txt
|
61
|
+
- README.md
|
62
|
+
- Rakefile
|
63
|
+
- dynameek.gemspec
|
64
|
+
- lib/dynameek.rb
|
65
|
+
- lib/dynameek/model.rb
|
25
66
|
- lib/dynameek/model/dynamo_db.rb
|
26
67
|
- lib/dynameek/model/query.rb
|
27
68
|
- lib/dynameek/model/structure.rb
|
28
|
-
- lib/dynameek/
|
29
|
-
-
|
69
|
+
- lib/dynameek/version.rb
|
70
|
+
- test/models/conversion.rb
|
71
|
+
- test/models/simple.rb
|
72
|
+
- test/spec/conversion_spec.rb
|
73
|
+
- test/spec/simple_spec.rb
|
74
|
+
- test/spec/spec_helper.rb
|
30
75
|
homepage: http://github.com/maxdupenois/dynameek
|
31
76
|
licenses: []
|
32
77
|
|
@@ -44,15 +89,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
90
|
none: false
|
46
91
|
requirements:
|
47
|
-
- - "
|
92
|
+
- - ">="
|
48
93
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
94
|
+
version: "0"
|
50
95
|
requirements: []
|
51
96
|
|
52
97
|
rubyforge_project:
|
53
98
|
rubygems_version: 1.8.24
|
54
99
|
signing_key:
|
55
100
|
specification_version: 3
|
56
|
-
summary: Dynameek
|
57
|
-
test_files:
|
58
|
-
|
101
|
+
summary: Dynameek - A dynamodb model
|
102
|
+
test_files:
|
103
|
+
- test/models/conversion.rb
|
104
|
+
- test/models/simple.rb
|
105
|
+
- test/spec/conversion_spec.rb
|
106
|
+
- test/spec/simple_spec.rb
|
107
|
+
- test/spec/spec_helper.rb
|