adept_dynamoid 0.5.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Dynamoid.gemspec +193 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +86 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +265 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/doc/.nojekyll +0 -0
- data/doc/Dynamoid.html +312 -0
- data/doc/Dynamoid/Adapter.html +1385 -0
- data/doc/Dynamoid/Adapter/AwsSdk.html +1585 -0
- data/doc/Dynamoid/Adapter/Local.html +1574 -0
- data/doc/Dynamoid/Associations.html +131 -0
- data/doc/Dynamoid/Associations/Association.html +794 -0
- data/doc/Dynamoid/Associations/BelongsTo.html +158 -0
- data/doc/Dynamoid/Associations/ClassMethods.html +723 -0
- data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +164 -0
- data/doc/Dynamoid/Associations/HasMany.html +164 -0
- data/doc/Dynamoid/Associations/HasOne.html +158 -0
- data/doc/Dynamoid/Associations/ManyAssociation.html +1640 -0
- data/doc/Dynamoid/Associations/SingleAssociation.html +598 -0
- data/doc/Dynamoid/Components.html +204 -0
- data/doc/Dynamoid/Config.html +395 -0
- data/doc/Dynamoid/Config/Options.html +609 -0
- data/doc/Dynamoid/Criteria.html +131 -0
- data/doc/Dynamoid/Criteria/Chain.html +1063 -0
- data/doc/Dynamoid/Criteria/ClassMethods.html +98 -0
- data/doc/Dynamoid/Document.html +666 -0
- data/doc/Dynamoid/Document/ClassMethods.html +937 -0
- data/doc/Dynamoid/Errors.html +118 -0
- data/doc/Dynamoid/Errors/DocumentNotValid.html +210 -0
- data/doc/Dynamoid/Errors/Error.html +130 -0
- data/doc/Dynamoid/Errors/InvalidField.html +133 -0
- data/doc/Dynamoid/Errors/MissingRangeKey.html +133 -0
- data/doc/Dynamoid/Fields.html +669 -0
- data/doc/Dynamoid/Fields/ClassMethods.html +309 -0
- data/doc/Dynamoid/Finders.html +128 -0
- data/doc/Dynamoid/Finders/ClassMethods.html +516 -0
- data/doc/Dynamoid/Indexes.html +308 -0
- data/doc/Dynamoid/Indexes/ClassMethods.html +353 -0
- data/doc/Dynamoid/Indexes/Index.html +1104 -0
- data/doc/Dynamoid/Persistence.html +651 -0
- data/doc/Dynamoid/Persistence/ClassMethods.html +670 -0
- data/doc/Dynamoid/Validations.html +399 -0
- data/doc/_index.html +461 -0
- data/doc/class_list.html +47 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +55 -0
- data/doc/css/style.css +322 -0
- data/doc/file.LICENSE.html +66 -0
- data/doc/file.README.html +312 -0
- data/doc/file_list.html +52 -0
- data/doc/frames.html +13 -0
- data/doc/index.html +312 -0
- data/doc/js/app.js +205 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +16 -0
- data/doc/method_list.html +1238 -0
- data/doc/top-level-namespace.html +105 -0
- data/lib/dynamoid.rb +47 -0
- data/lib/dynamoid/adapter.rb +177 -0
- data/lib/dynamoid/adapter/aws_sdk.rb +223 -0
- data/lib/dynamoid/associations.rb +106 -0
- data/lib/dynamoid/associations/association.rb +105 -0
- data/lib/dynamoid/associations/belongs_to.rb +44 -0
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +40 -0
- data/lib/dynamoid/associations/has_many.rb +39 -0
- data/lib/dynamoid/associations/has_one.rb +39 -0
- data/lib/dynamoid/associations/many_association.rb +191 -0
- data/lib/dynamoid/associations/single_association.rb +69 -0
- data/lib/dynamoid/components.rb +36 -0
- data/lib/dynamoid/config.rb +57 -0
- data/lib/dynamoid/config/options.rb +78 -0
- data/lib/dynamoid/criteria.rb +29 -0
- data/lib/dynamoid/criteria/chain.rb +243 -0
- data/lib/dynamoid/dirty.rb +41 -0
- data/lib/dynamoid/document.rb +184 -0
- data/lib/dynamoid/errors.rb +28 -0
- data/lib/dynamoid/fields.rb +130 -0
- data/lib/dynamoid/finders.rb +131 -0
- data/lib/dynamoid/identity_map.rb +96 -0
- data/lib/dynamoid/indexes.rb +69 -0
- data/lib/dynamoid/indexes/index.rb +103 -0
- data/lib/dynamoid/middleware/identity_map.rb +16 -0
- data/lib/dynamoid/persistence.rb +247 -0
- data/lib/dynamoid/validations.rb +36 -0
- data/spec/app/models/address.rb +10 -0
- data/spec/app/models/camel_case.rb +24 -0
- data/spec/app/models/magazine.rb +11 -0
- data/spec/app/models/message.rb +9 -0
- data/spec/app/models/sponsor.rb +8 -0
- data/spec/app/models/subscription.rb +12 -0
- data/spec/app/models/tweet.rb +12 -0
- data/spec/app/models/user.rb +26 -0
- data/spec/dynamoid/adapter/aws_sdk_spec.rb +186 -0
- data/spec/dynamoid/adapter_spec.rb +117 -0
- data/spec/dynamoid/associations/association_spec.rb +194 -0
- data/spec/dynamoid/associations/belongs_to_spec.rb +71 -0
- data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +47 -0
- data/spec/dynamoid/associations/has_many_spec.rb +42 -0
- data/spec/dynamoid/associations/has_one_spec.rb +45 -0
- data/spec/dynamoid/associations_spec.rb +16 -0
- data/spec/dynamoid/config_spec.rb +27 -0
- data/spec/dynamoid/criteria/chain_spec.rb +140 -0
- data/spec/dynamoid/criteria_spec.rb +72 -0
- data/spec/dynamoid/dirty_spec.rb +49 -0
- data/spec/dynamoid/document_spec.rb +118 -0
- data/spec/dynamoid/fields_spec.rb +127 -0
- data/spec/dynamoid/finders_spec.rb +135 -0
- data/spec/dynamoid/identity_map_spec.rb +45 -0
- data/spec/dynamoid/indexes/index_spec.rb +104 -0
- data/spec/dynamoid/indexes_spec.rb +25 -0
- data/spec/dynamoid/persistence_spec.rb +176 -0
- data/spec/dynamoid/validations_spec.rb +36 -0
- data/spec/dynamoid_spec.rb +9 -0
- data/spec/spec_helper.rb +50 -0
- metadata +376 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.7.5
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" media="screen" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
relpath = '';
|
19
|
+
if (relpath != '') relpath += '/';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
23
|
+
|
24
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
25
|
+
|
26
|
+
|
27
|
+
</head>
|
28
|
+
<body>
|
29
|
+
<script type="text/javascript" charset="utf-8">
|
30
|
+
if (window.top.frames.main) document.body.className = 'frames';
|
31
|
+
</script>
|
32
|
+
|
33
|
+
<div id="header">
|
34
|
+
<div id="menu">
|
35
|
+
|
36
|
+
<a href="_index.html">Index</a> »
|
37
|
+
|
38
|
+
|
39
|
+
<span class="title">Top Level Namespace</span>
|
40
|
+
|
41
|
+
|
42
|
+
<div class="noframes"><span class="title">(</span><a href="." target="_top">no frames</a><span class="title">)</span></div>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<div id="search">
|
46
|
+
|
47
|
+
<a id="class_list_link" href="#">Class List</a>
|
48
|
+
|
49
|
+
<a id="method_list_link" href="#">Method List</a>
|
50
|
+
|
51
|
+
<a id="file_list_link" href="#">File List</a>
|
52
|
+
|
53
|
+
</div>
|
54
|
+
<div class="clear"></div>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
<iframe id="search_frame"></iframe>
|
58
|
+
|
59
|
+
<div id="content"><h1>Top Level Namespace
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
</h1>
|
64
|
+
|
65
|
+
<dl class="box">
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
</dl>
|
75
|
+
<div class="clear"></div>
|
76
|
+
|
77
|
+
<h2>Defined Under Namespace</h2>
|
78
|
+
<p class="children">
|
79
|
+
|
80
|
+
|
81
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Dynamoid.html" title="Dynamoid (module)">Dynamoid</a></span>
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
</p>
|
87
|
+
|
88
|
+
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
</div>
|
97
|
+
|
98
|
+
<div id="footer">
|
99
|
+
Generated on Thu Apr 26 01:26:26 2012 by
|
100
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
101
|
+
0.7.5 (ruby-1.9.3).
|
102
|
+
</div>
|
103
|
+
|
104
|
+
</body>
|
105
|
+
</html>
|
data/lib/dynamoid.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "delegate"
|
2
|
+
require "time"
|
3
|
+
require "securerandom"
|
4
|
+
require "active_support/core_ext"
|
5
|
+
require 'active_support/json'
|
6
|
+
require "active_support/inflector"
|
7
|
+
require "active_support/lazy_load_hooks"
|
8
|
+
require "active_support/time_with_zone"
|
9
|
+
require "active_model"
|
10
|
+
|
11
|
+
require 'dynamoid/errors'
|
12
|
+
require 'dynamoid/fields'
|
13
|
+
require 'dynamoid/indexes'
|
14
|
+
require 'dynamoid/associations'
|
15
|
+
require 'dynamoid/persistence'
|
16
|
+
require 'dynamoid/dirty'
|
17
|
+
require 'dynamoid/validations'
|
18
|
+
require 'dynamoid/criteria'
|
19
|
+
require 'dynamoid/finders'
|
20
|
+
require 'dynamoid/identity_map'
|
21
|
+
require 'dynamoid/config'
|
22
|
+
require 'dynamoid/components'
|
23
|
+
require 'dynamoid/document'
|
24
|
+
require 'dynamoid/adapter'
|
25
|
+
|
26
|
+
require 'dynamoid/middleware/identity_map'
|
27
|
+
|
28
|
+
module Dynamoid
|
29
|
+
extend self
|
30
|
+
|
31
|
+
MAX_ITEM_SIZE = 65_536
|
32
|
+
|
33
|
+
def configure
|
34
|
+
block_given? ? yield(Dynamoid::Config) : Dynamoid::Config
|
35
|
+
Dynamoid::Adapter.reconnect!
|
36
|
+
end
|
37
|
+
alias :config :configure
|
38
|
+
|
39
|
+
def logger
|
40
|
+
Dynamoid::Config.logger
|
41
|
+
end
|
42
|
+
|
43
|
+
def included_models
|
44
|
+
@included_models ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Dynamoid
|
3
|
+
|
4
|
+
# Adapter provides a generic, write-through class that abstracts variations in the underlying connections to provide a uniform response
|
5
|
+
# to Dynamoid.
|
6
|
+
module Adapter
|
7
|
+
extend self
|
8
|
+
attr_accessor :tables
|
9
|
+
|
10
|
+
# The actual adapter currently in use: presently AwsSdk.
|
11
|
+
#
|
12
|
+
# @since 0.2.0
|
13
|
+
def adapter
|
14
|
+
reconnect! unless @adapter
|
15
|
+
@adapter
|
16
|
+
end
|
17
|
+
|
18
|
+
# Establishes a connection to the underyling adapter and caches all its tables for speedier future lookups. Issued when the adapter is first called.
|
19
|
+
#
|
20
|
+
# @since 0.2.0
|
21
|
+
def reconnect!
|
22
|
+
require "dynamoid/adapter/#{Dynamoid::Config.adapter}" unless Dynamoid::Adapter.const_defined?(Dynamoid::Config.adapter.camelcase)
|
23
|
+
@adapter = Dynamoid::Adapter.const_get(Dynamoid::Config.adapter.camelcase)
|
24
|
+
@adapter.connect! if @adapter.respond_to?(:connect!)
|
25
|
+
self.tables = benchmark('Cache Tables') {list_tables}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Shows how long it takes a method to run on the adapter. Useful for generating logged output.
|
29
|
+
#
|
30
|
+
# @param [Symbol] method the name of the method to appear in the log
|
31
|
+
# @param [Array] args the arguments to the method to appear in the log
|
32
|
+
# @yield the actual code to benchmark
|
33
|
+
#
|
34
|
+
# @return the result of the yield
|
35
|
+
#
|
36
|
+
# @since 0.2.0
|
37
|
+
def benchmark(method, *args)
|
38
|
+
start = Time.now
|
39
|
+
result = yield
|
40
|
+
Dynamoid.logger.info "(#{((Time.now - start) * 1000.0).round(2)} ms) #{method.to_s.split('_').collect(&:upcase).join(' ')}#{ " - #{args.inspect}" unless args.nil? || args.empty? }"
|
41
|
+
return result
|
42
|
+
end
|
43
|
+
|
44
|
+
# Write an object to the adapter. Partition it to a randomly selected key first if necessary.
|
45
|
+
#
|
46
|
+
# @param [String] table the name of the table to write the object to
|
47
|
+
# @param [Object] object the object itself
|
48
|
+
#
|
49
|
+
# @return [Object] the persisted object
|
50
|
+
#
|
51
|
+
# @since 0.2.0
|
52
|
+
def write(table, object)
|
53
|
+
if Dynamoid::Config.partitioning? && object[:id]
|
54
|
+
object[:id] = "#{object[:id]}.#{Random.rand(Dynamoid::Config.partition_size)}"
|
55
|
+
object[:updated_at] = Time.now.to_f
|
56
|
+
end
|
57
|
+
put_item(table, object)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Read one or many keys from the selected table. This method intelligently calls batch_get or get on the underlying adapter depending on
|
61
|
+
# whether ids is a range or a single key: additionally, if partitioning is enabled, it batch_gets all keys in the partition space
|
62
|
+
# automatically. Finally, if a range key is present, it will also interpolate that into the ids so that the batch get will acquire the
|
63
|
+
# correct record.
|
64
|
+
#
|
65
|
+
# @param [String] table the name of the table to write the object to
|
66
|
+
# @param [Array] ids to fetch, can also be a string of just one id
|
67
|
+
# @param [Number] range_key the range key of the record
|
68
|
+
#
|
69
|
+
# @since 0.2.0
|
70
|
+
def read(table, ids, options = {})
|
71
|
+
range_key = options[:range_key]
|
72
|
+
if ids.respond_to?(:each)
|
73
|
+
ids = ids.collect{|id| range_key ? [id, range_key] : id}
|
74
|
+
if Dynamoid::Config.partitioning?
|
75
|
+
results = batch_get_item(table => id_with_partitions(ids))
|
76
|
+
{table => result_for_partition(results[table])}
|
77
|
+
else
|
78
|
+
batch_get_item(table => ids)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
if Dynamoid::Config.partitioning?
|
82
|
+
ids = range_key ? [[ids, range_key]] : ids
|
83
|
+
results = batch_get_item(table => id_with_partitions(ids))
|
84
|
+
result_for_partition(results[table]).first
|
85
|
+
else
|
86
|
+
get_item(table, ids, options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Delete an item from a table. If partitioning is turned on, deletes all partitioned keys as well.
|
92
|
+
#
|
93
|
+
# @param [String] table the name of the table to write the object to
|
94
|
+
# @param [String] id the id of the record
|
95
|
+
# @param [Number] range_key the range key of the record
|
96
|
+
#
|
97
|
+
# @since 0.2.0
|
98
|
+
def delete(table, id, options = {})
|
99
|
+
if Dynamoid::Config.partitioning?
|
100
|
+
id_with_partitions(id).each {|i| delete_item(table, i, options)}
|
101
|
+
else
|
102
|
+
delete_item(table, id, options)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Scans a table. Generally quite slow; try to avoid using scan if at all possible.
|
107
|
+
#
|
108
|
+
# @param [String] table the name of the table to write the object to
|
109
|
+
# @param [Hash] scan_hash a hash of attributes: matching records will be returned by the scan
|
110
|
+
#
|
111
|
+
# @since 0.2.0
|
112
|
+
def scan(table, query, opts = {})
|
113
|
+
if Dynamoid::Config.partitioning?
|
114
|
+
results = benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
|
115
|
+
result_for_partition(results)
|
116
|
+
else
|
117
|
+
benchmark('Scan', table, query) {adapter.scan(table, query, opts)}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
[:batch_get_item, :create_table, :delete_item, :delete_table, :get_item, :list_tables, :put_item].each do |m|
|
122
|
+
# Method delegation with benchmark to the underlying adapter. Faster than relying on method_missing.
|
123
|
+
#
|
124
|
+
# @since 0.2.0
|
125
|
+
define_method(m) do |*args|
|
126
|
+
benchmark("#{m.to_s}", args) {adapter.send(m, *args)}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Takes a list of ids and returns them with partitioning added. If an array of arrays is passed, we assume the second key is the range key
|
131
|
+
# and pass it in unchanged.
|
132
|
+
#
|
133
|
+
# @example Partition id 1
|
134
|
+
# Dynamoid::Adapter.id_with_partitions(['1']) # ['1.0', '1.1', '1.2', ..., '1.199']
|
135
|
+
# @example Partition id 1 and range_key 1.0
|
136
|
+
# Dynamoid::Adapter.id_with_partitions([['1', 1.0]]) # [['1.0', 1.0], ['1.1', 1.0], ['1.2', 1.0], ..., ['1.199', 1.0]]
|
137
|
+
#
|
138
|
+
# @param [Array] ids array of ids to partition
|
139
|
+
#
|
140
|
+
# @since 0.2.0
|
141
|
+
def id_with_partitions(ids)
|
142
|
+
Array(ids).collect {|id| (0...Dynamoid::Config.partition_size).collect{|n| id.is_a?(Array) ? ["#{id.first}.#{n}", id.last] : "#{id}.#{n}"}}.flatten(1)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Takes an array of results that are partitioned, find the most recently updated one, and return only it. Compares each result by
|
146
|
+
# their id and updated_at attributes; if the updated_at is the greatest, then it must be the correct result.
|
147
|
+
#
|
148
|
+
# @param [Array] returned partitioned results from a query
|
149
|
+
#
|
150
|
+
# @since 0.2.0
|
151
|
+
def result_for_partition(results)
|
152
|
+
{}.tap do |hash|
|
153
|
+
Array(results).each do |result|
|
154
|
+
next if result.nil?
|
155
|
+
#Need to find the value of id with out the . and partition number
|
156
|
+
partition = result[:id].split('.').last
|
157
|
+
id = result[:id].split(".#{partition}").first
|
158
|
+
|
159
|
+
if !hash[id] || (result[:updated_at] > hash[id][:updated_at])
|
160
|
+
result[:id] = id
|
161
|
+
hash[id] = result
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end.values
|
165
|
+
end
|
166
|
+
|
167
|
+
# Delegate all methods that aren't defind here to the underlying adapter.
|
168
|
+
#
|
169
|
+
# @since 0.2.0
|
170
|
+
def method_missing(method, *args, &block)
|
171
|
+
return benchmark(method, *args) {adapter.send(method, *args, &block)} if @adapter.respond_to?(method)
|
172
|
+
super
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'aws'
|
3
|
+
|
4
|
+
module Dynamoid
|
5
|
+
module Adapter
|
6
|
+
|
7
|
+
# The AwsSdk adapter provides support for the AWS-SDK for Ruby gem.
|
8
|
+
# More information is available at that Gem's Github page:
|
9
|
+
# https://github.com/amazonwebservices/aws-sdk-for-ruby
|
10
|
+
#
|
11
|
+
module AwsSdk
|
12
|
+
extend self
|
13
|
+
@@connection = nil
|
14
|
+
|
15
|
+
# Establish the connection to DynamoDB.
|
16
|
+
#
|
17
|
+
# @return [AWS::DynamoDB::Connection] the raw DynamoDB connection
|
18
|
+
#
|
19
|
+
# @since 0.2.0
|
20
|
+
def connect!
|
21
|
+
@@connection = AWS::DynamoDB.new(:access_key_id => Dynamoid::Config.access_key, :secret_access_key => Dynamoid::Config.secret_key, :dynamo_db_endpoint => Dynamoid::Config.endpoint, :use_ssl => Dynamoid::Config.use_ssl, :dynamo_db_port => Dynamoid::Config.port)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return the established connection.
|
25
|
+
#
|
26
|
+
# @return [AWS::DynamoDB::Connection] the raw DynamoDB connection
|
27
|
+
#
|
28
|
+
# @since 0.2.0
|
29
|
+
def connection
|
30
|
+
@@connection
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get many items at once from DynamoDB. More efficient than getting each item individually.
|
34
|
+
#
|
35
|
+
# @example Retrieve IDs 1 and 2 from the table testtable
|
36
|
+
# Dynamoid::Adapter::AwsSdk.batch_get_item('table1' => ['1', '2'])
|
37
|
+
#
|
38
|
+
# @param [Hash] options the hash of tables and IDs to retrieve
|
39
|
+
#
|
40
|
+
# @return [Hash] a hash where keys are the table names and the values are the retrieved items
|
41
|
+
#
|
42
|
+
# @since 0.2.0
|
43
|
+
def batch_get_item(options)
|
44
|
+
hash = Hash.new{|h, k| h[k] = []}
|
45
|
+
return hash if options.all?{|k, v| v.empty?}
|
46
|
+
options.each do |t, ids|
|
47
|
+
Array(ids).in_groups_of(100, false) do |group|
|
48
|
+
batch = AWS::DynamoDB::BatchGet.new(:config => @@connection.config)
|
49
|
+
batch.table(t, :all, Array(group)) unless group.nil? || group.empty?
|
50
|
+
batch.each do |table_name, attributes|
|
51
|
+
hash[table_name] << attributes.symbolize_keys!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
hash
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create a table on DynamoDB. This usually takes a long time to complete.
|
59
|
+
#
|
60
|
+
# @param [String] table_name the name of the table to create
|
61
|
+
# @param [Symbol] key the table's primary key (defaults to :id)
|
62
|
+
# @param [Hash] options provide a range_key here if you want one for the table
|
63
|
+
#
|
64
|
+
# @since 0.2.0
|
65
|
+
def create_table(table_name, key = :id, options = {})
|
66
|
+
Dynamoid.logger.info "Creating #{table_name} table. This could take a while."
|
67
|
+
options[:hash_key] ||= {key.to_sym => :string}
|
68
|
+
read_capacity = options[:read_capacity] || Dynamoid::Config.read_capacity
|
69
|
+
write_capacity = options[:write_capacity] || Dynamoid::Config.write_capacity
|
70
|
+
table = @@connection.tables.create(table_name, read_capacity, write_capacity, options)
|
71
|
+
sleep 0.5 while table.status == :creating
|
72
|
+
return table
|
73
|
+
end
|
74
|
+
|
75
|
+
# Removes an item from DynamoDB.
|
76
|
+
#
|
77
|
+
# @param [String] table_name the name of the table
|
78
|
+
# @param [String] key the hash key of the item to delete
|
79
|
+
# @param [Number] range_key the range key of the item to delete, required if the table has a composite key
|
80
|
+
#
|
81
|
+
# @since 0.2.0
|
82
|
+
def delete_item(table_name, key, options = {})
|
83
|
+
range_key = options.delete(:range_key)
|
84
|
+
table = get_table(table_name)
|
85
|
+
result = table.items.at(key, range_key)
|
86
|
+
result.delete unless result.attributes.to_h.empty?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
# Deletes an entire table from DynamoDB. Only 10 tables can be in the deleting state at once,
|
91
|
+
# so if you have more this method may raise an exception.
|
92
|
+
#
|
93
|
+
# @param [String] table_name the name of the table to destroy
|
94
|
+
#
|
95
|
+
# @since 0.2.0
|
96
|
+
def delete_table(table_name)
|
97
|
+
Dynamoid.logger.info "Deleting #{table_name} table. This could take a while."
|
98
|
+
table = @@connection.tables[table_name]
|
99
|
+
table.delete
|
100
|
+
sleep 0.5 while table.exists? == true
|
101
|
+
end
|
102
|
+
|
103
|
+
# @todo Add a DescribeTable method.
|
104
|
+
|
105
|
+
# Fetches an item from DynamoDB.
|
106
|
+
#
|
107
|
+
# @param [String] table_name the name of the table
|
108
|
+
# @param [String] key the hash key of the item to find
|
109
|
+
# @param [Number] range_key the range key of the item to find, required if the table has a composite key
|
110
|
+
#
|
111
|
+
# @return [Hash] a hash representing the raw item in DynamoDB
|
112
|
+
#
|
113
|
+
# @since 0.2.0
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
def get_item(table_name, key, options = {})
|
118
|
+
range_key = options.delete(:range_key)
|
119
|
+
table = get_table(table_name)
|
120
|
+
|
121
|
+
result = table.items.at(key, range_key).attributes.to_h(options)
|
122
|
+
|
123
|
+
if result.empty?
|
124
|
+
nil
|
125
|
+
else
|
126
|
+
result.symbolize_keys!
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def update_item(table_name, key, options = {}, &block)
|
131
|
+
range_key = options.delete(:range_key)
|
132
|
+
conditions = options.delete(:conditions) || {}
|
133
|
+
table = get_table(table_name)
|
134
|
+
item = table.items.at(key, range_key)
|
135
|
+
item.attributes.update(conditions.merge(:return => :all_new), &block)
|
136
|
+
rescue AWS::DynamoDB::Errors::ConditionalCheckFailedException
|
137
|
+
raise Dynamoid::Errors::ConditionalCheckFailedException
|
138
|
+
end
|
139
|
+
|
140
|
+
# List all tables on DynamoDB.
|
141
|
+
#
|
142
|
+
# @since 0.2.0
|
143
|
+
def list_tables
|
144
|
+
@@connection.tables.collect(&:name)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Persists an item on DynamoDB.
|
148
|
+
#
|
149
|
+
# @param [String] table_name the name of the table
|
150
|
+
# @param [Object] object a hash or Dynamoid object to persist
|
151
|
+
#
|
152
|
+
# @since 0.2.0
|
153
|
+
def put_item(table_name, object)
|
154
|
+
table = get_table(table_name)
|
155
|
+
table.items.create(object.delete_if{|k, v| v.nil? || (v.respond_to?(:empty?) && v.empty?)})
|
156
|
+
end
|
157
|
+
|
158
|
+
# Query the DynamoDB table. This employs DynamoDB's indexes so is generally faster than scanning, but is
|
159
|
+
# only really useful for range queries, since it can only find by one hash key at once. Only provide
|
160
|
+
# one range key to the hash.
|
161
|
+
#
|
162
|
+
# @param [String] table_name the name of the table
|
163
|
+
# @param [Hash] opts the options to query the table with
|
164
|
+
# @option opts [String] :hash_value the value of the hash key to find
|
165
|
+
# @option opts [Range] :range_value find the range key within this range
|
166
|
+
# @option opts [Number] :range_greater_than find range keys greater than this
|
167
|
+
# @option opts [Number] :range_less_than find range keys less than this
|
168
|
+
# @option opts [Number] :range_gte find range keys greater than or equal to this
|
169
|
+
# @option opts [Number] :range_lte find range keys less than or equal to this
|
170
|
+
#
|
171
|
+
# @return [Array] an array of all matching items
|
172
|
+
#
|
173
|
+
# @since 0.2.0
|
174
|
+
def query(table_name, opts = {})
|
175
|
+
table = get_table(table_name)
|
176
|
+
|
177
|
+
consistent_opts = { :consistent_read => opts[:consistent_read] || false }
|
178
|
+
if table.composite_key?
|
179
|
+
results = []
|
180
|
+
table.items.query(opts).each {|data| results << data.attributes.to_h(consistent_opts).symbolize_keys!}
|
181
|
+
results
|
182
|
+
else
|
183
|
+
get_item(table_name, opts[:hash_value])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Scan the DynamoDB table. This is usually a very slow operation as it naively filters all data on
|
188
|
+
# the DynamoDB servers.
|
189
|
+
#
|
190
|
+
# @param [String] table_name the name of the table
|
191
|
+
# @param [Hash] scan_hash a hash of attributes: matching records will be returned by the scan
|
192
|
+
#
|
193
|
+
# @return [Array] an array of all matching items
|
194
|
+
#
|
195
|
+
# @since 0.2.0
|
196
|
+
def scan(table_name, scan_hash, select_opts)
|
197
|
+
table = get_table(table_name)
|
198
|
+
results = []
|
199
|
+
table.items.where(scan_hash).select(select_opts) do |data|
|
200
|
+
results << data.attributes.symbolize_keys!
|
201
|
+
end
|
202
|
+
results
|
203
|
+
end
|
204
|
+
|
205
|
+
# @todo Add an UpdateItem method.
|
206
|
+
|
207
|
+
# @todo Add an UpdateTable method.
|
208
|
+
|
209
|
+
def get_table(table_name)
|
210
|
+
unless table = table_cache[table_name]
|
211
|
+
table = @@connection.tables[table_name]
|
212
|
+
table.load_schema
|
213
|
+
table_cache[table_name] = table
|
214
|
+
end
|
215
|
+
table
|
216
|
+
end
|
217
|
+
|
218
|
+
def table_cache
|
219
|
+
@table_cache ||= {}
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|