#-- TODO: manipulation of bucket_listing through options hash, e.g. fetch particular page,
#-- specify a prefix and/or delimiter
require File.join(File.dirname(__FILE__), 'client')
require File.join(File.dirname(__FILE__), 's3_obj')
require File.join(File.dirname(__FILE__), 's33r_exception')
module S33r
# Wraps the S33r::Client class to make it more convenient for use with a single bucket.
class NamedBucket < Client
attr_accessor :name, :strict, :public_contents, :dump_requests
# Initialize an instance from a config_file. The config. file can include a separate
# +options+ section specifying options specific to NamedBucket instances (see the initialize method
# for more details).
# Other options are as for S33r::Client.init.
def NamedBucket.init(config_file)
aws_access_key, aws_secret_access_key, options = super.class.load_config(config_file)
NamedBucket.new(aws_access_key, aws_secret_access_key, options)
end
# Initialize a NamedBucket instance.
#
# +options+ is a hash of options for this instance:
# * :default_bucket => 'xxxx': name of the bucket this client is attached to.
# * :public_contents => true: all items put into bucket are made public (can be overridden per request).
# * :strict => true: check whether the bucket exists before attempting to initialize; initialization \
# fails if the bucket does not exist
def initialize(aws_access_key, aws_secret_access_key, options={}, &block)
super(aws_access_key, aws_secret_access_key, options)
@name = options[:default_bucket]
if @name.nil?
raise S33rException::MissingBucketName, "NamedBucket cannot be initialised without specifying\
a :default_bucket option"
end
# all content inside the bucket should be created as public-read
@public_contents = (true == options[:public_contents])
@client_headers.merge!(canned_acl_header('public-read')) if @public_contents
@strict = (true == options[:strict])
if @strict && !bucket_exists?(@name)
raise S33rException::MissingResource, "Non-existent bucket #{@bucket_name} specified"
end
yield self if block_given?
end
# Are all objects added to this bucket made public by default?
def public_contents?
@public_contents
end
# Is this a strict bucket (i.e. the target bucket must exist on S3)?
def strict?
@strict
end
# Get a single object from a bucket as an S3Object.
#
# To get a bare object (with no content):
#
# bucket['key']
#
# To get the object and load its content:
#
# bucket['key', :load]
def [](key, eager=false)
obj = listing.contents[key]
obj.named_bucket = self
obj.load if :load == eager
obj
end
# Get a raw response for a key inside the bucket.
def get_raw(key, headers={})
get_resource(@name, key, headers)
end
# Get a BucketListing instance for the content of this bucket.
# Uses the Client.list_bucket method to get the listing.
def listing
list_bucket(@name)[1]
end
# Does this bucket exist?
# Returns true if the bucket this NamedBucket is mapped to exists.
def exists?
bucket_exists?(@name)
end
# Delete the bucket.
def destroy(headers={}, options={})
delete_bucket(@name, headers, options)
end
# Get a pretty list of the keys in the bucket.
def keys
listing.pretty
end
# List content of the bucket, and attach each item to this NamedBucket
# instance as it is yielded (to enable easier manipulation directly from the S3Object).
# Note that the objects are incomplete, as the data associated with them has not been
# "got" from S3 yet.
def each_object
listing.contents.each_value { |obj| obj.named_bucket = self; yield obj }
end
# Does the given key exist in the bucket?
# Returns boolean
def key_exists?(key)
resource_exists?(@name, key)
end
# Put a string into a key inside the bucket.
def put_text(string, resource_key, headers={})
super(string, @name, resource_key, headers)
end
# Put a file into the bucket.
def put_file(filename, resource_key=nil, headers={}, options={})
super(filename, @name, resource_key, headers, options)
end
# Put a generic stream (e.g. from a file handle) into the bucket.
def put_stream(data, resource_key, headers={})
put_resource(@name, resource_key, data, headers)
end
# Delete an object from the bucket.
# NB S3 doesn't discriminate between successfully deleting a key
# and trying to delete a non-existent key (both return a 204).
# If you want to test for existence first, use key_exists?.
def delete(key, headers={})
delete_resource(@name, key, headers)
end
# Generate an authenticated URL (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/)
# for an object inside this bucket.
#
# +expires+: time in secs since the epoch when the link should become invalid.
def s3_authenticated_url(resource_key, expires=(Time.now.to_i + DEFAULT_EXPIRY_SECS))
super(@aws_access_key, @aws_secret_access_key, @name, resource_key, expires)
end
end
end