Class: CloudFiles::StorageObject

Inherits:
Object
  • Object
show all
Defined in:
lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(container, objectname, force_exists = false, make_path = false) ⇒ StorageObject

Builds a new CloudFiles::StorageObject in the current container. If force_exist is set, the object must exist or a NoSuchObjectException will be raised. If not, an “empty” CloudFiles::StorageObject will be returned, ready for data via CloudFiles::StorageObject.write



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 35

def initialize(container,objectname,force_exists=false,make_path=false) 
  if objectname.match(/\?/)
    raise SyntaxException, "Object #{objectname} contains an invalid character in the name (? not allowed)"
  end
  @container = container
  @containername = container.name
  @name = objectname
  @make_path = make_path
  @storagehost = self.container.connection.storagehost
  @storagepath = self.container.connection.storagepath+"/#{@containername}/#{@name}"
  if container.object_exists?(objectname)
    populate
  else
    raise NoSuchObjectException, "Object #{@name} does not exist" if force_exists
  end
end

Instance Attribute Details

#bytesObject (readonly)

Size of the object (in bytes)



18
19
20
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 18

def bytes
  @bytes
end

#containerObject (readonly)

The parent CloudFiles::Container object



21
22
23
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 21

def container
  @container
end

#content_typeObject (readonly)

Content type of the object data



30
31
32
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 30

def content_type
  @content_type
end

#etagObject (readonly)

ETag of the object data



27
28
29
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 27

def etag
  @etag
end

#last_modifiedObject (readonly)

Date of the object’s last modification



24
25
26
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 24

def last_modified
  @last_modified
end

#nameObject (readonly)

Name of the object corresponding to the instantiated object



15
16
17
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 15

def name
  @name
end

Instance Method Details

#data(size = -1,, offset = 0, headers = {}) ⇒ Object

Retrieves the data from an object and stores the data in memory. The data is returned as a string. Throws a NoSuchObjectException if the object doesn’t exist.

If the optional size and range arguments are provided, the call will return the number of bytes provided by size, starting from the offset provided in offset.

object.data
=> "This is the text stored in the file"


76
77
78
79
80
81
82
83
84
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 76

def data(size=-1,offset=0,headers = {})
  if size.to_i > 0
    range = sprintf("bytes=%d-%d", offset.to_i, (offset.to_i + size.to_i) - 1)
    headers['Range'] = range
  end
  response = self.container.connection.cfreq("GET",@storagehost,@storagepath,headers)
  raise NoSuchObjectException, "Object #{@name} does not exist" unless (response.code =~ /^20/)
  response.body.chomp
end

#data_stream(size = -1,, offset = 0, headers = {}, &block) ⇒ Object

Retrieves the data from an object and returns a stream that must be passed to a block. Throws a NoSuchObjectException if the object doesn’t exist.

If the optional size and range arguments are provided, the call will return the number of bytes provided by size, starting from the offset provided in offset.

data = ""
object.data_stream do |chunk|
  data += chunk
end

data
=> "This is the text stored in the file"


99
100
101
102
103
104
105
106
107
108
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 99

def data_stream(size=-1,offset=0,headers = {},&block)
  if size.to_i > 0
    range = sprintf("bytes=%d-%d", offset.to_i, (offset.to_i + size.to_i) - 1)
    headers['Range'] = range
  end
  self.container.connection.cfreq("GET",@storagehost,@storagepath,headers,nil) do |response|
    raise NoSuchObjectException, "Object #{@name} does not exist" unless (response.code == "200")
    response.read_body(&block)
  end
end

#load_from_filename(filename) ⇒ Object

A convenience method to stream data into an object from a local file (or anything that can be loaded by Ruby’s open method)

Throws an Errno::ENOENT if the file cannot be read.

object.data
=> "This is my data"

object.load_from_filename("/tmp/file.txt")
=> true

object.data
=> "This data was in the file /tmp/file.txt"

object.load_from_filename("/tmp/nonexistent.txt")
=> Errno::ENOENT: No such file or directory - /tmp/nonexistent.txt


190
191
192
193
194
195
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 190

def load_from_filename(filename)
  f = open(filename)
  self.write(f)
  f.close
  true
end

#metadataObject

Returns the object’s metadata as a nicely formatted hash, stripping off the X-Meta-Object- prefix that the system prepends to the key name.

object.
=> {"ruby"=>"cool", "foo"=>"bar"}


115
116
117
118
119
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 115

def 
  metahash = {}
  .each{|key, value| metahash[key.gsub(/x-object-meta-/,'').gsub(/\+\-/, ' ')] = URI.decode(value).gsub(/\+\-/, ' ')}
  metahash
