base = File.dirname(__FILE__) require File.join(base, 'libxml_loader') require File.join(base, 's33r_exception') module S33r # Represents a ListBucketResult # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/) class BucketListing < Hash # Name of the bucket this listing is for. attr_reader :name attr_reader :delimiter, :prefix, :marker, :max_keys, :is_truncated, :common_prefixes, :contents # +bucket_listing_xml+ is a ListBucketResult document, as returned from a GET on a bucket. # +bucket+ is a Bucket instance; any objects in the listing are assigned to this bucket. def initialize(bucket_listing_xml) @common_prefixes = [] set_listing_xml(bucket_listing_xml) end # Convert a ListBucketResult XML document into an object representation. def set_listing_xml(bucket_listing_xml) begin parse_listing(bucket_listing_xml) rescue message = "Cannot create bucket listing from supplied XML" message += " (was nil)" if bucket_listing_xml.nil? raise S3Exception::InvalidBucketListing, message end end # Parse raw XML ListBucketResponse from S3 into object instances. # The S3Objects are skeletons, and are not automatically populated # from S3 (their @value attributes are nil). To load the data into # an object, grab it from the listing and call the fetch method to # pull the data down from S3. # #-- TODO: common_prefixes def parse_listing(bucket_listing_xml) bucket_listing_xml = S33r.remove_namespace(bucket_listing_xml) doc = XML.get_xml_doc(bucket_listing_xml) prop_setter = lambda do |prop, path| node = doc.find("//ListBucketResult/#{path}").to_a.first self.send("#{prop}=", node.content) if node end # metadata prop_setter.call(:name, 'Name') prop_setter.call(:delimiter, 'Delimiter') prop_setter.call(:prefix, 'Prefix') prop_setter.call(:marker, 'Marker') prop_setter.call(:max_keys, 'MaxKeys') prop_setter.call(:is_truncated, 'IsTruncated') # contents doc.find('//Contents').to_a.each do |node| obj = S3Object.from_xml_node(node) # Add to the contents for the bucket self[obj.key] = obj end end # Setters which perform some type casts and normalisation. private def name=(val); @name = string_prop_normalise(val); end def prefix=(val); @prefix = string_prop_normalise(val); end def delimiter=(val); @delimiter = string_prop_normalise(val); end def marker=(val); @marker = string_prop_normalise(val); end def max_keys=(val); @max_keys = val.to_i; end def is_truncated=(val); @is_truncated = ('true' == val || true == val || 'True' == val); end # normalise string properties: # if value for XML element is nil, set property to empty string def string_prop_normalise(val) val = '' if val.nil? val end end end