dustMason-right_aws 2.1.0

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.
Files changed (70) hide show
  1. data/History.txt +305 -0
  2. data/Manifest.txt +60 -0
  3. data/README.txt +165 -0
  4. data/Rakefile +112 -0
  5. data/lib/acf/right_acf_interface.rb +549 -0
  6. data/lib/acf/right_acf_invalidations.rb +144 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +229 -0
  9. data/lib/acw/right_acw_interface.rb +248 -0
  10. data/lib/as/right_as_interface.rb +698 -0
  11. data/lib/awsbase/benchmark_fix.rb +39 -0
  12. data/lib/awsbase/right_awsbase.rb +1174 -0
  13. data/lib/awsbase/support.rb +35 -0
  14. data/lib/awsbase/version.rb +9 -0
  15. data/lib/ec2/right_ec2.rb +458 -0
  16. data/lib/ec2/right_ec2_ebs.rb +465 -0
  17. data/lib/ec2/right_ec2_images.rb +413 -0
  18. data/lib/ec2/right_ec2_instances.rb +785 -0
  19. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  20. data/lib/ec2/right_ec2_placement_groups.rb +108 -0
  21. data/lib/ec2/right_ec2_reserved_instances.rb +174 -0
  22. data/lib/ec2/right_ec2_security_groups.rb +396 -0
  23. data/lib/ec2/right_ec2_spot_instances.rb +425 -0
  24. data/lib/ec2/right_ec2_tags.rb +139 -0
  25. data/lib/ec2/right_ec2_vpc.rb +583 -0
  26. data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
  27. data/lib/elb/right_elb_interface.rb +571 -0
  28. data/lib/iam/right_iam_access_keys.rb +71 -0
  29. data/lib/iam/right_iam_groups.rb +195 -0
  30. data/lib/iam/right_iam_interface.rb +341 -0
  31. data/lib/iam/right_iam_mfa_devices.rb +67 -0
  32. data/lib/iam/right_iam_users.rb +251 -0
  33. data/lib/rds/right_rds_interface.rb +1309 -0
  34. data/lib/right_aws.rb +88 -0
  35. data/lib/route_53/right_route_53_interface.rb +630 -0
  36. data/lib/s3/right_s3.rb +1123 -0
  37. data/lib/s3/right_s3_interface.rb +1198 -0
  38. data/lib/sdb/active_sdb.rb +1107 -0
  39. data/lib/sdb/right_sdb_interface.rb +753 -0
  40. data/lib/sns/right_sns.rb +205 -0
  41. data/lib/sns/right_sns_interface.rb +343 -0
  42. data/lib/sqs/right_sqs.rb +387 -0
  43. data/lib/sqs/right_sqs_gen2.rb +342 -0
  44. data/lib/sqs/right_sqs_gen2_interface.rb +523 -0
  45. data/lib/sqs/right_sqs_interface.rb +593 -0
  46. data/right_aws.gemspec +91 -0
  47. data/test/acf/test_helper.rb +2 -0
  48. data/test/acf/test_right_acf.rb +138 -0
  49. data/test/awsbase/test_helper.rb +2 -0
  50. data/test/awsbase/test_right_awsbase.rb +12 -0
  51. data/test/ec2/test_helper.rb +2 -0
  52. data/test/ec2/test_right_ec2.rb +108 -0
  53. data/test/http_connection.rb +87 -0
  54. data/test/rds/test_helper.rb +2 -0
  55. data/test/rds/test_right_rds.rb +120 -0
  56. data/test/s3/test_helper.rb +2 -0
  57. data/test/s3/test_right_s3.rb +421 -0
  58. data/test/s3/test_right_s3_stubbed.rb +97 -0
  59. data/test/sdb/test_active_sdb.rb +357 -0
  60. data/test/sdb/test_batch_put_attributes.rb +54 -0
  61. data/test/sdb/test_helper.rb +3 -0
  62. data/test/sdb/test_right_sdb.rb +253 -0
  63. data/test/sns/test_helper.rb +2 -0
  64. data/test/sns/test_right_sns.rb +73 -0
  65. data/test/sqs/test_helper.rb +2 -0
  66. data/test/sqs/test_right_sqs.rb +285 -0
  67. data/test/sqs/test_right_sqs_gen2.rb +264 -0
  68. data/test/test_credentials.rb +37 -0
  69. data/test/ts_right_aws.rb +14 -0
  70. metadata +244 -0
