s3fsr 1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ module AWS
2
+ module S3
3
+ # Anything you do that makes a request to S3 could result in an error. If it does, the AWS::S3 library will raise an exception
4
+ # specific to the error. All exception that are raised as a result of a request returning an error response inherit from the
5
+ # ResponseError exception. So should you choose to rescue any such exception, you can simple rescue ResponseError.
6
+ #
7
+ # Say you go to delete a bucket, but the bucket turns out to not be empty. This results in a BucketNotEmpty error (one of the many
8
+ # errors listed at http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ErrorCodeList.html):
9
+ #
10
+ # begin
11
+ # Bucket.delete('jukebox')
12
+ # rescue ResponseError => error
13
+ # # ...
14
+ # end
15
+ #
16
+ # Once you've captured the exception, you can extract the error message from S3, as well as the full error response, which includes
17
+ # things like the HTTP response code:
18
+ #
19
+ # error
20
+ # # => #<AWS::S3::BucketNotEmpty The bucket you tried to delete is not empty>
21
+ # error.message
22
+ # # => "The bucket you tried to delete is not empty"
23
+ # error.response.code
24
+ # # => 409
25
+ #
26
+ # You could use this information to redisplay the error in a way you see fit, or just to log the error and continue on.
27
+ class Error
28
+ #:stopdoc:
29
+ attr_accessor :response
30
+ def initialize(error, response = nil)
31
+ @error = error
32
+ @response = response
33
+ @container = AWS::S3
34
+ find_or_create_exception!
35
+ end
36
+
37
+ def raise
38
+ Kernel.raise exception.new(message, response)
39
+ end
40
+
41
+ private
42
+ attr_reader :error, :exception, :container
43
+
44
+ def find_or_create_exception!
45
+ @exception = container.const_defined?(code) ? find_exception : create_exception
46
+ end
47
+
48
+ def find_exception
49
+ exception_class = container.const_get(code)
50
+ Kernel.raise ExceptionClassClash.new(exception_class) unless exception_class.ancestors.include?(ResponseError)
51
+ exception_class
52
+ end
53
+
54
+ def create_exception
55
+ container.const_set(code, Class.new(ResponseError))
56
+ end
57
+
58
+ def method_missing(method, *args, &block)
59
+ # We actually want nil if the attribute is nil. So we use has_key? rather than [] + ||.
60
+ if error.has_key?(method.to_s)
61
+ error[method.to_s]
62
+ else
63
+ super
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ #:startdoc:
@@ -0,0 +1,133 @@
1
+ module AWS
2
+ module S3
3
+
4
+ # Abstract super class of all AWS::S3 exceptions
5
+ class S3Exception < StandardError
6
+ end
7
+
8
+ # All responses with a code between 300 and 599 that contain an <Error></Error> body are wrapped in an
9
+ # ErrorResponse which contains an Error object. This Error class generates a custom exception with the name
10
+ # of the xml Error and its message. All such runtime generated exception classes descend from ResponseError
11
+ # and contain the ErrorResponse object so that all code that makes a request can rescue ResponseError and get
12
+ # access to the ErrorResponse.
13
+ class ResponseError < S3Exception
14
+ attr_reader :response
15
+ def initialize(message, response)
16
+ @response = response
17
+ super(message)
18
+ end
19
+ end
20
+
21
+ #:stopdoc:
22
+
23
+ # Most ResponseError's are created just time on a need to have basis, but we explicitly define the
24
+ # InternalError exception because we want to explicitly rescue InternalError in some cases.
25
+ class InternalError < ResponseError
26
+ end
27
+
28
+ class NoSuchKey < ResponseError
29
+ end
30
+
31
+ class RequestTimeout < ResponseError
32
+ end
33
+
34
+ # Abstract super class for all invalid options.
35
+ class InvalidOption < S3Exception
36
+ end
37
+
38
+ # Raised if an invalid value is passed to the <tt>:access</tt> option when creating a Bucket or an S3Object.
39
+ class InvalidAccessControlLevel < InvalidOption
40
+ def initialize(valid_levels, access_level)
41
+ super("Valid access control levels are #{valid_levels.inspect}. You specified `#{access_level}'.")
42
+ end
43
+ end
44
+
45
+ # Raised if either the access key id or secret access key arguments are missing when establishing a connection.
46
+ class MissingAccessKey < InvalidOption
47
+ def initialize(missing_keys)
48
+ key_list = missing_keys.map {|key| key.to_s}.join(' and the ')
49
+ super("You did not provide both required access keys. Please provide the #{key_list}.")
50
+ end
51
+ end
52
+
53
+ # Raised if a request is attempted before any connections have been established.
54
+ class NoConnectionEstablished < S3Exception
55
+ end
56
+
57
+ # Raised if an unrecognized option is passed when establishing a connection.
58
+ class InvalidConnectionOption < InvalidOption
59
+ def initialize(invalid_options)
60
+ message = "The following connection options are invalid: #{invalid_options.join(', ')}. " +
61
+ "The valid connection options are: #{Connection::Options.valid_options.join(', ')}."
62
+ super(message)
63
+ end
64
+ end
65
+
66
+ # Raised if an invalid bucket name is passed when creating a new Bucket.
67
+ class InvalidBucketName < S3Exception
68
+ def initialize(invalid_name)
69
+ message = "`#{invalid_name}' is not a valid bucket name. " +
70
+ "Bucket names must be between 3 and 255 bytes and " +
71
+ "can contain letters, numbers, dashes and underscores."
72
+ super(message)
73
+ end
74
+ end
75
+
76
+ # Raised if an invalid key name is passed when creating an S3Object.
77
+ class InvalidKeyName < S3Exception
78
+ def initialize(invalid_name)
79
+ message = "`#{invalid_name}' is not a valid key name. " +
80
+ "Key names must be no more than 1024 bytes long."
81
+ super(message)
82
+ end
83
+ end
84
+
85
+ # Raised if an invalid value is assigned to an S3Object's specific metadata name.
86
+ class InvalidMetadataValue < S3Exception
87
+ def initialize(invalid_names)
88
+ message = "The following metadata names have invalid values: #{invalid_names.join(', ')}. " +
89
+ "Metadata can not be larger than 2kilobytes."
90
+ super(message)
91
+ end
92
+ end
93
+
94
+ # Raised if the current bucket can not be inferred when not explicitly specifying the target bucket in the calling
95
+ # method's arguments.
96
+ class CurrentBucketNotSpecified < S3Exception
97
+ def initialize(address)
98
+ message = "No bucket name can be inferred from your current connection's address (`#{address}')"
99
+ super(message)
100
+ end
101
+ end
102
+
103
+ # Raised when an orphaned S3Object belonging to no bucket tries to access its (non-existant) bucket.
104
+ class NoBucketSpecified < S3Exception
105
+ def initialize
106
+ super('The current object must have its bucket set')
107
+ end
108
+ end
109
+
110
+ # Raised if an attempt is made to save an S3Object that does not have a key set.
111
+ class NoKeySpecified < S3Exception
112
+ def initialize
113
+ super('The current object must have its key set')
114
+ end
115
+ end
116
+
117
+ # Raised if you try to save a deleted object.
118
+ class DeletedObject < S3Exception
119
+ def initialize
120
+ super('You can not save a deleted object')
121
+ end
122
+ end
123
+
124
+ class ExceptionClassClash < S3Exception #:nodoc:
125
+ def initialize(klass)
126
+ message = "The exception class you tried to create (`#{klass}') exists and is not an exception"
127
+ super(message)
128
+ end
129
+ end
130
+
131
+ #:startdoc:
132
+ end
133
+ end
@@ -0,0 +1,324 @@
1
+ #:stopdoc:
2
+
3
+ class Hash
4
+ def to_query_string(include_question_mark = true)
5
+ query_string = ''
6
+ unless empty?
7
+ query_string << '?' if include_question_mark
8
+ query_string << inject([]) do |params, (key, value)|
9
+ params << "#{key}=#{value}"
10
+ end.join('&')
11
+ end
12
+ query_string
13
+ end
14
+
15
+ def to_normalized_options
16
+ # Convert all option names to downcased strings, and replace underscores with hyphens
17
+ inject({}) do |normalized_options, (name, value)|
18
+ normalized_options[name.to_header] = value.to_s
19
+ normalized_options
20
+ end
21
+ end
22
+
23
+ def to_normalized_options!
24
+ replace(to_normalized_options)
25
+ end
26
+ end
27
+
28
+ class String
29
+ def previous!
30
+ self[-1] -= 1
31
+ self
32
+ end
33
+
34
+ def previous
35
+ dup.previous!
36
+ end
37
+
38
+ def to_header
39
+ downcase.tr('_', '-')
40
+ end
41
+
42
+ # ActiveSupport adds an underscore method to String so let's just use that one if
43
+ # we find that the method is already defined
44
+ def underscore
45
+ gsub(/::/, '/').
46
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
47
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
48
+ tr("-", "_").downcase
49
+ end unless public_method_defined? :underscore
50
+
51
+ def utf8?
52
+ scan(/[^\x00-\xa0]/u) { |s| s.unpack('U') }
53
+ true
54
+ rescue ArgumentError
55
+ false
56
+ end
57
+
58
+ # All paths in in S3 have to be valid unicode so this takes care of
59
+ # cleaning up any strings that aren't valid utf-8 according to String#utf8?
60
+ def remove_extended!
61
+ gsub!(/[\x80-\xFF]/) { "%02X" % $&[0] }
62
+ end
63
+
64
+ def remove_extended
65
+ dup.remove_extended!
66
+ end
67
+ end
68
+
69
+ class CoercibleString < String
70
+ class << self
71
+ def coerce(string)
72
+ new(string).coerce
73
+ end
74
+ end
75
+
76
+ def coerce
77
+ case self
78
+ when 'true': true
79
+ when 'false': false
80
+ # Don't coerce numbers that start with zero
81
+ when /^[1-9]+\d*$/: Integer(self)
82
+ when datetime_format: Time.parse(self)
83
+ else
84
+ self
85
+ end
86
+ end
87
+
88
+ private
89
+ # Lame hack since Date._parse is so accepting. S3 dates are of the form: '2006-10-29T23:14:47.000Z'
90
+ # so unless the string looks like that, don't even try, otherwise it might convert an object's
91
+ # key from something like '03 1-2-3-Apple-Tree.mp3' to Sat Feb 03 00:00:00 CST 2001.
92
+ def datetime_format
93
+ /^\d{4}-\d{2}-\d{2}\w\d{2}:\d{2}:\d{2}/
94
+ end
95
+ end
96
+
97
+ class Symbol
98
+ def to_header
99
+ to_s.to_header
100
+ end
101
+ end
102
+
103
+ module Kernel
104
+ def __method__(depth = 0)
105
+ caller[depth][/`([^']+)'/, 1]
106
+ end #if RUBY_VERSION < '1.8.7'
107
+
108
+ def memoize(reload = false, storage = nil)
109
+ storage = "@#{storage || __method__(1)}"
110
+ if reload
111
+ instance_variable_set(storage, nil)
112
+ else
113
+ if cache = instance_variable_get(storage)
114
+ return cache
115
+ end
116
+ end
117
+ instance_variable_set(storage, yield)
118
+ end
119
+
120
+ def require_library_or_gem(library)
121
+ require library
122
+ rescue LoadError => library_not_installed
123
+ begin
124
+ require 'rubygems'
125
+ require library
126
+ rescue LoadError
127
+ raise library_not_installed
128
+ end
129
+ end
130
+ end
131
+
132
+ class Object
133
+ def returning(value)
134
+ yield(value)
135
+ value
136
+ end
137
+ end
138
+
139
+ class Module
140
+ def memoized(method_name)
141
+ original_method = "unmemoized_#{method_name}_#{Time.now.to_i}"
142
+ alias_method original_method, method_name
143
+ module_eval(<<-EVAL, __FILE__, __LINE__)
144
+ def #{method_name}(reload = false, *args, &block)
145
+ memoize(reload) do
146
+ send(:#{original_method}, *args, &block)
147
+ end
148
+ end
149
+ EVAL
150
+ end
151
+
152
+ def constant(name, value)
153
+ unless const_defined?(name)
154
+ const_set(name, value)
155
+ module_eval(<<-EVAL, __FILE__, __LINE__)
156
+ def self.#{name.to_s.downcase}
157
+ #{name.to_s}
158
+ end
159
+ EVAL
160
+ end
161
+ end
162
+
163
+ # Transforms MarcelBucket into
164
+ #
165
+ # class MarcelBucket < AWS::S3::Bucket
166
+ # set_current_bucket_to 'marcel'
167
+ # end
168
+ def const_missing_from_s3_library(sym)
169
+ if sym.to_s =~ /^(\w+)(Bucket|S3Object)$/
170
+ const = const_set(sym, Class.new(AWS::S3.const_get($2)))
171
+ const.current_bucket = $1.underscore
172
+ const
173
+ else
174
+ const_missing_not_from_s3_library(sym)
175
+ end
176
+ end
177
+ alias_method :const_missing_not_from_s3_library, :const_missing
178
+ alias_method :const_missing, :const_missing_from_s3_library
179
+ end
180
+
181
+
182
+ class Class # :nodoc:
183
+ def cattr_reader(*syms)
184
+ syms.flatten.each do |sym|
185
+ class_eval(<<-EOS, __FILE__, __LINE__)
186
+ unless defined? @@#{sym}
187
+ @@#{sym} = nil
188
+ end
189
+
190
+ def self.#{sym}
191
+ @@#{sym}
192
+ end
193
+
194
+ def #{sym}
195
+ @@#{sym}
196
+ end
197
+ EOS
198
+ end
199
+ end
200
+
201
+ def cattr_writer(*syms)
202
+ syms.flatten.each do |sym|
203
+ class_eval(<<-EOS, __FILE__, __LINE__)
204
+ unless defined? @@#{sym}
205
+ @@#{sym} = nil
206
+ end
207
+
208
+ def self.#{sym}=(obj)
209
+ @@#{sym} = obj
210
+ end
211
+
212
+ def #{sym}=(obj)
213
+ @@#{sym} = obj
214
+ end
215
+ EOS
216
+ end
217
+ end
218
+
219
+ def cattr_accessor(*syms)
220
+ cattr_reader(*syms)
221
+ cattr_writer(*syms)
222
+ end
223
+ end if Class.instance_methods(false).grep(/^cattr_(?:reader|writer|accessor)$/).empty?
224
+
225
+ module SelectiveAttributeProxy
226
+ def self.included(klass)
227
+ klass.extend(ClassMethods)
228
+ klass.class_eval(<<-EVAL, __FILE__, __LINE__)
229
+ cattr_accessor :attribute_proxy
230
+ cattr_accessor :attribute_proxy_options
231
+
232
+ # Default name for attribute storage
233
+ self.attribute_proxy = :attributes
234
+ self.attribute_proxy_options = {:exclusively => true}
235
+
236
+ private
237
+ # By default proxy all attributes
238
+ def proxiable_attribute?(name)
239
+ return true unless self.class.attribute_proxy_options[:exclusively]
240
+ send(self.class.attribute_proxy).has_key?(name)
241
+ end
242
+
243
+ def method_missing(method, *args, &block)
244
+ # Autovivify attribute storage
245
+ if method == self.class.attribute_proxy
246
+ ivar = "@\#{method}"
247
+ instance_variable_set(ivar, {}) unless instance_variable_get(ivar).is_a?(Hash)
248
+ instance_variable_get(ivar)
249
+ # Delegate to attribute storage
250
+ elsif method.to_s =~ /^(\\w+)(=?)$/ && proxiable_attribute?($1)
251
+ attributes_hash_name = self.class.attribute_proxy
252
+ $2.empty? ? send(attributes_hash_name)[$1] : send(attributes_hash_name)[$1] = args.first
253
+ else
254
+ super
255
+ end
256
+ end
257
+ EVAL
258
+ end
259
+
260
+ module ClassMethods
261
+ def proxy_to(attribute_name, options = {})
262
+ if attribute_name.is_a?(Hash)
263
+ options = attribute_name
264
+ else
265
+ self.attribute_proxy = attribute_name
266
+ end
267
+ self.attribute_proxy_options = options
268
+ end
269
+ end
270
+ end
271
+
272
+ # When streaming data up, Net::HTTPGenericRequest hard codes a chunk size of 1k. For large files this
273
+ # is an unfortunately low chunk size, so here we make it use a much larger default size and move it into a method
274
+ # so that the implementation of send_request_with_body_stream doesn't need to be changed to change the chunk size (at least not anymore
275
+ # than I've already had to...).
276
+ module Net
277
+ class HTTPGenericRequest
278
+ def send_request_with_body_stream(sock, ver, path, f)
279
+ raise ArgumentError, "Content-Length not given and Transfer-Encoding is not `chunked'" unless content_length() or chunked?
280
+ unless content_type()
281
+ warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
282
+ set_content_type 'application/x-www-form-urlencoded'
283
+ end
284
+ write_header sock, ver, path
285
+ if chunked?
286
+ while s = f.read(chunk_size)
287
+ sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
288
+ end
289
+ sock.write "0\r\n\r\n"
290
+ else
291
+ while s = f.read(chunk_size)
292
+ sock.write s
293
+ end
294
+ end
295
+ end
296
+
297
+ def chunk_size
298
+ 1048576 # 1 megabyte
299
+ end
300
+ end
301
+
302
+ # Net::HTTP before 1.8.4 doesn't have the use_ssl? method or the Delete request type
303
+ class HTTP
304
+ def use_ssl?
305
+ @use_ssl
306
+ end unless public_method_defined? :use_ssl?
307
+
308
+ class Delete < HTTPRequest
309
+ METHOD = 'DELETE'
310
+ REQUEST_HAS_BODY = false
311
+ RESPONSE_HAS_BODY = true
312
+ end unless const_defined? :Delete
313
+ end
314
+ end
315
+
316
+ class XmlGenerator < String #:nodoc:
317
+ attr_reader :xml
318
+ def initialize
319
+ @xml = Builder::XmlMarkup.new(:indent => 2, :target => self)
320
+ super()
321
+ build
322
+ end
323
+ end
324
+ #:startdoc: