#-- 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