@@ -0,0 +1,1123 @@
1
+ #
2
+ # Copyright (c) 2007-2008 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module RightAws
25
+
26
+ # = RightAws::S3 -- RightScale's Amazon S3 interface
27
+ # The RightAws::S3 class provides a complete interface to Amazon's Simple
28
+ # Storage Service.
29
+ # For explanations of the semantics
30
+ # of each call, please refer to Amazon's documentation at
31
+ # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=48
32
+ #
33
+ # See examples below for the bucket and buckets methods.
34
+ #
35
+ # Error handling: all operations raise an RightAws::AwsError in case
36
+ # of problems. Note that transient errors are automatically retried.
37
+ #
38
+ # It is a good way to use domain naming style getting a name for the buckets.
39
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingBucket.html
40
+ # about the naming convention for the buckets. This case they can be accessed using a virtual domains.
41
+ #
42
+ # Let assume you have 3 buckets: 'awesome-bucket', 'awesome_bucket' and 'AWEsomE-bucket'.
43
+ # The first ones objects can be accessed as: http:// awesome-bucket.s3.amazonaws.com/key/object
44
+ #
45
+ # But the rest have to be accessed as:
46
+ # http:// s3.amazonaws.com/awesome_bucket/key/object and http:// s3.amazonaws.com/AWEsomE-bucket/key/object
47
+ #
48
+ # See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html for better explanation.
49
+ #
50
+ class S3
51
+ attr_reader :interface
52
+
53
+ # Create a new handle to an S3 account. All handles share the same per process or per thread
54
+ # HTTP connection to Amazon S3. Each handle is for a specific account.
55
+ # The +params+ are passed through as-is to RightAws::S3Interface.new
56
+ #
57
+ # Params is a hash:
58
+ #
59
+ # {:server => 's3.amazonaws.com' # Amazon service host: 's3.amazonaws.com'(default)
60
+ # :port => 443 # Amazon service port: 80 or 443(default)
61
+ # :protocol => 'https' # Amazon service protocol: 'http' or 'https'(default)
62
+ # :logger => Logger Object} # Logger instance: logs to STDOUT if omitted }
63
+ def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
64
+ @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
65
+ end
66
+
67
+ # Retrieve a list of buckets.
68
+ # Returns an array of RightAws::S3::Bucket instances.
69
+ # # Create handle to S3 account
70
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
71
+ # my_buckets_names = s3.buckets.map{|b| b.name}
72
+ # puts "Buckets on S3: #{my_bucket_names.join(', ')}"
73
+ def buckets
74
+ @interface.list_all_my_buckets.map! do |entry|
75
+ owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
76
+ Bucket.new(self, entry[:name], entry[:creation_date], owner)
77
+ end
78
+ end
79
+
80
+ # Retrieve an individual bucket.
81
+ # If the bucket does not exist and +create+ is set, a new bucket
82
+ # is created on S3. Launching this method with +create+=+true+ may
83
+ # affect on the bucket's ACL if the bucket already exists.
84
+ # Returns a RightAws::S3::Bucket instance or +nil+ if the bucket does not exist
85
+ # and +create+ is not set.
86
+ #
87
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
88
+ # bucket1 = s3.bucket('my_awesome_bucket_1')
89
+ # bucket1.keys #=> exception here if the bucket does not exists
90
+ # ...
91
+ # bucket2 = s3.bucket('my_awesome_bucket_2', true)
92
+ # bucket2.keys #=> list of keys
93
+ # # create a bucket at the European location with public read access
94
+ # bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)
95
+ #
96
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
97
+ # (section: Canned Access Policies)
98
+ #
99
+ def bucket(name, create=false, perms=nil, headers={})
100
+ headers['x-amz-acl'] = perms if perms
101
+ @interface.create_bucket(name, headers) if create
102
+ buckets.each { |bucket| return bucket if bucket.name == name }
103
+ nil
104
+ end
105
+
106
+
107
+ class Bucket
108
+ attr_reader :s3, :name, :owner, :creation_date
109
+
110
+ # Create a Bucket instance.
111
+ # If the bucket does not exist and +create+ is set, a new bucket
112
+ # is created on S3. Launching this method with +create+=+true+ may
113
+ # affect on the bucket's ACL if the bucket already exists.
114
+ # Returns Bucket instance or +nil+ if the bucket does not exist
115
+ # and +create+ is not set.
116
+ #
117
+ # s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)
118
+ # ...
119
+ # bucket1 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_1')
120
+ # bucket1.keys #=> exception here if the bucket does not exists
121
+ # ...
122
+ # bucket2 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)
123
+ # bucket2.keys #=> list of keys
124
+ # # create a bucket at the European location with public read access
125
+ # bucket3 = RightAws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)
126
+ #
127
+ # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html
128
+ # (section: Canned Access Policies)
129
+ #
130
+ def self.create(s3, name, create=false, perms=nil, headers={})
131
+ s3.bucket(name, create, perms, headers)
132
+ end
133
+
134
+
135
+ # Create a bucket instance. In normal use this method should
136
+ # not be called directly.
137
+ # Use RightAws::S3::Bucket.create or RightAws::S3.bucket instead.
138
+ def initialize(s3, name, creation_date=nil, owner=nil)
139
+ @s3 = s3
140
+ @name = name
141
+ @owner = owner
142
+ @creation_date = creation_date
143
+ if @creation_date && !@creation_date.is_a?(Time)
144
+ @creation_date = Time.parse(@creation_date)
145
+ end
146
+ end
147
+
148
+ # Return bucket name as a String.
149
+ #
150
+ # bucket = RightAws::S3.bucket('my_awesome_bucket')
151
+ # puts bucket #=> 'my_awesome_bucket'
152
+ #
153
+ def to_s
154
+ @name.to_s
155
+ end
156
+ alias_method :full_name, :to_s
157
+
158
+ # Return a public link to bucket.
159
+ #
160
+ # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
161
+ #
162
+ def public_link
163
+ params = @s3.interface.params
164
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
165
+ end
166
+
167
+ # Returns the bucket location
168
+ def location
169
+ @location ||= @s3.interface.bucket_location(@name)
170
+ end
171
+
172
+ # Retrieves the logging configuration for a bucket.
173
+ # Returns a hash of {:enabled, :targetbucket, :targetprefix}
174
+ #
175
+ # bucket.logging_info()
176
+ # => {:enabled=>true, :targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/"}
177
+ def logging_info
178
+ @s3.interface.get_logging_parse(:bucket => @name)
179
+ end
180
+
181
+ # Enables S3 server access logging on a bucket. The target bucket must have been properly configured to receive server
182
+ # access logs.
183
+ # Params:
184
+ # :targetbucket - either the target bucket object or the name of the target bucket
185
+ # :targetprefix - the prefix under which all logs should be stored
186
+ #
187
+ # bucket.enable_logging(:targetbucket=>"mylogbucket", :targetprefix=>"loggylogs/")
188
+ # => true
189
+ def enable_logging(params)
190
+ AwsUtils.mandatory_arguments([:targetbucket, :targetprefix], params)
191
+ AwsUtils.allow_only([:targetbucket, :targetprefix], params)
192
+ xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"><LoggingEnabled><TargetBucket>#{params[:targetbucket]}</TargetBucket><TargetPrefix>#{params[:targetprefix]}</TargetPrefix></LoggingEnabled></BucketLoggingStatus>"
193
+ @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
194
+ end
195
+
196
+ # Disables S3 server access logging on a bucket. Takes no arguments.
197
+ def disable_logging
198
+ xmldoc = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\"></BucketLoggingStatus>"
199
+ @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)
200
+ end
201
+
202
+ # Retrieve a group of keys from Amazon.
203
+ # +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }).
204
+ # Retrieves meta-headers information if +head+ it +true+.
205
+ # Returns an array of Key instances.
206
+ #
207
+ # bucket.keys #=> # returns all keys from bucket
208
+ # bucket.keys('prefix' => 'logs') #=> # returns all keys that starts with 'logs'
209
+ #
210
+ def keys(options={}, head=false)
211
+ keys_and_service(options, head)[0]
212
+ end
213
+
214
+ # Same as +keys+ method but return an array of [keys, service_data].
215
+ # where +service_data+ is a hash with additional output information.
216
+ #
217
+ # keys, service = bucket.keys_and_service({'max-keys'=> 2, 'prefix' => 'logs'})
218
+ # p keys #=> # 2 keys array
219
+ # p service #=> {"max-keys"=>"2", "prefix"=>"logs", "name"=>"my_awesome_bucket", "marker"=>"", "is_truncated"=>true, :common_prefixes=>[]}
220
+ #
221
+ def keys_and_service(options={}, head=false)
222
+ opt = {}; options.each{ |key, value| opt[key.to_s] = value }
223
+ thislist = {}
224
+ list = []
225
+ @s3.interface.incrementally_list_bucket(@name, opt) do |thislist|
226
+ thislist[:contents].each do |entry|
227
+ owner = Owner.new(entry[:owner_id], entry[:owner_display_name])
228
+ key = Key.new(self, entry[:key], nil, {}, {}, entry[:last_modified], entry[:e_tag], entry[:size], entry[:storage_class], owner)
229
+ key.head if head
230
+ list << key
231
+ end
232
+ end
233
+ thislist.delete(:contents)
234
+ [list, thislist]
235
+ end
236
+
237
+ # Retrieve key information from Amazon.
238
+ # The +key_name+ is a +String+ or Key instance.
239
+ # Retrieves meta-header information if +head+ is +true+.
240
+ # Returns new Key instance.
241
+ #
242
+ # key = bucket.key('logs/today/1.log', true) #=> #<RightAws::S3::Key:0xb7b1e240 ... >
243
+ # # is the same as:
244
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')
245
+ # key.head
246
+ #
247
+ def key(key_name, head=false)
248
+ raise 'Key name can not be empty.' if key_name.right_blank?
249
+ key_instance = nil
250
+ # if this key exists - find it ....
251
+ keys({'prefix'=>key_name}, head).each do |key|
252
+ if key.name == key_name.to_s
253
+ key_instance = key
254
+ break
255
+ end
256
+ end
257
+ # .... else this key is unknown
258
+ unless key_instance
259
+ key_instance = Key.create(self, key_name.to_s)
260
+ end
261
+ key_instance
262
+ end
263
+
264
+ # Store object data.
265
+ # The +key+ is a +String+ or Key instance.
266
+ # Returns +true+.
267
+ #
268
+ # bucket.put('logs/today/1.log', 'Olala!') #=> true
269
+ #
270
+ def put(key, data=nil, meta_headers={}, perms=nil, headers={})
271
+ key = Key.create(self, key.to_s, data, meta_headers) unless key.is_a?(Key)
272
+ key.put(data, perms, headers)
273
+ end
274
+
275
+ # Retrieve object data from Amazon.
276
+ # The +key+ is a +String+ or Key.
277
+ # Returns Key instance.
278
+ #
279
+ # key = bucket.get('logs/today/1.log') #=>
280
+ # puts key.data #=> 'sasfasfasdf'
281
+ #
282
+ def get(key, headers={})
283
+ key = Key.create(self, key.to_s) unless key.is_a?(Key)
284
+ key.get(headers)
285
+ end
286
+
287
+ # Rename object. Returns RightAws::S3::Key instance.
288
+ #
289
+ # new_key = bucket.rename_key('logs/today/1.log','logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
290
+ # puts key.name #=> 'logs/today/2.log'
291
+ # key.exists? #=> true
292
+ #
293
+ def rename_key(old_key_or_name, new_name)
294
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
295
+ old_key_or_name.rename(new_name)
296
+ old_key_or_name
297
+ end
298
+
299
+ # Create an object copy. Returns a destination RightAws::S3::Key instance.
300
+ #
301
+ # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
302
+ # puts key.name #=> 'logs/today/2.log'
303
+ # key.exists? #=> true
304
+ #
305
+ def copy_key(old_key_or_name, new_key_or_name)
306
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
307
+ old_key_or_name.copy(new_key_or_name)
308
+ end
309
+
310
+ # Move an object to other location. Returns a destination RightAws::S3::Key instance.
311
+ #
312
+ # new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
313
+ # puts key.name #=> 'logs/today/2.log'
314
+ # key.exists? #=> true
315
+ #
316
+ def move_key(old_key_or_name, new_key_or_name)
317
+ old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)
318
+ old_key_or_name.move(new_key_or_name)
319
+ end
320
+
321
+ # Remove all keys from a bucket.
322
+ # Returns +true+.
323
+ #
324
+ # bucket.clear #=> true
325
+ #
326
+ def clear
327
+ @s3.interface.clear_bucket(@name)
328
+ end
329
+
330
+ # Delete all keys where the 'folder_key' can be interpreted
331
+ # as a 'folder' name.
332
+ # Returns an array of string keys that have been deleted.
333
+ #
334
+ # bucket.keys.map{|key| key.name}.join(', ') #=> 'test, test/2/34, test/3, test1, test1/logs'
335
+ # bucket.delete_folder('test') #=> ['test','test/2/34','test/3']
336
+ #
337
+ def delete_folder(folder, separator='/')
338
+ @s3.interface.delete_folder(@name, folder, separator)
339
+ end
340
+
341
+ # Delete a bucket. Bucket must be empty.
342
+ # If +force+ is set, clears and deletes the bucket.
343
+ # Returns +true+.
344
+ #
345
+ # bucket.delete(:force => true) #=> true
346
+ #
347
+ def delete(options={})
348
+ force = options.is_a?(Hash) && options[:force]==true
349
+ force ? @s3.interface.force_delete_bucket(@name) : @s3.interface.delete_bucket(@name)
350
+ end
351
+
352
+ # Return a list of grantees.
353
+ #
354
+ def grantees
355
+ Grantee::grantees(self)
356
+ end
357
+
358
+ end
359
+
360
+
361
+ class Key
362
+ attr_reader :bucket, :name, :last_modified, :e_tag, :size, :storage_class, :owner
363
+ attr_accessor :headers, :meta_headers
364
+ attr_writer :data
365
+
366
+ # Separate Amazon meta headers from other headers
367
+ def self.split_meta(headers) #:nodoc:
368
+ hash = headers.dup
369
+ meta = {}
370
+ hash.each do |key, value|
371
+ if key[/^#{S3Interface::AMAZON_METADATA_PREFIX}/]
372
+ meta[key.gsub(S3Interface::AMAZON_METADATA_PREFIX,'')] = value
373
+ hash.delete(key)
374
+ end
375
+ end
376
+ [hash, meta]
377
+ end
378
+
379
+ def self.add_meta_prefix(meta_headers, prefix=S3Interface::AMAZON_METADATA_PREFIX)
380
+ meta = {}
381
+ meta_headers.each do |meta_header, value|
382
+ if meta_header[/#{prefix}/]
383
+ meta[meta_header] = value
384
+ else
385
+ meta["#{S3Interface::AMAZON_METADATA_PREFIX}#{meta_header}"] = value
386
+ end
387
+ end
388
+ meta
389
+ end
390
+
391
+
392
+ # Create a new Key instance, but do not create the actual key.
393
+ # The +name+ is a +String+.
394
+ # Returns a new Key instance.
395
+ #
396
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
397
+ # key.exists? #=> true | false
398
+ # key.put('Woohoo!') #=> true
399
+ # key.exists? #=> true
400
+ #
401
+ def self.create(bucket, name, data=nil, meta_headers={})
402
+ new(bucket, name, data, {}, meta_headers)
403
+ end
404
+
405
+ # Create a new Key instance, but do not create the actual key.
406
+ # In normal use this method should not be called directly.
407
+ # Use RightAws::S3::Key.create or bucket.key() instead.
408
+ #
409
+ def initialize(bucket, name, data=nil, headers={}, meta_headers={},
410
+ last_modified=nil, e_tag=nil, size=nil, storage_class=nil, owner=nil)
411
+ raise 'Bucket must be a Bucket instance.' unless bucket.is_a?(Bucket)
412
+ @bucket = bucket
413
+ @name = name
414
+ @data = data
415
+ @e_tag = e_tag
416
+ @size = size.to_i
417
+ @storage_class = storage_class
418
+ @owner = owner
419
+ @last_modified = last_modified
420
+ if @last_modified && !@last_modified.is_a?(Time)
421
+ @last_modified = Time.parse(@last_modified)
422
+ end
423
+ @headers, @meta_headers = self.class.split_meta(headers)
424
+ @meta_headers.merge!(meta_headers)
425
+ end
426
+
427
+ # Return key name as a String.
428
+ #
429
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
430
+ # puts key #=> 'logs/today/1.log'
431
+ #
432
+ def to_s
433
+ @name.to_s
434
+ end
435
+
436
+ # Return the full S3 path to this key (bucket/key).
437
+ #
438
+ # key.full_name #=> 'my_awesome_bucket/cool_key'
439
+ #
440
+ def full_name(separator='/')
441
+ "#{@bucket.to_s}#{separator}#{@name}"
442
+ end
443
+
444
+ # Return a public link to a key.
445
+ #
446
+ # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
447
+ #
448
+ def public_link
449
+ params = @bucket.s3.interface.params
450
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
451
+ end
452
+
453
+ # Return Key data. Retrieve this data from Amazon if it is the first time call.
454
+ # TODO TRB 6/19/07 What does the above mean? Clarify.
455
+ #
456
+ def data
457
+ get if !@data and exists?
458
+ @data
459
+ end
460
+
461
+ # Getter for the 'content-type' metadata
462
+ def content_type
463
+ @headers['content-type'] if @headers
464
+ end
465
+
466
+ # Helper to get and URI-decode a header metadata.
467
+ # Metadata have to be HTTP encoded (rfc2616) as we use the Amazon S3 REST api
468
+ # see http://docs.amazonwebservices.com/AmazonS3/latest/index.html?UsingMetadata.html
469
+ def decoded_meta_headers(key = nil)
470
+ if key
471
+ # Get one metadata value by its key
472
+ URI.decode(@meta_headers[key.to_s])
473
+ else
474
+ # Get a hash of all metadata with a decoded value
475
+ @decoded_meta_headers ||= begin
476
+ metadata = {}
477
+ @meta_headers.each do |key, value|
478
+ metadata[key.to_sym] = URI.decode(value)
479
+ end
480
+ metadata
481
+ end
482
+ end
483
+ end
484
+
485
+ # Retrieve object data and attributes from Amazon.
486
+ # Returns a +String+.
487
+ #
488
+ def get(headers={})
489
+ response = @bucket.s3.interface.get(@bucket.name, @name, headers)
490
+ @data = response[:object]
491
+ @headers, @meta_headers = self.class.split_meta(response[:headers])
492
+ refresh(false)
493
+ @data
494
+ end
495
+
496
+ # Store object data on S3.
497
+ # Parameter +data+ is a +String+ or S3Object instance.
498
+ # Returns +true+.
499
+ #
500
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')
501
+ # key.data = 'Qwerty'
502
+ # key.put #=> true
503
+ # ...
504
+ # key.put('Olala!') #=> true
505
+ #
506
+ def put(data=nil, perms=nil, headers={})
507
+ headers['x-amz-acl'] = perms if perms
508
+ @data = data || @data
509
+ meta = self.class.add_meta_prefix(@meta_headers)
510
+ @bucket.s3.interface.put(@bucket.name, @name, @data, meta.merge(headers))
511
+ end
512
+
513
+ # Rename an object. Returns new object name.
514
+ #
515
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
516
+ # key.rename('logs/today/2.log') #=> 'logs/today/2.log'
517
+ # puts key.name #=> 'logs/today/2.log'
518
+ # key.exists? #=> true
519
+ #
520
+ def rename(new_name)
521
+ @bucket.s3.interface.rename(@bucket.name, @name, new_name)
522
+ @name = new_name
523
+ end
524
+
525
+ # Create an object copy. Returns a destination RightAws::S3::Key instance.
526
+ #
527
+ # # Key instance as destination
528
+ # key1 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
529
+ # key2 = RightAws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
530
+ # key1.put('Olala!') #=> true
531
+ # key1.copy(key2) #=> #<RightAws::S3::Key:0xb7b5e240 ... >
532
+ # key1.exists? #=> true
533
+ # key2.exists? #=> true
534
+ # puts key2.data #=> 'Olala!'
535
+ #
536
+ # # String as destination
537
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
538
+ # key.put('Olala!') #=> true
539
+ # new_key = key.copy('logs/today/888.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
540
+ # key.exists? #=> true
541
+ # new_key.exists? #=> true
542
+ #
543
+ def copy(new_key_or_name)
544
+ new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
545
+ @bucket.s3.interface.copy(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
546
+ new_key_or_name
547
+ end
548
+
549
+ # Move an object to other location. Returns a destination RightAws::S3::Key instance.
550
+ #
551
+ # # Key instance as destination
552
+ # key1 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
553
+ # key2 = RightAws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
554
+ # key1.put('Olala!') #=> true
555
+ # key1.move(key2) #=> #<RightAws::S3::Key:0xb7b5e240 ... >
556
+ # key1.exists? #=> false
557
+ # key2.exists? #=> true
558
+ # puts key2.data #=> 'Olala!'
559
+ #
560
+ # # String as destination
561
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >
562
+ # key.put('Olala!') #=> true
563
+ # new_key = key.move('logs/today/888.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >
564
+ # key.exists? #=> false
565
+ # new_key.exists? #=> true
566
+ #
567
+ def move(new_key_or_name)
568
+ new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)
569
+ @bucket.s3.interface.move(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
570
+ new_key_or_name
571
+ end
572
+
573
+ # Retrieve key info from bucket and update attributes.
574
+ # Refresh meta-headers (by calling +head+ method) if +head+ is set.
575
+ # Returns +true+ if the key exists in bucket and +false+ otherwise.
576
+ #
577
+ # key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')
578
+ # key.e_tag #=> nil
579
+ # key.meta_headers #=> {}
580
+ # key.refresh #=> true
581
+ # key.e_tag #=> '12345678901234567890bf11094484b6'
582
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
583
+ #
584
+ def refresh(head=true)
585
+ new_key = @bucket.key(self)
586
+ @last_modified = new_key.last_modified
587
+ @e_tag = new_key.e_tag
588
+ @size = new_key.size
589
+ @storage_class = new_key.storage_class
590
+ @owner = new_key.owner
591
+ if @last_modified
592
+ self.head
593
+ true
594
+ else
595
+ @headers = @meta_headers = {}
596
+ false
597
+ end
598
+ end
599
+
600
+ # Updates headers and meta-headers from S3.
601
+ # Returns +true+.
602
+ #
603
+ # key.meta_headers #=> {"family"=>"qwerty"}
604
+ # key.head #=> true
605
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
606
+ #
607
+ def head
608
+ @headers, @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name))
609
+ true
610
+ end
611
+
612
+ # Reload meta-headers only. Returns meta-headers hash.
613
+ #
614
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
615
+ #
616
+ def reload_meta
617
+ @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name)).last
618
+ end
619
+
620
+ # Replace meta-headers by new hash at S3. Returns new meta-headers hash.
621
+ #
622
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
623
+ # key.save_meta #=> {"family"=>"oops", "race" => "troll"}
624
+ # key.reload_meta #=> {"family"=>"oops", "race" => "troll"}
625
+ #
626
+ def save_meta(meta_headers)
627
+ meta = self.class.add_meta_prefix(meta_headers)
628
+ @bucket.s3.interface.copy(@bucket.name, @name, @bucket.name, @name, :replace, meta)
629
+ @meta_headers = self.class.split_meta(meta)[1]
630
+ end
631
+
632
+ # Check for existence of the key in the given bucket.
633
+ # Returns +true+ or +false+.
634
+ #
635
+ # key = RightAws::S3::Key.create(bucket,'logs/today/1.log')
636
+ # key.exists? #=> false
637
+ # key.put('Woohoo!') #=> true
638
+ # key.exists? #=> true
639
+ #
640
+ def exists?
641
+ @bucket.key(self).last_modified ? true : false
642
+ end
643
+
644
+ # Remove key from bucket.
645
+ # Returns +true+.
646
+ #
647
+ # key.delete #=> true
648
+ #
649
+ def delete
650
+ raise 'Key name must be specified.' if @name.right_blank?
651
+ @bucket.s3.interface.delete(@bucket, @name)
652
+ end
653
+
654
+ # Return a list of grantees.
655
+ #
656
+ def grantees
657
+ Grantee::grantees(self)
658
+ end
659
+
660
+ end
661
+
662
+
663
+ class Owner
664
+ attr_reader :id, :name
665
+
666
+ def initialize(id, name)
667
+ @id = id
668
+ @name = name
669
+ end
670
+
671
+ # Return Owner name as a +String+.
672
+ def to_s
673
+ @name
674
+ end
675
+ end
676
+
677
+
678
+ # There are 2 ways to set permissions for a bucket or key (called a +thing+ below):
679
+ #
680
+ # 1 . Use +perms+ param to set 'Canned Access Policies' when calling the <tt>bucket.create</tt>,
681
+ # <tt>bucket.put</tt> and <tt>key.put</tt> methods.
682
+ # The +perms+ param can take these values: 'private', 'public-read', 'public-read-write' and
683
+ # 'authenticated-read'.
684
+ # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html).
685
+ #
686
+ # bucket = s3.bucket('bucket_for_kd_test_13', true, 'public-read')
687
+ # key.put('Woohoo!','public-read-write' )
688
+ #
689
+ # 2 . Use Grantee instances (the permission is a +String+ or an +Array+ of: 'READ', 'WRITE',
690
+ # 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'):
691
+ #
692
+ # bucket = s3.bucket('my_awesome_bucket', true)
693
+ # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL, :apply)
694
+ # grantee2 = RightAws::S3::Grantee.new(bucket, 'xy3v3...5fhp', [READ, WRITE], :apply)
695
+ #
696
+ # There is only one way to get and to remove permission (via Grantee instances):
697
+ #
698
+ # grantees = bucket.grantees # a list of Grantees that have any access for this bucket
699
+ # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c')
700
+ # grantee1.perms #=> returns a list of perms for this grantee to that bucket
701
+ # ...
702
+ # grantee1.drop # remove all perms for this grantee
703
+ # grantee2.revoke('WRITE') # revoke write access only
704
+ #
705
+ class Grantee
706
+ # A bucket or a key the grantee has an access to.
707
+ attr_reader :thing
708
+ # Grantee Amazon id.
709
+ attr_reader :id
710
+ # Grantee display name.
711
+ attr_reader :name
712
+ # Array of permissions.
713
+ attr_accessor :perms
714
+
715
+ # Retrieve Owner information and a list of Grantee instances that have
716
+ # a access to this thing (bucket or key).
717
+ #
718
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
719
+ # ...
720
+ # RightAws::S3::Grantee.owner_and_grantees(bucket) #=> [owner, grantees]
721
+ #
722
+ def self.owner_and_grantees(thing)
723
+ if thing.is_a?(Bucket)
724
+ bucket, key = thing, ''
725
+ else
726
+ bucket, key = thing.bucket, thing
727
+ end
728
+ hash = bucket.s3.interface.get_acl_parse(bucket.to_s, key.to_s)
729
+ owner = Owner.new(hash[:owner][:id], hash[:owner][:display_name])
730
+
731
+ grantees = []
732
+ hash[:grantees].each do |id, params|
733
+ grantees << new(thing, id, params[:permissions], nil, params[:display_name])
734
+ end
735
+ [owner, grantees]
736
+ end
737
+
738
+ # Retrieves a list of Grantees instances that have an access to this thing(bucket or key).
739
+ #
740
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
741
+ # ...
742
+ # RightAws::S3::Grantee.grantees(bucket) #=> grantees
743
+ #
744
+ def self.grantees(thing)
745
+ owner_and_grantees(thing)[1]
746
+ end
747
+
748
+ def self.put_acl(thing, owner, grantees) #:nodoc:
749
+ if thing.is_a?(Bucket)
750
+ bucket, key = thing, ''
751
+ else
752
+ bucket, key = thing.bucket, thing
753
+ end
754
+ body = "<AccessControlPolicy>" +
755
+ "<Owner>" +
756
+ "<ID>#{owner.id}</ID>" +
757
+ "<DisplayName>#{owner.name}</DisplayName>" +
758
+ "</Owner>" +
759
+ "<AccessControlList>" +
760
+ grantees.map{|grantee| grantee.to_xml}.join +
761
+ "</AccessControlList>" +
762
+ "</AccessControlPolicy>"
763
+ bucket.s3.interface.put_acl(bucket.to_s, key.to_s, body)
764
+ end
765
+
766
+ # Create a new Grantee instance.
767
+ # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
768
+ # permissions from S3 and update @perms. If +action+ == :apply, then apply
769
+ # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
770
+ # both the actions. This is used for the new grantees that had no perms to
771
+ # this thing before. The default action is :refresh.
772
+ #
773
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
774
+ # grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
775
+ # ...
776
+ # grantee2 = RightAws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
777
+ # grantee3 = RightAws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
778
+ #
779
+ def initialize(thing, id, perms=[], action=:refresh, name=nil)
780
+ @thing = thing
781
+ @id = id
782
+ @name = name
783
+ @perms = Array(perms)
784
+ case action
785
+ when :apply then apply
786
+ when :refresh then refresh
787
+ when :apply_and_refresh then apply; refresh
788
+ end
789
+ end
790
+
791
+ # Return +true+ if the grantee has any permissions to the thing.
792
+ def exists?
793
+ self.class.grantees(@thing).each do |grantee|
794
+ return true if @id == grantee.id
795
+ end
796
+ false
797
+ end
798
+
799
+ # Return Grantee type (+String+): "Group", "AmazonCustomerByEmail" or "CanonicalUser".
800
+ def type
801
+ case @id
802
+ when /^http:/ then "Group"
803
+ when /@/ then "AmazonCustomerByEmail"
804
+ else "CanonicalUser"
805
+ end
806
+ end
807
+
808
+ # Return a name or an id.
809
+ def to_s
810
+ @name || @id
811
+ end
812
+
813
+ # Add permissions for grantee.
814
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'.
815
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
816
+ # Returns +true+.
817
+ #
818
+ # grantee.grant('FULL_CONTROL') #=> true
819
+ # grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
820
+ # grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
821
+ #
822
+ def grant(*permissions)
823
+ permissions.flatten!
824
+ old_perms = @perms.dup
825
+ @perms += permissions
826
+ @perms.uniq!
827
+ return true if @perms == old_perms
828
+ apply
829
+ end
830
+
831
+ # Revoke permissions for grantee.
832
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'
833
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
834
+ # Default value is 'FULL_CONTROL'.
835
+ # Returns +true+.
836
+ #
837
+ # grantee.revoke('READ') #=> true
838
+ # grantee.revoke('FULL_CONTROL','WRITE') #=> true
839
+ # grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
840
+ #
841
+ def revoke(*permissions)
842
+ permissions.flatten!
843
+ old_perms = @perms.dup
844
+ @perms -= permissions
845
+ @perms.uniq!
846
+ return true if @perms == old_perms
847
+ apply
848
+ end
849
+
850
+ # Revoke all permissions for this grantee.
851
+ # Returns +true+.
852
+ #
853
+ # grantee.drop #=> true
854
+ #
855
+ def drop
856
+ @perms = []
857
+ apply
858
+ end
859
+
860
+ # Refresh grantee perms for its +thing+.
861
+ # Returns +true+ if the grantee has perms for this +thing+ or
862
+ # +false+ otherwise, and updates @perms value as a side-effect.
863
+ #
864
+ # grantee.grant('FULL_CONTROL') #=> true
865
+ # grantee.refresh #=> true
866
+ # grantee.drop #=> true
867
+ # grantee.refresh #=> false
868
+ #
869
+ def refresh
870
+ @perms = []
871
+ self.class.grantees(@thing).each do |grantee|
872
+ if @id == grantee.id
873
+ @name = grantee.name
874
+ @perms = grantee.perms
875
+ return true
876
+ end
877
+ end
878
+ false
879
+ end
880
+
881
+ # Apply current grantee @perms to +thing+. This method is called internally by the +grant+
882
+ # and +revoke+ methods. In normal use this method should not
883
+ # be called directly.
884
+ #
885
+ # grantee.perms = ['FULL_CONTROL']
886
+ # grantee.apply #=> true
887
+ #
888
+ def apply
889
+ @perms.uniq!
890
+ owner, grantees = self.class.owner_and_grantees(@thing)
891
+ # walk through all the grantees and replace the data for the current one and ...
892
+ grantees.map! { |grantee| grantee.id == @id ? self : grantee }
893
+ # ... if this grantee is not known - add this bad boy to a list
894
+ grantees << self unless grantees.include?(self)
895
+ # set permissions
896
+ self.class.put_acl(@thing, owner, grantees)
897
+ end
898
+
899
+ def to_xml # :nodoc:
900
+ id_str = case @id
901
+ when /^http/ then "<URI>#{@id}</URI>"
902
+ when /@/ then "<EmailAddress>#{@id}</EmailAddress>"
903
+ else "<ID>#{@id}</ID>"
904
+ end
905
+ grants = ''
906
+ @perms.each do |perm|
907
+ grants << "<Grant>" +
908
+ "<Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
909
+ "xsi:type=\"#{type}\">#{id_str}</Grantee>" +
910
+ "<Permission>#{perm}</Permission>" +
911
+ "</Grant>"
912
+ end
913
+ grants
914
+ end
915
+
916
+ end
917
+
918
+ end
919
+
920
+ # RightAws::S3Generator and RightAws::S3Generator::Bucket methods:
921
+ #
922
+ # s3g = RightAws::S3Generator.new('1...2', 'nx...Y6') #=> #<RightAws::S3Generator:0xb7b5cc94>
923
+ #
924
+ # # List all buckets(method 'GET'):
925
+ # buckets_list = s3g.buckets #=> 'https://s3.amazonaws.com:443/?Signature=Y...D&Expires=1180941864&AWSAccessKeyId=1...2'
926
+ # # Create bucket link (method 'PUT'):
927
+ # bucket = s3g.bucket('my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>
928
+ # link_to_create = bucket.create_link(1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2
929
+ # # ... or:
930
+ # bucket = RightAws::S3Generator::Bucket.create(s3g, 'my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>
931
+ # link_to_create = bucket.create_link(1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2
932
+ # # ... or:
933
+ # bucket = RightAws::S3Generator::Bucket.new(s3g, 'my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>
934
+ # link_to_create = bucket.create_link(1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2
935
+ # # List bucket(method 'GET'):
936
+ # bucket.keys(1.day) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=i...D&Expires=1180942620&AWSAccessKeyId=1...2
937
+ # # Create/put key (method 'PUT'):
938
+ # bucket.put('my_cool_key') #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=q...D&Expires=1180943094&AWSAccessKeyId=1...2
939
+ # # Get key data (method 'GET'):
940
+ # bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2
941
+ # # Delete bucket (method 'DELETE'):
942
+ # bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
943
+ #
944
+ # RightAws::S3Generator::Key methods:
945
+ #
946
+ # # Create Key instance:
947
+ # key = RightAws::S3Generator::Key.new(bicket, 'my_cool_key') #=> #<RightAws::S3Generator::Key:0xb7b7394c>
948
+ # # Put key data (method 'PUT'):
949
+ # key.put #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=2...D&Expires=1180943302&AWSAccessKeyId=1...2
950
+ # # Get key data (method 'GET'):
951
+ # key.get #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=a...D&Expires=1180820032&AWSAccessKeyId=1...2
952
+ # # Head key (method 'HEAD'):
953
+ # key.head #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=b...D&Expires=1180820032&AWSAccessKeyId=1...2
954
+ # # Delete key (method 'DELETE'):
955
+ # key.delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=x...D&Expires=1180820032&AWSAccessKeyId=1...2
956
+ #
957
+ class S3Generator
958
+ attr_reader :interface
959
+
960
+ def initialize(aws_access_key_id, aws_secret_access_key, params={})
961
+ @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)
962
+ end
963
+
964
+ # Generate link to list all buckets
965
+ #
966
+ # s3.buckets(1.hour)
967
+ #
968
+ def buckets(expires=nil, headers={})
969
+ @interface.list_all_my_buckets_link(expires, headers)
970
+ end
971
+
972
+ # Create new S3LinkBucket instance and generate link to create it at S3.
973
+ #
974
+ # bucket= s3.bucket('my_owesome_bucket')
975
+ #
976
+ def bucket(name, expires=nil, headers={})
977
+ Bucket.create(self, name.to_s)
978
+ end
979
+
980
+ class Bucket
981
+ attr_reader :s3, :name
982
+
983
+ def to_s
984
+ @name
985
+ end
986
+ alias_method :full_name, :to_s
987
+
988
+ # Return a public link to bucket.
989
+ #
990
+ # bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'
991
+ #
992
+ def public_link
993
+ params = @s3.interface.params
994
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}"
995
+ end
996
+
997
+ # Create new S3LinkBucket instance and generate creation link for it.
998
+ def self.create(s3, name, expires=nil, headers={})
999
+ new(s3, name.to_s)
1000
+ end
1001
+
1002
+ # Create new S3LinkBucket instance.
1003
+ def initialize(s3, name)
1004
+ @s3, @name = s3, name.to_s
1005
+ end
1006
+
1007
+ # Return a link to create this bucket.
1008
+ #
1009
+ def create_link(expires=nil, headers={})
1010
+ @s3.interface.create_bucket_link(@name, expires, headers)
1011
+ end
1012
+
1013
+ # Generate link to list keys.
1014
+ #
1015
+ # bucket.keys
1016
+ # bucket.keys('prefix'=>'logs')
1017
+ #
1018
+ def keys(options=nil, expires=nil, headers={})
1019
+ @s3.interface.list_bucket_link(@name, options, expires, headers)
1020
+ end
1021
+
1022
+ # Return a S3Generator::Key instance.
1023
+ #
1024
+ # bucket.key('my_cool_key').get #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2
1025
+ # bucket.key('my_cool_key').delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820098&AWSAccessKeyId=1...2
1026
+ #
1027
+ def key(name)
1028
+ Key.new(self, name)
1029
+ end
1030
+
1031
+ # Generates link to PUT key data.
1032
+ #
1033
+ # puts bucket.put('logs/today/1.log', 2.hour)
1034
+ #
1035
+ def put(key, meta_headers={}, expires=nil, headers={})
1036
+ meta = RightAws::S3::Key.add_meta_prefix(meta_headers)
1037
+ @s3.interface.put_link(@name, key.to_s, nil, expires, meta.merge(headers))
1038
+ end
1039
+
1040
+ # Generate link to GET key data.
1041
+ #
1042
+ # bucket.get('logs/today/1.log', 1.hour)
1043
+ #
1044
+ def get(key, expires=nil, headers={})
1045
+ @s3.interface.get_link(@name, key.to_s, expires, headers)
1046
+ end
1047
+
1048
+ # Generate link to delete bucket.
1049
+ #
1050
+ # bucket.delete(2.hour)
1051
+ #
1052
+ def delete(expires=nil, headers={})
1053
+ @s3.interface.delete_bucket_link(@name, expires, headers)
1054
+ end
1055
+ end
1056
+
1057
+
1058
+ class Key
1059
+ attr_reader :bucket, :name
1060
+
1061
+ def to_s
1062
+ @name
1063
+ end
1064
+
1065
+ # Return a full S# name (bucket/key).
1066
+ #
1067
+ # key.full_name #=> 'my_awesome_bucket/cool_key'
1068
+ #
1069
+ def full_name(separator='/')
1070
+ "#{@bucket.to_s}#{separator}#{@name}"
1071
+ end
1072
+
1073
+ # Return a public link to key.
1074
+ #
1075
+ # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
1076
+ #
1077
+ def public_link
1078
+ params = @bucket.s3.interface.params
1079
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
1080
+ end
1081
+
1082
+ def initialize(bucket, name, meta_headers={})
1083
+ @bucket = bucket
1084
+ @name = name.to_s
1085
+ @meta_headers = meta_headers
1086
+ raise 'Key name can not be empty.' if @name.right_blank?
1087
+ end
1088
+
1089
+ # Generate link to PUT key data.
1090
+ #
1091
+ # puts bucket.put('logs/today/1.log', '123', 2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2
1092
+ #
1093
+ def put(expires=nil, headers={})
1094
+ @bucket.put(@name.to_s, @meta_headers, expires, headers)
1095
+ end
1096
+
1097
+ # Generate link to GET key data.
1098
+ #
1099
+ # bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2
1100
+ #
1101
+ def get(expires=nil, headers={})
1102
+ @bucket.s3.interface.get_link(@bucket.to_s, @name, expires, headers)
1103
+ end
1104
+
1105
+ # Generate link to delete key.
1106
+ #
1107
+ # bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1108
+ #
1109
+ def delete(expires=nil, headers={})
1110
+ @bucket.s3.interface.delete_link(@bucket.to_s, @name, expires, headers)
1111
+ end
1112
+
1113
+ # Generate link to head key.
1114
+ #
1115
+ # bucket.head(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2
1116
+ #
1117
+ def head(expires=nil, headers={})
1118
+ @bucket.s3.interface.head_link(@bucket.to_s, @name, expires, headers)
1119
+ end
1120
+ end
1121
+ end
1122
+
1123
+ end