end

#populateObject Also known as: refresh

Caches data about the CloudFiles::StorageObject for fast retrieval. This method is automatically called when the class is initialized, but it can be called again if the data needs to be updated.



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 54

def populate
  response = self.container.connection.cfreq("HEAD",@storagehost,@storagepath)
  raise NoSuchObjectException, "Object #{@name} does not exist" if (response.code != "204")
  @bytes = response["content-length"]
  @last_modified = Time.parse(response["last-modified"])
  @etag = response["etag"]
  @content_type = response["content-type"]
  resphash = {}
  response.to_hash.select { |k,v| k.match(/^x-object-meta/) }.each { |x| resphash[x[0]] = x[1][0].to_s }
   = resphash
  true
end

#public_urlObject

If the parent container is public (CDN-enabled), returns the CDN URL to this object. Otherwise, return nil

public_object.public_url
=> "http://cdn.cloudfiles.mosso.com/c10181/rampage.jpg"

private_object.public_url
=> nil


229
230
231
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 229

def public_url
  self.container.public? ? self.container.cdn_url + "/#{URI.encode(@name)}" : nil
end

#save_to_filename(filename) ⇒ Object

A convenience method to stream data from an object into a local file

Throws an Errno::ENOENT if the file cannot be opened for writing due to a path error, and Errno::EACCES if the file cannot be opened for writing due to permissions.

object.data
=> "This is my data"

object.save_to_filename("/tmp/file.txt")
=> true

$ cat /tmp/file.txt
"This is my data"

object.save_to_filename("/tmp/owned_by_root.txt")
=> Errno::EACCES: Permission denied - /tmp/owned_by_root.txt


213
214
215
216
217
218
219
220
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 213

def save_to_filename(filename)
  ::File.open(filename, 'w+') do |f|
    self.data_stream do |chunk|
      f.write chunk
    end
  end
  true
end

#set_metadata(metadatahash) ⇒ Object

Sets the metadata for an object. By passing a hash as an argument, you can set the metadata for an object. However, setting metadata will overwrite any existing metadata for the object.

Throws NoSuchObjectException if the object doesn’t exist. Throws InvalidResponseException if the request fails.



126
127
128
129
130
131
132
133
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 126

def (metadatahash)
  headers = {}
  metadatahash.each{|key, value| headers['X-Object-Meta-' + key.to_s.capitalize] = value.to_s}
  response = self.container.connection.cfreq("POST",@storagehost,@storagepath,headers)
  raise NoSuchObjectException, "Object #{@name} does not exist" if (response.code == "404")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "202")
  true
end

#to_sObject

:nodoc:



233
234
235
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 233

def to_s # :nodoc:
  @name
end

#write(data = nil, headers = {}) ⇒ Object

Takes supplied data and writes it to the object, saving it. You can supply an optional hash of headers, including Content-Type and ETag, that will be applied to the object.

If you would rather stream the data in chunks, instead of reading it all into memory at once, you can pass an IO object for the data, such as: object.write(open(‘/path/to/file.mp3’))

You can compute your own MD5 sum and send it in the “ETag” header. If you provide yours, it will be compared to the MD5 sum on the server side. If they do not match, the server will return a 422 status code and a MisMatchedChecksumException will be raised. If you do not provide an MD5 sum as the ETag, one will be computed on the server side.

Updates the container cache and returns true on success, raises exceptions if stuff breaks.

object = container.create_object("newfile.txt")

object.write("This is new data")
=> true

object.data
=> "This is new data"

Raises:



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/vendor/cloudfiles-1.3.0/cloudfiles/storage_object.rb', line 154

def write(data=nil,headers={})
  #raise SyntaxException, "No data was provided for object '#{@name}'" if (data.nil?)
  # Try to get the content type
  raise SyntaxException, "No data or header updates supplied" if (data.nil? and headers.empty?)
  if headers['Content-Type'].nil?
    type = MIME::Types.type_for(self.name).first.to_s
    if type.empty?
      headers['Content-Type'] = "application/octet-stream"
    else
      headers['Content-Type'] = type
    end
  end
  response = self.container.connection.cfreq("PUT",@storagehost,"#{@storagepath}",headers,data)
  raise InvalidResponseException, "Invalid content-length header sent" if (response.code == "412")
  raise MisMatchedChecksumException, "Mismatched etag" if (response.code == "422")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "201")
  make_path(File.dirname(self.name)) if @make_path == true
  self.populate
  true
end