dynamodb 0.0.2 → 1.1.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.
@@ -0,0 +1,51 @@
1
+ module DynamoDB
2
+ # Successful response from Dynamo
3
+ class SuccessResponse
4
+ attr_reader :http_response
5
+
6
+ def initialize(http_response)
7
+ @http_response = http_response
8
+ end
9
+
10
+ def hash_key_element
11
+ DynamoDB.deserialize(data["LastEvaluatedKey"]["HashKeyElement"])
12
+ end
13
+
14
+ def range_key_element
15
+ DynamoDB.deserialize(data["LastEvaluatedKey"]["RangeKeyElement"])
16
+ end
17
+
18
+ # Return single item response as a Hash
19
+ #
20
+ # Some DynamoDB operations, such as `GetItem`, will only return a
21
+ # single 'Item' entry. This converts that entry in a Hash where
22
+ # values have been type-casted into their Ruby equivalents.
23
+ def item
24
+ return unless data["Item"]
25
+ @item ||= DynamoDB.deserialize(data["Item"])
26
+ end
27
+
28
+ # Return an Array of item responses
29
+ #
30
+ # DynamoDB operations like `Query` return a collection of entries
31
+ # under the 'Items' key. This returns an Array of Hashes where
32
+ # values have been casted to their Ruby equivalents.
33
+ def items
34
+ return unless data["Items"]
35
+ @items ||= data["Items"].map { |i| DynamoDB.deserialize(i) }
36
+ end
37
+
38
+ def success?
39
+ true
40
+ end
41
+
42
+ def body
43
+ http_response.body
44
+ end
45
+
46
+ # Access the deserialized JSON response body
47
+ def data
48
+ @data ||= MultiJson.load(http_response.body)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module DynamoDB
2
+ VERSION = "1.1.1"
3
+ end
@@ -0,0 +1,226 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'net/http/connection_pool/session'
15
+ require 'net/http/connection_pool/connection'
16
+ require 'thread'
17
+ require 'logger'
18
+
19
+ # A wrapper around Net::HTTP that provides a manged pool of persistant HTTP
20
+ # connections.
21
+ #
22
+ # pool = Net::HTTP::ConnectionPool.new
23
+ # connection = pool.connection_for('domain.com')
24
+ # connection.request(Net::HTTP::Get.new('/')) do |resp|
25
+ # # Connection#request yields Net::HTTPResponse objects
26
+ # puts resp.body
27
+ # end
28
+ #
29
+ # @private
30
+ class Net::HTTP::ConnectionPool
31
+
32
+ # @param [Hash] options
33
+ #
34
+ # @option options [Numeric] :http_idle_timeout (60) The number of seconds a
35
+ # connection is allowed to sit idle before it is closed and removed
36
+ # from the pool.
37
+ #
38
+ # @option options [Numeric] :http_open_timeout (15) The number of seconds to
39
+ # wait when opening a http session before raising a timeout exception.
40
+ #
41
+ # @option options [Boolean] :http_wire_trace (false) When +true+, HTTP
42
+ # debug output will be sent to the +:logger+.
43
+ #
44
+ # @option options [Logger] :logger (Logger.new($stdout)) Where debug out
45
+ # is sent (wire traces).
46
+ #
47
+ def initialize options = {}
48
+ @pool_mutex = Mutex.new
49
+ @pool = []
50
+ @open_timeout = options[:http_open_timeout] || 15
51
+ @idle_timeout = options[:http_idle_timeout] || 60
52
+ @http_wire_trace = !!options[:http_wire_trace]
53
+ if logger = options[:logger]
54
+ @logger = logger
55
+ elsif http_wire_trace?
56
+ @logger = Logger.new($stdout)
57
+ end
58
+ end
59
+
60
+ # @return [Integer]
61
+ attr_reader :idle_timeout
62
+
63
+ # @return [Integer]
64
+ attr_accessor :open_timeout
65
+
66
+ # @return [Boolean] Returns +true+ when HTTP debug output (wire traces)
67
+ # will be logged.
68
+ attr_reader :http_wire_trace
69
+
70
+ alias_method :http_wire_trace?, :http_wire_trace
71
+ alias_method :log_wire_trace?, :http_wire_trace
72
+ alias_method :log_wire_trace, :http_wire_trace
73
+
74
+ # @return [Logger] Where debug output is sent.
75
+ attr_reader :logger
76
+
77
+ # Requests a connection object from the connection pool.
78
+ #
79
+ # connection = pool.connection_for('domain.com')
80
+ # connection.request(Net::HTTP::Get.new('/index.html')) {|resp|}
81
+ # connection.request(Net::HTTP::Get.new('/about.html')) {|resp|}
82
+ #
83
+ # # same thing in block form
84
+ # pool.connection_for('domain.com') do |connection|
85
+ # connection.request(Net::HTTP::Get.new('/index.html')) {|resp|}
86
+ # connection.request(Net::HTTP::Get.new('/about.html')) {|resp|}
87
+ # end
88
+ #
89
+ # Because the pool manages HTTP sessions you do not have to
90
+ # worry about closing a connection or returning a connection
91
+ # to the pool.
92
+ #
93
+ # @param [String] host
94
+ #
95
+ # @param [Hash] options
96
+ #
97
+ # @option options [Integer] :port Which port the connection should use.
98
+ # Defaults to 80, unless +:ssl+ is +true+, then it defaults to 443.
99
+ #
100
+ # @option options [Boolean] :ssl If the connection should be made over
101
+ # SSL. Defaults to +false+, unless +:port+ is 443, then it defaults
102
+ # to +true+.
103
+ #
104
+ # @option options [Boolean] :ssl_verify_peer (true) If true, ssl
105
+ # connections should verify peer certificates. This should only ever be
106
+ # set false false for debugging purposes.
107
+ #
108
+ # @option options [String] :ssl_ca_file Full path to the SSL certificate
109
+ # authority bundle file that should be used when verifying peer
110
+ # certificates. If you do not pass +:ssl_ca_file+ or +:ssl_ca_path+
111
+ # the the system default will be used if available.
112
+ #
113
+ # @option options [String] :ssl_ca_path Full path of the directory that
114
+ # contains the unbundled SSL certificate authority files for verifying
115
+ # peer certificates. If you do not pass +:ssl_ca_file+ or +:ssl_ca_path+
116
+ # the the system default will be used if available.
117
+ #
118
+ # @option options [URI::HTTP,String] :proxy_uri (nil) A URI string or
119
+ # URI::HTTP object to use as a proxy. You should not provide
120
+ # +:proxy_uri+ with any other proxy options.
121
+ #
122
+ # :proxy_uri => 'http://user:pass@host.com:80'
123
+ #
124
+ # @option options [String] :proxy_address
125
+ #
126
+ # @option options [String] :proxy_port
127
+ #
128
+ # @option options [String] :proxy_user
129
+ #
130
+ # @option options [String] :proxy_password
131
+ #
132
+ # @yield [connection]
133
+ #
134
+ # @yieldparam [optional,Connection] connection
135
+ #
136
+ # @return [Connection]
137
+ #
138
+ def connection_for host, options = {}, &block
139
+ connection = Connection.new(self, host, options)
140
+ yield(connection) if block_given?
141
+ connection
142
+ end
143
+
144
+ # Returns the number of sessions currently in the pool, not counting those
145
+ # currently in use.
146
+ def size
147
+ @pool_mutex.synchronize { @pool.size }
148
+ end
149
+
150
+ # Removes stale http sessions from the pool (that have exceeded
151
+ # the idle timeout).
152
+ def clean!
153
+ @pool_mutex.synchronize { _clean }
154
+ end
155
+
156
+ # Closes and removes removes all sessions from the pool.
157
+ # If empty! is called while there are outstanding requests they may
158
+ # get checked back into the pool, leaving the pool in a non-empty state.
159
+ def empty!
160
+ @pool_mutex.synchronize do
161
+ @pool.each(&:finish)
162
+ @pool = []
163
+ end
164
+ end
165
+
166
+ # Makes a single HTTP request. See {Connection#request} for more information
167
+ # on making an HTTP request.
168
+ # @return [nil]
169
+ # @private
170
+ def request connection, *args, &block
171
+ session_for(connection) do |session|
172
+ session.read_timeout = connection.read_timeout
173
+ session.request(*args, &block)
174
+ end
175
+ end
176
+
177
+ protected
178
+
179
+ # Yields an open http session for the given connection.
180
+ def session_for connection, &block
181
+
182
+ session = nil
183
+
184
+ # search the pool for an idle session that can be used
185
+ @pool_mutex.synchronize do
186
+ _clean # removes stale sessions
187
+ session = @pool.find{|idle_session| idle_session.key == connection.key }
188
+ @pool.delete(session) if session
189
+ end
190
+
191
+ begin
192
+ # opens a new HTTP session if no suitable idle session was found
193
+ session = _create_session(connection) unless session
194
+ yield(session)
195
+ rescue Exception => error
196
+ session.finish if session
197
+ raise error
198
+ else
199
+ # only check the session back into the pool if no errors were raised
200
+ @pool_mutex.synchronize { @pool << session }
201
+ end
202
+
203
+ nil
204
+
205
+ end
206
+
207
+ def _create_session connection
208
+ Session.start(connection,
209
+ :open_timeout => open_timeout,
210
+ :debug_logger => log_wire_trace? ? logger : nil)
211
+ end
212
+
213
+ def _clean
214
+ now = Time.now
215
+ @pool.delete_if do |idle_session|
216
+ if
217
+ idle_session.last_used_at.nil? or
218
+ now - idle_session.last_used_at > idle_timeout
219
+ then
220
+ idle_session.finish
221
+ true
222
+ end
223
+ end
224
+ end
225
+
226
+ end
@@ -0,0 +1,189 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'uri'
15
+
16
+ class Net::HTTP::ConnectionPool
17
+
18
+ # Represents a HTTP connection. Call {#request} on a connection like
19
+ # you would with a Net::HTTPSession object.
20
+ #
21
+ # == Getting a Connection object
22
+ #
23
+ # To get a connection object, you start with a connection pool:
24
+ #
25
+ # pool = Net::HTTP::ConnectionPool.new
26
+ # connection = pool.connection_for('domain.com')
27
+ #
28
+ # {ConnectionPool#connection_for} accepts a number of options to control
29
+ # the connection settings (SSL, proxy, timeouts, etc).
30
+ #
31
+ # == Making Requests
32
+ #
33
+ # Given a connection object, you call #request. {Connection#request}
34
+ # yields Net::HTTPResponse objects (when given a block). You should
35
+ # read the response (via #body or #read_body) before the end of the
36
+ # block.
37
+ #
38
+ # connection.request(Net::HTTP::Get.new('/')) do |resp|
39
+ # puts resp.body
40
+ # end
41
+ #
42
+ class Connection
43
+
44
+ # Use {ConnectionPool#connection_for} to construct {Connection} objects.
45
+ # @private
46
+ def initialize pool, host, options = {}
47
+
48
+ @pool = pool
49
+
50
+ @host = host
51
+
52
+ @port = options.key?(:port) ? options[:port] : (options[:ssl] ? 443 : 80)
53
+
54
+ @ssl = options.key?(:ssl) ? options[:ssl] : (port == 443)
55
+
56
+ @ssl_verify_peer = options.key?(:ssl_verify_peer) ?
57
+ options[:ssl_verify_peer] : true
58
+
59
+ @ssl_ca_file = options[:ssl_ca_file]
60
+
61
+ @ssl_ca_path = options[:ssl_ca_path]
62
+
63
+ if uri = options[:proxy_uri]
64
+ uri = URI.parse(uri) if uri.is_a?(String)
65
+ @proxy_address = uri.host
66
+ @proxy_port = uri.port
67
+ @proxy_user = uri.user
68
+ @proxy_password = uri.password
69
+ else
70
+ @proxy_address = options[:proxy_address]
71
+ @proxy_port = options[:proxy_port]
72
+ @proxy_user = options[:proxy_user]
73
+ @proxy_password = options[:proxy_password]
74
+ end
75
+
76
+ @read_timeout = options[:read_timeout] || 60
77
+
78
+ end
79
+
80
+ # @return [ConnectionPool]
81
+ attr_reader :pool
82
+
83
+ # @return [String]
84
+ attr_reader :host
85
+
86
+ # @return [Integer]
87
+ attr_reader :port
88
+
89
+ # @return [Boolean]
90
+ attr_reader :ssl
91
+
92
+ # @return [Boolean]
93
+ attr_reader :ssl_verify_peer
94
+
95
+ # @return [String,nil]
96
+ attr_reader :ssl_ca_file
97
+
98
+ # @return [String,nil]
99
+ attr_reader :ssl_ca_path
100
+
101
+ # @return [String,nil]
102
+ attr_reader :proxy_address
103
+
104
+ # @return [Integer,nil]
105
+ attr_reader :proxy_port
106
+
107
+ # @return [String,nil]
108
+ attr_reader :proxy_user
109
+
110
+ # @return [String,nil]
111
+ attr_reader :proxy_password
112
+
113
+ # @return [Numeric,nil]
114
+ attr_accessor :read_timeout
115
+
116
+ # @return [Boolean] Returns +true+ if this connection requires SSL.
117
+ def ssl?
118
+ @ssl
119
+ end
120
+
121
+ # @return [Boolean] Returns +true+ if ssl connections should verify the
122
+ # peer certificate.
123
+ def ssl_verify_peer?
124
+ @ssl_verify_peer
125
+ end
126
+
127
+ # @return [Boolean] Returns +true+ if this connection proxies requests.
128
+ def proxy?
129
+ !!proxy_address
130
+ end
131
+
132
+ # Makes a single HTTP request. The Net::HTTPResponse is yielded to the
133
+ # given block.
134
+ #
135
+ # pool = Net::HTTP::ConnectionPool.new
136
+ # connection = pool.connection_for('www.google.com')
137
+ #
138
+ # connection.request(Net::HTTP::Get.new('/')) do |response|
139
+ # # yeilds a Net::HTTPResponse object
140
+ # puts "STATUS CODE: #{response.code}"
141
+ # puts "HEADERS: #{response.to_hash.inspect}"
142
+ # puts "BODY:\n#{response.body}"
143
+ # end
144
+ #
145
+ # If you want to read the HTTP response body in chunks (useful for
146
+ # large responses you do not want to load into memory), you should
147
+ # pass a block to the #read_body method of the yielded response.
148
+ #
149
+ # File.open('output.txt', 'w') do |file|
150
+ # connection.request(Net::HTTP::Get.new('/')) do |response|
151
+ # response.read_body do |chunk|
152
+ # file.write(chunk)
153
+ # end
154
+ # end
155
+ # end
156
+ #
157
+ # If you omit the block when calling #request, you will not be able
158
+ # to read the response. This method never returns the
159
+ # Net::HTTPResponse generated.
160
+ #
161
+ # This method passes *args to Net::HTTPSession#request. See the
162
+ # Ruby standard lib documentation for more documentation about
163
+ # accepted arguments.
164
+ #
165
+ # @note You should read the yielded response object before the end
166
+ # of the passed block. Do no save a reference to yielded response
167
+ # objects.
168
+ #
169
+ # @yield [response]
170
+ # @yieldparam [Net::HTTPResponse] response
171
+ # @return [nil]
172
+ def request *args, &block
173
+ pool.request(self, *args, &block)
174
+ end
175
+
176
+ # @return [String] Returns a key that can be used to group connections
177
+ # that may share the same HTTP sessions.
178
+ def key
179
+ @key ||= begin
180
+ %w(
181
+ host port
182
+ ssl ssl_verify_peer ssl_ca_file ssl_ca_path
183
+ proxy_address proxy_port proxy_user proxy_password
184
+ ).map{|part| send(part).to_s }.join(":")
185
+ end
186
+ end
187
+
188
+ end
189
+ end