dynamosaurus 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +55 -0
- data/Rakefile +6 -0
- data/dynamosaurus.gemspec +27 -0
- data/lib/dynamosaurus.rb +163 -0
- data/lib/dynamosaurus/dynamo_base.rb +144 -0
- data/lib/dynamosaurus/dynamo_class.rb +359 -0
- data/lib/dynamosaurus/version.rb +3 -0
- data/spec/dynamosaurus_spec.rb +149 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/testmodel.rb +103 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7c83dcc84e2df48c376bcee1bbf2b75888a2ea63
|
4
|
+
data.tar.gz: a3481c7cd31a0ccce8beee8217539f0b0d993660
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1156a59ba6601629bd3e773285e4dd5b6e6741633a61364e201b01fae67bb433e95ceba19e69d8c5a471a004d0b9c5742dcf78fc6358d7d87dac60283c7b2d53
|
7
|
+
data.tar.gz: 6c6a0ea222c90b658d50a67b160fb6177be10946ad043b71f677661f3be55c2ada79199a87c067d67bb021f6c97e45e79bc7a6270299f7401b5c63116f00e2cb
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Isamu Arimoto
|
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,55 @@
|
|
1
|
+
# Dynamosaurus
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
gem 'dynamosaurus'
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle
|
12
|
+
|
13
|
+
Or install it yourself as:
|
14
|
+
|
15
|
+
$ gem install dynamosaurus
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
require "dynamosaurus"
|
20
|
+
|
21
|
+
class SimpleKVS < Dynamosaurus::DynamoBase
|
22
|
+
key :simple_key, :string
|
23
|
+
end
|
24
|
+
|
25
|
+
aws_config = {
|
26
|
+
:access_key_id => 'aws_dynamodb_access_key_id',
|
27
|
+
:secret_access_key => 'aws_dynamodb_secret_access_key',
|
28
|
+
:region => 'us-west-1',
|
29
|
+
}
|
30
|
+
Aws.config = aws_config
|
31
|
+
|
32
|
+
# create
|
33
|
+
SimpleKVS.put({:simple_key => "key"}, {:num => 1})
|
34
|
+
|
35
|
+
# read
|
36
|
+
kvs = SimpleKVS.get("key")
|
37
|
+
|
38
|
+
# update
|
39
|
+
kvs.update({:test => 1})
|
40
|
+
|
41
|
+
# update
|
42
|
+
kvs.num = 100
|
43
|
+
kvs.save
|
44
|
+
|
45
|
+
# delete
|
46
|
+
kvs.delete
|
47
|
+
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
1. Fork it
|
52
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
53
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
54
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
55
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dynamosaurus/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dynamosaurus"
|
8
|
+
spec.version = Dynamosaurus::VERSION
|
9
|
+
spec.authors = ["Isamu Arimoto"]
|
10
|
+
spec.email = ["isamu.a@gmail.com"]
|
11
|
+
spec.description = %q{Dynamodb simple ORM}
|
12
|
+
spec.summary = %q{Dynamodb simple ORM}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
|
25
|
+
spec.add_runtime_dependency 'aws-sdk-core', ">= 2.0.20"
|
26
|
+
|
27
|
+
end
|
data/lib/dynamosaurus.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require "dynamosaurus/version"
|
2
|
+
require "dynamosaurus/dynamo_base"
|
3
|
+
require "dynamosaurus/dynamo_class"
|
4
|
+
|
5
|
+
module Dynamosaurus
|
6
|
+
class << self
|
7
|
+
attr_accessor :logger
|
8
|
+
end
|
9
|
+
|
10
|
+
class Logger
|
11
|
+
|
12
|
+
attr_accessor :level
|
13
|
+
attr_accessor :delimiter
|
14
|
+
attr_accessor :auto_flush
|
15
|
+
attr_reader :buffer
|
16
|
+
attr_reader :log
|
17
|
+
attr_reader :init_args
|
18
|
+
|
19
|
+
Levels =
|
20
|
+
{
|
21
|
+
:fatal => 7,
|
22
|
+
:error => 6,
|
23
|
+
:warn => 4,
|
24
|
+
:info => 3,
|
25
|
+
:debug => 0
|
26
|
+
}
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Readies a log for writing.
|
31
|
+
#
|
32
|
+
# ==== Parameters
|
33
|
+
# log<IO, String>:: Either an IO object or a name of a logfile.
|
34
|
+
def initialize_log(log)
|
35
|
+
close if @log # be sure that we don't leave open files laying around.
|
36
|
+
|
37
|
+
if log.respond_to?(:write)
|
38
|
+
@log = log
|
39
|
+
elsif File.exist?(log)
|
40
|
+
@log = open(log, (File::WRONLY | File::APPEND))
|
41
|
+
@log.sync = true
|
42
|
+
else
|
43
|
+
FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
|
44
|
+
@log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
|
45
|
+
@log.sync = true
|
46
|
+
@log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
public
|
51
|
+
|
52
|
+
# To initialize the logger you create a new object, proxies to set_log.
|
53
|
+
#
|
54
|
+
# ==== Parameters
|
55
|
+
# *args:: Arguments to create the log from. See set_logs for specifics.
|
56
|
+
def initialize(*args)
|
57
|
+
@init_args = args
|
58
|
+
set_log(*args)
|
59
|
+
self.auto_flush = true
|
60
|
+
Dynamosaurus.logger = self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Replaces an existing logger with a new one.
|
64
|
+
#
|
65
|
+
# ==== Parameters
|
66
|
+
# log<IO, String>:: Either an IO object or a name of a logfile.
|
67
|
+
# log_level<~to_sym>::
|
68
|
+
# The log level from, e.g. :fatal or :info. Defaults to :error in the
|
69
|
+
# production environment and :debug otherwise.
|
70
|
+
# delimiter<String>::
|
71
|
+
# Delimiter to use between message sections. Defaults to " ~ ".
|
72
|
+
# auto_flush<Boolean>::
|
73
|
+
# Whether the log should automatically flush after new messages are
|
74
|
+
# added. Defaults to false.
|
75
|
+
def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
|
76
|
+
if log_level && Levels[log_level.to_sym]
|
77
|
+
@level = Levels[log_level.to_sym]
|
78
|
+
else
|
79
|
+
@level = Levels[:debug]
|
80
|
+
end
|
81
|
+
@buffer = []
|
82
|
+
@delimiter = delimiter
|
83
|
+
@auto_flush = auto_flush
|
84
|
+
|
85
|
+
initialize_log(log)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Flush the entire buffer to the log object.
|
89
|
+
def flush
|
90
|
+
return unless @buffer.size > 0
|
91
|
+
@log.write(@buffer.slice!(0..-1).join)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Close and remove the current log object.
|
95
|
+
def close
|
96
|
+
flush
|
97
|
+
@log.close if @log.respond_to?(:close) && !@log.tty?
|
98
|
+
@log = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# Appends a message to the log. The methods yield to an optional block and
|
102
|
+
# the output of this block will be appended to the message.
|
103
|
+
#
|
104
|
+
# ==== Parameters
|
105
|
+
# string<String>:: The message to be logged. Defaults to nil.
|
106
|
+
#
|
107
|
+
# ==== Returns
|
108
|
+
# String:: The resulting message added to the log file.
|
109
|
+
def <<(string = nil)
|
110
|
+
message = ""
|
111
|
+
message << delimiter
|
112
|
+
message << string if string
|
113
|
+
message << "\n" unless message[-1] == ?\n
|
114
|
+
@buffer << message
|
115
|
+
flush if @auto_flush
|
116
|
+
|
117
|
+
message
|
118
|
+
end
|
119
|
+
alias_method :push, :<<
|
120
|
+
|
121
|
+
# Generate the logging methods for DataMapper.logger for each log level.
|
122
|
+
Levels.each_pair do |name, number|
|
123
|
+
class_eval <<-LEVELMETHODS, __FILE__, __LINE__
|
124
|
+
|
125
|
+
# Appends a message to the log if the log level is at least as high as
|
126
|
+
# the log level of the logger.
|
127
|
+
#
|
128
|
+
# ==== Parameters
|
129
|
+
# string<String>:: The message to be logged. Defaults to nil.
|
130
|
+
#
|
131
|
+
# ==== Returns
|
132
|
+
# self:: The logger object for chaining.
|
133
|
+
def #{name}(message = nil)
|
134
|
+
self << message if #{number} >= level
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
# Appends a message to the log if the log level is at least as high as
|
139
|
+
# the log level of the logger. The bang! version of the method also auto
|
140
|
+
# flushes the log buffer to disk.
|
141
|
+
#
|
142
|
+
# ==== Parameters
|
143
|
+
# string<String>:: The message to be logged. Defaults to nil.
|
144
|
+
#
|
145
|
+
# ==== Returns
|
146
|
+
# self:: The logger object for chaining.
|
147
|
+
def #{name}!(message = nil)
|
148
|
+
self << message if #{number} >= level
|
149
|
+
flush if #{number} >= level
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# ==== Returns
|
154
|
+
# Boolean:: True if this level will be logged by this logger.
|
155
|
+
def #{name}?
|
156
|
+
#{number} >= level
|
157
|
+
end
|
158
|
+
LEVELMETHODS
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require "aws-sdk-core"
|
2
|
+
|
3
|
+
module Dynamosaurus
|
4
|
+
class DynamoBase
|
5
|
+
TYPES = {
|
6
|
+
:string => :s,
|
7
|
+
:number => :n
|
8
|
+
}
|
9
|
+
|
10
|
+
def dynamo_db
|
11
|
+
self.class.dynamo_db
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize params
|
15
|
+
data = params[:data] if params[:data]
|
16
|
+
@data = Dynamosaurus::DynamoBase.res2hash(data)
|
17
|
+
end
|
18
|
+
|
19
|
+
def table_name
|
20
|
+
self.class.table_name
|
21
|
+
end
|
22
|
+
|
23
|
+
def [] key
|
24
|
+
@data[key]
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(key,value)
|
28
|
+
@data[key] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
@data
|
33
|
+
end
|
34
|
+
|
35
|
+
def exist?
|
36
|
+
! @data.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def empty?
|
40
|
+
@data.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def try name
|
44
|
+
@data[name.to_s]
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(name, *params)
|
48
|
+
name = name.to_s[0..-2] if name.to_s[-1] == "="
|
49
|
+
if exist? and @data.has_key?(name.to_s)
|
50
|
+
if params.empty?
|
51
|
+
@data[name.to_s]
|
52
|
+
else
|
53
|
+
@data[name.to_s] = params[0]
|
54
|
+
end
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def key
|
61
|
+
return @key if @key
|
62
|
+
@key = self.class.get_key
|
63
|
+
end
|
64
|
+
|
65
|
+
def keys
|
66
|
+
item_key = {}
|
67
|
+
item_key[key[0].to_sym] = @data[key[0].to_s]
|
68
|
+
item_key[key[2].to_sym] = @data[key[2].to_s] if key.size == 4
|
69
|
+
item_key
|
70
|
+
end
|
71
|
+
|
72
|
+
def update attributes={}, attribute_nums={}, options={}
|
73
|
+
Dynamosaurus.logger << "update"
|
74
|
+
|
75
|
+
attribute_updates = {}
|
76
|
+
attributes.each do |k, v|
|
77
|
+
@data[k.to_s] = v
|
78
|
+
attribute_updates[k.to_s] = {
|
79
|
+
:value => v,
|
80
|
+
:action => "PUT"
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
attribute_nums.each do |k, v|
|
85
|
+
@data[k.to_s] = v.to_i
|
86
|
+
attribute_updates[k.to_s] = {
|
87
|
+
:value => v.to_i,
|
88
|
+
:action => "PUT"
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
query = {
|
93
|
+
:table_name => self.class.table_name,
|
94
|
+
:key => keys,
|
95
|
+
:attribute_updates => attribute_updates
|
96
|
+
}.merge(options)
|
97
|
+
res = dynamo_db.update_item(query)
|
98
|
+
end
|
99
|
+
|
100
|
+
def save
|
101
|
+
attribute_updates = {}
|
102
|
+
@data.each do |k, v|
|
103
|
+
if key.index(k.to_sym).nil?
|
104
|
+
attribute_updates[k.to_s] = {
|
105
|
+
:value => v,
|
106
|
+
:action => "PUT"
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
query = {
|
112
|
+
:table_name => self.class.table_name,
|
113
|
+
:key => keys,
|
114
|
+
:attribute_updates => attribute_updates
|
115
|
+
}
|
116
|
+
res = dynamo_db.update_item(query)
|
117
|
+
end
|
118
|
+
|
119
|
+
def attr_delete attributes=[]
|
120
|
+
Dynamosaurus.logger << "delete"
|
121
|
+
|
122
|
+
attribute_updates = {}
|
123
|
+
attributes.each do |k|
|
124
|
+
attribute_updates[k.to_s] = {
|
125
|
+
:action => "DELETE"
|
126
|
+
}
|
127
|
+
@data.delete(k.to_s)
|
128
|
+
end
|
129
|
+
|
130
|
+
res = dynamo_db.update_item(
|
131
|
+
:table_name => self.class.table_name,
|
132
|
+
:key => keys,
|
133
|
+
:attribute_updates => attribute_updates
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
def delete
|
138
|
+
dynamo_db.delete_item(
|
139
|
+
:table_name => self.class.table_name,
|
140
|
+
:key => keys
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,359 @@
|
|
1
|
+
module Dynamosaurus
|
2
|
+
class DynamoBase
|
3
|
+
class << self
|
4
|
+
def all_models
|
5
|
+
subclasses = []
|
6
|
+
ObjectSpace.each_object(Dynamosaurus::DynamoBase.singleton_class) do |k|
|
7
|
+
subclasses << k if k.superclass == Dynamosaurus::DynamoBase
|
8
|
+
end
|
9
|
+
subclasses
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_table
|
13
|
+
tables = dynamo_db.list_tables.table_names
|
14
|
+
|
15
|
+
Dynamosaurus::DynamoBase.all_models.each do |model_class|
|
16
|
+
if tables.index(model_class.table_name).nil?
|
17
|
+
table = dynamo_db.create_table(
|
18
|
+
model_class.get_table_schema
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def tables
|
25
|
+
Dynamosaurus::DynamoBase.all_models.map do |model_class|
|
26
|
+
model_class.table_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def table_name
|
31
|
+
name.downcase + (ENV['DYNAMODB_SUFFIX'] || "_local")
|
32
|
+
end
|
33
|
+
|
34
|
+
def dynamo_db
|
35
|
+
return @dynamo_db if @dynamo_db
|
36
|
+
unless ENV['DYNAMODB_SUFFIX']
|
37
|
+
@dynamo_db = Aws::DynamoDB::Client.new(
|
38
|
+
:endpoint => "http://localhost:8000",
|
39
|
+
:region => "us-west-1"
|
40
|
+
)
|
41
|
+
else
|
42
|
+
@dynamo_db = Aws::DynamoDB::Client.new
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def key k, type, range_key=nil, range_key_type=nil
|
47
|
+
@key = [k, Dynamosaurus::DynamoBase::TYPES[type]]
|
48
|
+
@key << range_key << Dynamosaurus::DynamoBase::TYPES[range_key_type] if range_key
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_key
|
52
|
+
@key
|
53
|
+
end
|
54
|
+
|
55
|
+
def table_schema schema = {}
|
56
|
+
@schema = schema
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_table_schema
|
60
|
+
@schema[:table_name] = table_name
|
61
|
+
@schema
|
62
|
+
end
|
63
|
+
|
64
|
+
def global_index index_name, key, key_type, range_key=nil, range_key_type=nil
|
65
|
+
@global_index = {} if @global_index.nil?
|
66
|
+
@global_index[index_name] = [key, Dynamosaurus::DynamoBase::TYPES[key_type]]
|
67
|
+
@global_index[index_name] << range_key << Dynamosaurus::DynamoBase::TYPES[range_key_type] if range_key
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_global_indexes
|
71
|
+
(@global_index) ? @global_index : {}
|
72
|
+
|
73
|
+
end
|
74
|
+
def get_global_index name
|
75
|
+
@global_index[name]
|
76
|
+
end
|
77
|
+
|
78
|
+
def secondary_index index_name, range_key=nil, range_key_type=nil
|
79
|
+
@secondary_index = {} if @secondary_index.nil?
|
80
|
+
@secondary_index[index_name.to_sym] = [@key[0], @key[1], range_key, Dynamosaurus::DynamoBase::TYPES[range_key_type]] if range_key
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_secondary_indexes
|
84
|
+
@secondary_index ? @secondary_index : {}
|
85
|
+
end
|
86
|
+
def get_secondary_index name
|
87
|
+
@secondary_index[name]
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_index hash
|
91
|
+
get_secondary_indexes.merge(get_global_indexes).each{|key, value|
|
92
|
+
if hash.size == 1 && hash.keys.first == value.first
|
93
|
+
return {
|
94
|
+
:index_name => key,
|
95
|
+
:keys => [value[0]]
|
96
|
+
}
|
97
|
+
else hash.size == 2 && hash.keys.sort == [value[0], value[2]].sort
|
98
|
+
return {
|
99
|
+
:index_name => key,
|
100
|
+
:keys => [value[0], value[2]]
|
101
|
+
}
|
102
|
+
end
|
103
|
+
}
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def key_list
|
108
|
+
keys = get_key + get_secondary_indexes.values.flatten
|
109
|
+
list = {}
|
110
|
+
until keys.empty?
|
111
|
+
convi = keys.shift(2)
|
112
|
+
list[convi[0]] = convi[1]
|
113
|
+
end
|
114
|
+
list
|
115
|
+
end
|
116
|
+
|
117
|
+
def res2hash hash
|
118
|
+
new_hash = {}
|
119
|
+
return new_hash if hash.nil?
|
120
|
+
hash
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def query_without_index value, option
|
125
|
+
keys = {}
|
126
|
+
|
127
|
+
value.each_with_index{|(k,v), i|
|
128
|
+
keys[k] = {
|
129
|
+
:comparison_operator => "EQ",
|
130
|
+
:attribute_value_list => [v.to_s]
|
131
|
+
}
|
132
|
+
}
|
133
|
+
Dynamosaurus.logger << "query index #{table_name} #{keys}"
|
134
|
+
query keys, nil, option
|
135
|
+
end
|
136
|
+
|
137
|
+
def get_item_key value
|
138
|
+
if value.is_a? Array
|
139
|
+
{
|
140
|
+
get_key[0] => value[0].to_s,
|
141
|
+
get_key[2] => value[1].to_s,
|
142
|
+
}
|
143
|
+
else
|
144
|
+
{
|
145
|
+
get_key[0] => value.to_s,
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def get_from_key value, option={}
|
151
|
+
return nil if value.nil?
|
152
|
+
|
153
|
+
item_key = get_item_key(value)
|
154
|
+
Dynamosaurus.logger << "get_item #{table_name} #{item_key}"
|
155
|
+
|
156
|
+
res = dynamo_db.get_item(
|
157
|
+
:table_name => table_name,
|
158
|
+
:key => item_key
|
159
|
+
)
|
160
|
+
if res.item
|
161
|
+
new :data => res.item
|
162
|
+
else
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_from_index hash, option={}
|
168
|
+
if index = get_index(hash)
|
169
|
+
keys = {}
|
170
|
+
|
171
|
+
index[:keys].each do |key|
|
172
|
+
keys[key] = {
|
173
|
+
:comparison_operator => "EQ",
|
174
|
+
:attribute_value_list => [hash[key]]
|
175
|
+
}
|
176
|
+
end
|
177
|
+
Dynamosaurus.logger << "query index #{table_name} #{keys}"
|
178
|
+
query keys, index[:index_name], option
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_from_local_index hash, option={}
|
183
|
+
if index = hash.delete(:index)
|
184
|
+
keys = {}
|
185
|
+
hash.each do |k, v|
|
186
|
+
keys[k] = {
|
187
|
+
:comparison_operator => "EQ",
|
188
|
+
:attribute_value_list => [ v.to_s ]
|
189
|
+
}
|
190
|
+
end
|
191
|
+
Dynamosaurus.logger << "query local_index #{table_name} #{keys}"
|
192
|
+
query keys, index, option
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def query keys, index=nil, option={}
|
197
|
+
query = {
|
198
|
+
:table_name => table_name,
|
199
|
+
:key_conditions => keys,
|
200
|
+
}
|
201
|
+
if index
|
202
|
+
query[:index_name] = index
|
203
|
+
end
|
204
|
+
|
205
|
+
query.merge!(option)
|
206
|
+
res = dynamo_db.query(query)
|
207
|
+
if query[:select] == "COUNT"
|
208
|
+
res[:count]
|
209
|
+
else
|
210
|
+
if res.items
|
211
|
+
return res.items.map{|item|
|
212
|
+
new :data => item
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# public method
|
219
|
+
def get value, option={}
|
220
|
+
if value.is_a? Hash
|
221
|
+
if value[:index]
|
222
|
+
get_from_local_index value, option
|
223
|
+
elsif option[:noindex]
|
224
|
+
option.delete(:noindex)
|
225
|
+
query_without_index value, option
|
226
|
+
else
|
227
|
+
get_from_index value, option
|
228
|
+
end
|
229
|
+
else
|
230
|
+
get_from_key value, option
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def batch_get_item keys
|
235
|
+
return nil if keys.nil? or keys.empty?
|
236
|
+
Dynamosaurus.logger << "batch_get_item #{table_name} #{keys}"
|
237
|
+
if get_key.size == 2
|
238
|
+
my_keys = keys.map{|_key| {get_key[0].to_s => _key.to_s } }
|
239
|
+
else
|
240
|
+
my_keys = []
|
241
|
+
keys[get_key[0]].each do |key1|
|
242
|
+
keys[get_key[2]].each do |key2|
|
243
|
+
my_keys << {
|
244
|
+
get_key[0].to_s => key1,
|
245
|
+
get_key[2].to_s => key2,
|
246
|
+
}
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
res = dynamo_db.batch_get_item(
|
252
|
+
:request_items => {
|
253
|
+
table_name => {
|
254
|
+
:keys => my_keys
|
255
|
+
}
|
256
|
+
})
|
257
|
+
|
258
|
+
if res.responses[table_name]
|
259
|
+
return res.responses[table_name].map{|item|
|
260
|
+
new :data => item
|
261
|
+
}
|
262
|
+
end
|
263
|
+
nil
|
264
|
+
end
|
265
|
+
|
266
|
+
def first
|
267
|
+
Dynamosaurus.logger << "first #{table_name}"
|
268
|
+
res = dynamo_db.scan({
|
269
|
+
:table_name => table_name,
|
270
|
+
:limit => 1
|
271
|
+
})
|
272
|
+
if res.items && res.count > 0
|
273
|
+
new :data => res.items[0]
|
274
|
+
else
|
275
|
+
nil
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def all
|
280
|
+
Dynamosaurus.logger << "all #{table_name}"
|
281
|
+
res = dynamo_db.scan({
|
282
|
+
:table_name => table_name,
|
283
|
+
})
|
284
|
+
if res.items
|
285
|
+
res.items.map{|item|
|
286
|
+
new :data => item
|
287
|
+
}
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def put hash, num_hash={}, return_values=nil
|
292
|
+
|
293
|
+
new_hash = {}
|
294
|
+
hash.each{|key, value|
|
295
|
+
new_hash[key] = value.to_s unless value.nil?
|
296
|
+
}
|
297
|
+
num_hash.merge({:updated_at => Time.now.to_i}).each{|key, value|
|
298
|
+
new_hash[key] = value.to_i unless value.nil?
|
299
|
+
} if num_hash
|
300
|
+
|
301
|
+
res = dynamo_db.put_item(
|
302
|
+
:table_name => table_name,
|
303
|
+
:item => new_hash,
|
304
|
+
:return_values => return_values || "NONE"
|
305
|
+
)
|
306
|
+
end
|
307
|
+
|
308
|
+
def save hash, num_hash={}, return_values=nil
|
309
|
+
put(hash, num_hash, return_values)
|
310
|
+
my_keys = get_key
|
311
|
+
|
312
|
+
if my_keys.size == 4
|
313
|
+
get([hash[my_keys[0]], hash[my_keys[2]]])
|
314
|
+
else
|
315
|
+
get(hash[my_keys[0]])
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def add key=[], attribute_nums={}, options={}
|
320
|
+
Dynamosaurus.logger << "update"
|
321
|
+
|
322
|
+
attribute_updates = {}
|
323
|
+
attribute_nums.each do |k, v|
|
324
|
+
attribute_updates[k.to_s] = {
|
325
|
+
:value => v,
|
326
|
+
:action => "ADD"
|
327
|
+
}
|
328
|
+
end
|
329
|
+
|
330
|
+
class_key = get_key
|
331
|
+
keys = {
|
332
|
+
class_key[0].to_sym => key.is_a?(Array) ? key[0] : key
|
333
|
+
}
|
334
|
+
keys[class_key[2].to_sym] = key[1] if class_key.size > 2
|
335
|
+
|
336
|
+
query ={
|
337
|
+
:table_name => table_name,
|
338
|
+
:key => keys,
|
339
|
+
:attribute_updates => attribute_updates
|
340
|
+
}
|
341
|
+
query = query.merge(options)
|
342
|
+
res = dynamo_db.update_item(query)
|
343
|
+
|
344
|
+
end
|
345
|
+
|
346
|
+
def delete_item value
|
347
|
+
return nil if value.nil?
|
348
|
+
|
349
|
+
old_item = dynamo_db.delete_item(
|
350
|
+
:table_name => table_name,
|
351
|
+
:key => get_item_key(value),
|
352
|
+
:return_values => "ALL_OLD"
|
353
|
+
)
|
354
|
+
new :data => old_item.attributes
|
355
|
+
end
|
356
|
+
end
|
357
|
+
# end of self class
|
358
|
+
end
|
359
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Dynamosaurus do
|
4
|
+
before(:all) do
|
5
|
+
ENV['DYNAMODB_SUFFIX'] = "_local"
|
6
|
+
|
7
|
+
Dynamosaurus::Logger.new('log/vm.log', :debug)
|
8
|
+
Aws.config = {
|
9
|
+
:endpoint => "http://localhost:8000",
|
10
|
+
:region => 'local_test',
|
11
|
+
}
|
12
|
+
|
13
|
+
Dynamosaurus::DynamoBase.create_table
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:all) do
|
17
|
+
connect = Dynamosaurus::DynamoBase.dynamo_db
|
18
|
+
Dynamosaurus::DynamoBase.tables.each do |table_name|
|
19
|
+
connect.delete_table(:table_name => table_name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should have a version number' do
|
24
|
+
expect(Dynamosaurus::VERSION).not_to be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'simple ordered kvs test' do
|
28
|
+
expect(SimpleOrderedKVS.first).to be_nil
|
29
|
+
|
30
|
+
SimpleOrderedKVS.put({:simple_key => "key", :simple_id => "1"})
|
31
|
+
|
32
|
+
kvs = SimpleOrderedKVS.first
|
33
|
+
expect(kvs.simple_key).to eq "key"
|
34
|
+
expect(kvs.simple_id).to eq "1"
|
35
|
+
|
36
|
+
SimpleOrderedKVS.get(["key", "1"])
|
37
|
+
expect(kvs.simple_id).to eq "1"
|
38
|
+
|
39
|
+
sleep 1
|
40
|
+
SimpleOrderedKVS.put({:simple_key => "key", :simple_id => "3"})
|
41
|
+
sleep 1
|
42
|
+
SimpleOrderedKVS.put({:simple_key => "key", :simple_id => "2"})
|
43
|
+
|
44
|
+
orderd_items = SimpleOrderedKVS.get({
|
45
|
+
:index => "updated_at_index",
|
46
|
+
:simple_key => "key"
|
47
|
+
},{
|
48
|
+
:scan_index_forward => false,
|
49
|
+
:limit => 50,
|
50
|
+
})
|
51
|
+
expect(orderd_items[0].simple_id).to eq "2"
|
52
|
+
expect(orderd_items[1].simple_id).to eq "3"
|
53
|
+
expect(orderd_items[2].simple_id).to eq "1"
|
54
|
+
|
55
|
+
batch_items = SimpleOrderedKVS.batch_get_item({:simple_key => ["key"], :simple_id => ["1", "2", "3"]})
|
56
|
+
expect(orderd_items.size).to eq 3
|
57
|
+
|
58
|
+
kvs = SimpleOrderedKVS.first
|
59
|
+
kvs2 = SimpleOrderedKVS.get({:simple_key => kvs.simple_key, :updated_at => kvs.updated_at})
|
60
|
+
expect(kvs.simple_id).to eq kvs2[0].simple_id
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'simple kvs test' do
|
65
|
+
expect(SimpleKVS.first).to be_nil
|
66
|
+
expect(SimpleKVS.get("key")).to be_nil
|
67
|
+
|
68
|
+
SimpleKVS.put({:simple_key => "key"}, {:num => 1})
|
69
|
+
expect(SimpleKVS.first.num).to eq 1
|
70
|
+
|
71
|
+
kvs = SimpleKVS.get("key")
|
72
|
+
expect(kvs.num).to eq 1
|
73
|
+
|
74
|
+
kvs = SimpleKVS.get("key2")
|
75
|
+
expect(kvs).to be_nil
|
76
|
+
|
77
|
+
kvs = SimpleKVS.first
|
78
|
+
expect(kvs.num).to eq 1
|
79
|
+
|
80
|
+
SimpleKVS.add("key", {:num => 1})
|
81
|
+
expect(SimpleKVS.first.num).to eq 2
|
82
|
+
|
83
|
+
SimpleKVS.put({:simple_key => "key"}, {:num => 1})
|
84
|
+
expect(SimpleKVS.first.num).to eq 1
|
85
|
+
|
86
|
+
kvs = SimpleKVS.first
|
87
|
+
kvs.update({}, {:test => 1})
|
88
|
+
expect(SimpleKVS.first.test).to eq 1
|
89
|
+
|
90
|
+
kvs = SimpleKVS.first
|
91
|
+
kvs.update({:test => "1"})
|
92
|
+
expect(SimpleKVS.first.test).to eq "1"
|
93
|
+
|
94
|
+
kvs.attr_delete(["test"])
|
95
|
+
expect(kvs["test"]).to be_nil
|
96
|
+
|
97
|
+
kvs = SimpleKVS.first
|
98
|
+
expect(kvs["test"]).to be_nil
|
99
|
+
|
100
|
+
kvs.num = 100
|
101
|
+
kvs.save
|
102
|
+
|
103
|
+
kvs = SimpleKVS.first
|
104
|
+
expect(kvs.num).to eq 100
|
105
|
+
|
106
|
+
SimpleKVS.put({:simple_key => "key1"}, {:num => 1})
|
107
|
+
SimpleKVS.put({:simple_key => "key2"}, {:num => 2})
|
108
|
+
SimpleKVS.put({:simple_key => "key3"}, {:num => 3})
|
109
|
+
|
110
|
+
expect(SimpleKVS.all.size).to eq 4
|
111
|
+
|
112
|
+
expect(SimpleKVS.batch_get_item(["key1"]).size).to eq 1
|
113
|
+
expect(SimpleKVS.batch_get_item(["key1", "key2"]).size).to eq 2
|
114
|
+
expect(SimpleKVS.batch_get_item(["key1", "key10"]).size).to eq 1
|
115
|
+
|
116
|
+
old_kvs = SimpleKVS.delete_item("key2")
|
117
|
+
expect(old_kvs.simple_key).to eq "key2"
|
118
|
+
expect(SimpleKVS.batch_get_item(["key1", "key2"]).size).to eq 1
|
119
|
+
|
120
|
+
kvs = SimpleKVS.get("key3")
|
121
|
+
kvs.delete
|
122
|
+
kvs = SimpleKVS.get("key3")
|
123
|
+
expect(kvs).to be_nil
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'comment test' do
|
128
|
+
expect(Comment.first).to be_nil
|
129
|
+
expect(Comment.get(["1", "1"])).to be_nil
|
130
|
+
|
131
|
+
Comment.put({:content_id => "1", :message_id => "1", :user_id => "abc"})
|
132
|
+
expect(Comment.first.content_id).to eq "1"
|
133
|
+
|
134
|
+
kvs = Comment.get(["1", "1"])
|
135
|
+
expect(kvs.content_id).to eq "1"
|
136
|
+
|
137
|
+
kvs = Comment.get(["2", "2"])
|
138
|
+
expect(kvs).to be_nil
|
139
|
+
|
140
|
+
Comment.put({:content_id => "1", :message_id => "2", :user_id => "abc"})
|
141
|
+
Comment.put({:content_id => "1", :message_id => "3", :user_id => "xyz"})
|
142
|
+
|
143
|
+
# global secondary index
|
144
|
+
comments = Comment.get({:user_id => "abc"})
|
145
|
+
expect(comments.size).to eq 2
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/testmodel.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class SimpleKVS < Dynamosaurus::DynamoBase
|
4
|
+
key :simple_key, :string
|
5
|
+
|
6
|
+
table_schema :key_schema =>
|
7
|
+
[
|
8
|
+
{ :key_type => "HASH",
|
9
|
+
:attribute_name => "simple_key"
|
10
|
+
}
|
11
|
+
],
|
12
|
+
:provisioned_throughput => {
|
13
|
+
:read_capacity_units => 100,
|
14
|
+
:write_capacity_units => 10
|
15
|
+
},
|
16
|
+
:attribute_definitions => [
|
17
|
+
{:attribute_name => "simple_key", :attribute_type => "S"},
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
class SimpleOrderedKVS < Dynamosaurus::DynamoBase
|
22
|
+
key :simple_key, :string, :simple_id, :string
|
23
|
+
secondary_index :updated_at_index, :updated_at, :number
|
24
|
+
|
25
|
+
table_schema :key_schema =>
|
26
|
+
[
|
27
|
+
{ :key_type => "HASH",
|
28
|
+
:attribute_name => "simple_key"
|
29
|
+
},
|
30
|
+
{ :key_type => "RANGE",
|
31
|
+
:attribute_name => "simple_id"},
|
32
|
+
],
|
33
|
+
:provisioned_throughput => {
|
34
|
+
:read_capacity_units => 20,
|
35
|
+
:write_capacity_units => 10
|
36
|
+
},
|
37
|
+
:attribute_definitions => [
|
38
|
+
{:attribute_name => "simple_key", :attribute_type => "S"},
|
39
|
+
{:attribute_name => "simple_id", :attribute_type => "S"},
|
40
|
+
{:attribute_name => "updated_at", :attribute_type => "N"},
|
41
|
+
],
|
42
|
+
:local_secondary_indexes => [
|
43
|
+
{
|
44
|
+
:index_name => "updated_at_index",
|
45
|
+
:key_schema => [
|
46
|
+
{ :key_type => "HASH",
|
47
|
+
:attribute_name => "simple_key"},
|
48
|
+
|
49
|
+
{
|
50
|
+
:key_type => "RANGE",
|
51
|
+
:attribute_name => "updated_at"
|
52
|
+
}
|
53
|
+
],
|
54
|
+
:projection => {
|
55
|
+
:projection_type => "ALL"
|
56
|
+
},
|
57
|
+
},
|
58
|
+
]
|
59
|
+
end
|
60
|
+
|
61
|
+
class Comment < Dynamosaurus::DynamoBase
|
62
|
+
key :content_id, :string, :message_id, :string
|
63
|
+
global_index :user_index, :user_id, :string
|
64
|
+
|
65
|
+
table_schema :key_schema =>
|
66
|
+
[
|
67
|
+
{
|
68
|
+
:key_type => "HASH",
|
69
|
+
:attribute_name => "content_id"
|
70
|
+
},
|
71
|
+
{
|
72
|
+
:key_type => "RANGE",
|
73
|
+
:attribute_name => "message_id"
|
74
|
+
}
|
75
|
+
],
|
76
|
+
:provisioned_throughput => {
|
77
|
+
:read_capacity_units => 100,
|
78
|
+
:write_capacity_units => 10
|
79
|
+
},
|
80
|
+
:attribute_definitions => [
|
81
|
+
{:attribute_name => "content_id", :attribute_type => "S"},
|
82
|
+
{:attribute_name => "message_id", :attribute_type => "S"},
|
83
|
+
{:attribute_name => "user_id", :attribute_type => "S"},
|
84
|
+
],
|
85
|
+
:global_secondary_indexes => [
|
86
|
+
{
|
87
|
+
:index_name => "user_index",
|
88
|
+
:key_schema => [
|
89
|
+
{
|
90
|
+
:key_type => "HASH",
|
91
|
+
:attribute_name => "user_id"
|
92
|
+
},
|
93
|
+
],
|
94
|
+
:projection => {
|
95
|
+
:projection_type => "KEYS_ONLY",
|
96
|
+
},
|
97
|
+
:provisioned_throughput => {
|
98
|
+
:read_capacity_units => 50,
|
99
|
+
:write_capacity_units => 10
|
100
|
+
},
|
101
|
+
},
|
102
|
+
]
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dynamosaurus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Isamu Arimoto
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: aws-sdk-core
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.0.20
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.0.20
|
69
|
+
description: Dynamodb simple ORM
|
70
|
+
email:
|
71
|
+
- isamu.a@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .rspec
|
78
|
+
- .travis.yml
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- dynamosaurus.gemspec
|
84
|
+
- lib/dynamosaurus.rb
|
85
|
+
- lib/dynamosaurus/dynamo_base.rb
|
86
|
+
- lib/dynamosaurus/dynamo_class.rb
|
87
|
+
- lib/dynamosaurus/version.rb
|
88
|
+
- spec/dynamosaurus_spec.rb
|
89
|
+
- spec/spec_helper.rb
|
90
|
+
- spec/testmodel.rb
|
91
|
+
homepage: ''
|
92
|
+
licenses:
|
93
|
+
- MIT
|
94
|
+
metadata: {}
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 2.4.6
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: Dynamodb simple ORM
|
115
|
+
test_files:
|
116
|
+
- spec/dynamosaurus_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- spec/testmodel.rb
|