cloudfiles 1.4.17 → 1.4.18

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ ================================================================================
2
+ 1.4.18 (2011/09/07)
3
+ ================================================================================
4
+ o Added Streaming URL support
5
+
1
6
  ================================================================================
2
7
  1.4.17 (2011/05/27)
3
8
  ================================================================================
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
1
  source :gemcutter
2
2
 
3
3
  gemspec
4
+
5
+ group :development do
6
+ gem 'rake'
7
+ end
data/cloudfiles.gemspec CHANGED
@@ -1,4 +1,4 @@
1
- require 'lib/cloudfiles/version'
1
+ require File.expand_path('../lib/cloudfiles/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{cloudfiles}
@@ -14,29 +14,33 @@ module CloudFiles
14
14
  path = parsed_auth_url.path
15
15
  hdrhash = { "X-Auth-User" => connection.authuser, "X-Auth-Key" => connection.authkey }
16
16
  begin
17
- server = get_server(connection, parsed_auth_url)
17
+ server = get_server(connection, parsed_auth_url)
18
18
 
19
19
  if parsed_auth_url.scheme == "https"
20
20
  server.use_ssl = true
21
21
  server.verify_mode = OpenSSL::SSL::VERIFY_NONE
22
22
  end
23
23
  server.start
24
- rescue
25
- raise CloudFiles::Exception::Connection, "Unable to connect to #{server.address}"
24
+ rescue Exception => e
25
+ # uncomment if you suspect a problem with this branch of code
26
+ # $stderr.puts "got error #{e.class}: #{e.message.inspect}\n" << e.traceback.map{|n| "\t#{n}"}.join("\n")
27
+ raise CloudFiles::Exception::Connection, "Unable to connect to #{server.address}", caller
26
28
  end
27
29
  response = server.get(path, hdrhash)
28
30
  if (response.code =~ /^20./)
29
31
  if response["x-cdn-management-url"]
30
32
  connection.cdn_available = true
31
- connection.cdnmgmthost = URI.parse(response["x-cdn-management-url"]).host
32
- connection.cdnmgmtpath = URI.parse(response["x-cdn-management-url"]).path
33
- connection.cdnmgmtport = URI.parse(response["x-cdn-management-url"]).port
34
- connection.cdnmgmtscheme = URI.parse(response["x-cdn-management-url"]).scheme
33
+ parsed_cdn_url = URI.parse(response["x-cdn-management-url"])
34
+ connection.cdnmgmthost = parsed_cdn_url.host
35
+ connection.cdnmgmtpath = parsed_cdn_url.path
36
+ connection.cdnmgmtport = parsed_cdn_url.port
37
+ connection.cdnmgmtscheme = parsed_cdn_url.scheme
35
38
  end
36
- connection.storagehost = set_snet(connection, URI.parse(response["x-storage-url"]).host)
37
- connection.storagepath = URI.parse(response["x-storage-url"]).path
38
- connection.storageport = URI.parse(response["x-storage-url"]).port
39
- connection.storagescheme = URI.parse(response["x-storage-url"]).scheme
39
+ parsed_storage_url = URI.parse(response["x-storage-url"])
40
+ connection.storagehost = set_snet(connection, parsed_storage_url.host)
41
+ connection.storagepath = parsed_storage_url.path
42
+ connection.storageport = parsed_storage_url.port
43
+ connection.storagescheme = parsed_storage_url.scheme
40
44
  connection.authtoken = response["x-auth-token"]
41
45
  connection.authok = true
42
46
  else
@@ -133,7 +133,7 @@ module CloudFiles
133
133
  # cf.bytes
134
134
  # => 42438527
135
135
  def get_info
136
- response = cfreq("HEAD", @storagehost, @storagepath, @storageport, @storagescheme)
136
+ response = storage_request("HEAD")
137
137
  raise CloudFiles::Exception::InvalidResponse, "Unable to obtain account size" unless (response.code == "204")
138
138
  @bytes = response["x-account-bytes-used"].to_i
139
139
  @count = response["x-account-container-count"].to_i
@@ -166,7 +166,7 @@ module CloudFiles
166
166
  query = []
167
167
  query << "limit=#{CloudFiles.escape limit.to_s}" if limit.to_i > 0
168
168
  query << "marker=#{CloudFiles.escape marker.to_s}" unless marker.to_s.empty?
169
- response = cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
169
+ response = storage_request("GET", "?#{query.join('&')}")
170
170
  return [] if (response.code == "204")
171
171
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
172
172
  CloudFiles.lines(response.body)
@@ -187,7 +187,7 @@ module CloudFiles
187
187
  query = ['format=xml']
188
188
  query << "limit=#{CloudFiles.escape limit.to_s}" if limit.to_i > 0
189
189
  query << "marker=#{CloudFiles.escape marker.to_s}" unless marker.to_s.empty?
190
- response = cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
190
+ response = storage_request("GET", "?#{query.join('&')}")
191
191
  return {} if (response.code == "204")
192
192
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
193
193
  doc = REXML::Document.new(response.body)
@@ -208,7 +208,7 @@ module CloudFiles
208
208
  # cf.container_exists?('bad_container')
209
209
  # => false
210
210
  def container_exists?(containername)
211
- response = cfreq("HEAD", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
211
+ response = storage_request("HEAD", CloudFiles.escape(containername))
212
212
  return (response.code == "204")? true : false ;
213
213
  end
214
214
 
@@ -227,7 +227,7 @@ module CloudFiles
227
227
  def create_container(containername)
228
228
  raise CloudFiles::Exception::Syntax, "Container name cannot contain the characters '/' or '?'" if containername.match(/[\/\?]/)
229
229
  raise CloudFiles::Exception::Syntax, "Container name is limited to 256 characters" if containername.length > 256
230
- response = cfreq("PUT", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
230
+ response = storage_request("PUT", CloudFiles.escape(containername))
231
231
  raise CloudFiles::Exception::InvalidResponse, "Unable to create container #{containername}" unless (response.code == "201" || response.code == "202")
232
232
  CloudFiles::Container.new(self, containername)
233
233
  end
@@ -244,7 +244,7 @@ module CloudFiles
244
244
  # cf.delete_container('nonexistent')
245
245
  # => NoSuchContainerException: Container nonexistent does not exist
246
246
  def delete_container(containername)
247
- response = cfreq("DELETE", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
247
+ response = storage_request("DELETE", CloudFiles.escape(containername))
248
248
  raise CloudFiles::Exception::NonEmptyContainer, "Container #{containername} is not empty" if (response.code == "409")
249
249
  raise CloudFiles::Exception::NoSuchContainer, "Container #{containername} does not exist" unless (response.code == "204")
250
250
  true
@@ -261,12 +261,22 @@ module CloudFiles
261
261
  # => ["video", "webpics"]
262
262
  def public_containers(enabled_only = false)
263
263
  paramstr = enabled_only == true ? "enabled_only=true" : ""
264
- response = cfreq("GET", @cdnmgmthost, "#{@cdnmgmtpath}?#{paramstr}", @cdnmgmtport, @cdnmgmtscheme)
264
+ response = cdn_request("GET", "?#{paramstr}")
265
265
  return [] if (response.code == "204")
266
266
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
267
267
  CloudFiles.lines(response.body)
268
268
  end
269
269
 
270
+ def storage_request(method, path = "", headers = {}, data = nil, attempts = 0, &block)
271
+ path = "#{@storagepath}/#{path}" unless (path[0,1] == '/')
272
+ cfreq(method, @storagehost, path, @storageport, @storagescheme, headers, data, attempts, &block)
273
+ end
274
+
275
+ def cdn_request(method, path = "", headers = {}, data = nil, attempts = 0, &block)
276
+ path = "#{@cdnmgmtpath}/#{path}" unless (path[0,1] == '/')
277
+ cfreq(method, @cdnmgmthost, path, @cdnmgmtport, @cdnmgmtscheme, headers, data, attempts, &block)
278
+ end
279
+
270
280
  # This method actually makes the HTTP calls out to the server
271
281
  def cfreq(method, server, path, port, scheme, headers = {}, data = nil, attempts = 0, &block) # :nodoc:
272
282
  start = Time.now
@@ -292,7 +302,11 @@ module CloudFiles
292
302
  response
293
303
  rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError, IOError
294
304
  # Server closed the connection, retry
295
- raise CloudFiles::Exception::Connection, "Unable to reconnect to #{server.address} after #{attempts} attempts" if attempts >= 5
305
+ if attempts >= 5
306
+ host_str = server.respond_to?(:address) ? server.address : server
307
+ raise CloudFiles::Exception::Connection, "Unable to reconnect to #{host_str} after #{attempts} attempts", caller
308
+ end
309
+
296
310
  attempts += 1
297
311
  begin
298
312
  @http[server].finish
@@ -302,7 +316,7 @@ module CloudFiles
302
316
  start_http(server, path, port, scheme, headers)
303
317
  retry
304
318
  rescue ExpiredAuthTokenException
305
- raise CloudFiles::Exception::Connection, "Authentication token expired and you have requested not to retry" if @retry_auth == false
319
+ raise CloudFiles::Exception::Connection, "Authentication token expired and you have requested not to retry", caller if @retry_auth == false
306
320
  CloudFiles::Authentication.new(self)
307
321
  retry
308
322
  end
@@ -329,8 +343,8 @@ module CloudFiles
329
343
  @http[server].verify_mode = OpenSSL::SSL::VERIFY_NONE
330
344
  end
331
345
  @http[server].start
332
- rescue
333
- raise CloudFiles::Exception::Connection, "Unable to connect to #{server.address}"
346
+ rescue Exception
347
+ raise CloudFiles::Exception::Connection, "Unable to connect to #{server.address}", caller
334
348
  end
335
349
  end
336
350
  end
@@ -16,16 +16,6 @@ module CloudFiles
16
16
  def initialize(connection, name)
17
17
  @connection = connection
18
18
  @name = name
19
- @storagehost = self.connection.storagehost
20
- @storagepath = self.connection.storagepath + "/" + CloudFiles.escape(@name)
21
- @storageport = self.connection.storageport
22
- @storagescheme = self.connection.storagescheme
23
- if self.connection.cdn_available?
24
- @cdnmgmthost = self.connection.cdnmgmthost
25
- @cdnmgmtpath = self.connection.cdnmgmtpath + "/" + CloudFiles.escape(@name) if self.connection.cdnmgmtpath
26
- @cdnmgmtport = self.connection.cdnmgmtport
27
- @cdnmgmtscheme = self.connection.cdnmgmtscheme
28
- end
29
19
  # Load the metadata now, so we'll get a CloudFiles::Exception::NoSuchContainer exception should the container
30
20
  # not exist.
31
21
  self.container_metadata
@@ -51,11 +41,11 @@ module CloudFiles
51
41
  # Retrieves Metadata for the container
52
42
  def container_metadata
53
43
  @metadata ||= (
54
- response = self.connection.cfreq("HEAD", @storagehost, @storagepath + "/", @storageport, @storagescheme)
44
+ response = self.connection.storage_request("HEAD", "#{escaped_name}/")
55
45
  raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code =~ /^20/)
56
46
  resphash = {}
57
47
  response.to_hash.select { |k,v| k.match(/^x-container-meta/) }.each { |x| resphash[x[0]] = x[1].to_s }
58
- {:bytes => response["x-container-bytes-used"].to_i, :count => response["x-container-object-count"].to_i, :metadata => resphash}
48
+ {:bytes => response["x-container-bytes-used"].to_i, :count => response["x-container-object-count"].to_i, :metadata => resphash, :container_read => response["x-container-read"], :container_write => response["x-container-write"]}
59
49
  )
60
50
  end
61
51
 
@@ -64,13 +54,14 @@ module CloudFiles
64
54
  return @cdn_metadata if @cdn_metadata
65
55
  if cdn_available?
66
56
  @cdn_metadata = (
67
- response = self.connection.cfreq("HEAD", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
57
+ response = self.connection.cdn_request("HEAD", escaped_name)
68
58
  cdn_enabled = ((response["x-cdn-enabled"] || "").downcase == "true") ? true : false
69
59
  {
70
60
  :cdn_enabled => cdn_enabled,
71
61
  :cdn_ttl => cdn_enabled ? response["x-ttl"].to_i : nil,
72
62
  :cdn_url => cdn_enabled ? response["x-cdn-uri"] : nil,
73
63
  :cdn_ssl_url => cdn_enabled ? response["x-cdn-ssl-uri"] : nil,
64
+ :cdn_streaming_url => cdn_enabled ? response["x-cdn-streaming-uri"] : nil,
74
65
  :user_agent_acl => response["x-user-agent-acl"],
75
66
  :referrer_acl => response["x-referrer-acl"],
76
67
  :cdn_log => (cdn_enabled and response["x-log-retention"] == "True") ? true : false
@@ -100,7 +91,7 @@ module CloudFiles
100
91
  def set_metadata(metadatahash)
101
92
  headers = {}
102
93
  metadatahash.each{ |key, value| headers['X-Container-Meta-' + CloudFiles.escape(key.to_s.capitalize)] = value.to_s }
103
- response = self.connection.cfreq("POST", @storagehost, @storagepath, @storageport, @storagescheme, headers)
94
+ response = self.connection.storage_request("POST", escaped_name, headers)
104
95
  raise CloudFiles::Exception::NoSuchObject, "Container #{@name} does not exist" if (response.code == "404")
105
96
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
106
97
  true
@@ -108,12 +99,12 @@ module CloudFiles
108
99
 
109
100
  # Size of the container (in bytes)
110
101
  def bytes
111
- self.metadata[:bytes]
102
+ self.container_metadata[:bytes]
112
103
  end
113
104
 
114
105
  # Number of objects in the container
115
106
  def count
116
- self.metadata[:count]
107
+ self.container_metadata[:count]
117
108
  end
118
109
 
119
110
  # Returns true if the container is public and CDN-enabled. Returns false otherwise.
@@ -145,6 +136,10 @@ module CloudFiles
145
136
  def cdn_ssl_url
146
137
  self.cdn_metadata[:cdn_ssl_url]
147
138
  end
139
+ # CDN Streaming container URL (if container is public)
140
+ def cdn_ssl_url
141
+ self.cdn_metadata[:cdn_streaming_url]
142
+ end
148
143
 
149
144
  # The container ACL on the User Agent
150
145
  def user_agent_acl
@@ -156,6 +151,16 @@ module CloudFiles
156
151
  self.cdn_metadata[:referrer_acl]
157
152
  end
158
153
 
154
+ #used by openstack swift
155
+ def read_acl
156
+ self.container_metadata[:container_read]
157
+ end
158
+
159
+ #used by openstack swift
160
+ def write_acl
161
+ self.container_metadata[:container_write]
162
+ end
163
+
159
164
  # Returns true if log retention is enabled on this container, false otherwise
160
165
  def cdn_log
161
166
  self.cdn_metadata[:cdn_log]
@@ -166,10 +171,10 @@ module CloudFiles
166
171
  # Change the log retention status for this container. Values are true or false.
167
172
  #
168
173
  # These logs will be periodically (at unpredictable intervals) compressed and uploaded
169
- # to a “.CDN_ACCESS_LOGS container in the form of container_name.YYYYMMDDHH-XXXX.gz”.
174
+ # to a ".CDN_ACCESS_LOGS" container in the form of "container_name.YYYYMMDDHH-XXXX.gz".
170
175
  def log_retention=(value)
171
176
  raise Exception::CDNNotAvailable unless cdn_available?
172
- response = self.connection.cfreq("POST", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, {"x-log-retention" => value.to_s.capitalize})
177
+ response = self.connection.cdn_request("POST", escaped_name, {"x-log-retention" => value.to_s.capitalize})
173
178
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "201" or response.code == "202")
174
179
  return true
175
180
  end
@@ -216,7 +221,7 @@ module CloudFiles
216
221
  query << "#{param}=#{CloudFiles.escape(value.to_s)}"
217
222
  end
218
223
  end
219
- response = self.connection.cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
224
+ response = self.connection.storage_request("GET", "#{escaped_name}?#{query.join '&'}")
220
225
  return [] if (response.code == "204")
221
226
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
222
227
  return CloudFiles.lines(response.body)
@@ -249,7 +254,7 @@ module CloudFiles
249
254
  query << "#{param}=#{CloudFiles.escape(value.to_s)}"
250
255
  end
251
256
  end
252
- response = self.connection.cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
257
+ response = self.connection.storage_request("GET", "#{escaped_name}?#{query.join '&'}")
253
258
  return {} if (response.code == "204")
254
259
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
255
260
  doc = REXML::Document.new(response.body)
@@ -281,7 +286,7 @@ module CloudFiles
281
286
  # container.object_exists?('badfile.txt')
282
287
  # => false
283
288
  def object_exists?(objectname)
284
- response = self.connection.cfreq("HEAD", @storagehost, "#{@storagepath}/#{CloudFiles.escape objectname}", @storageport, @storagescheme)
289
+ response = self.connection.storage_request("HEAD", "#{escaped_name}/#{CloudFiles.escape objectname}")
285
290
  return (response.code =~ /^20/)? true : false
286
291
  end
287
292
 
@@ -306,7 +311,7 @@ module CloudFiles
306
311
  # container.delete_object('nonexistent_file.txt')
307
312
  # => NoSuchObjectException: Object nonexistent_file.txt does not exist
308
313
  def delete_object(objectname)
309
- response = self.connection.cfreq("DELETE", @storagehost, "#{@storagepath}/#{CloudFiles.escape objectname}", @storageport, @storagescheme)
314
+ response = self.connection.storage_request("DELETE", "#{escaped_name}/#{CloudFiles.escape objectname}")
310
315
  raise CloudFiles::Exception::NoSuchObject, "Object #{objectname} does not exist" if (response.code == "404")
311
316
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
312
317
  true
@@ -333,17 +338,43 @@ module CloudFiles
333
338
  options = {:ttl => ttl}
334
339
  end
335
340
 
336
- response = self.connection.cfreq("PUT", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
341
+ response = self.connection.cdn_request("PUT", escaped_name)
337
342
  raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")
338
343
 
339
344
  headers = { "X-TTL" => options[:ttl].to_s , "X-CDN-Enabled" => "True" }
340
345
  headers["X-User-Agent-ACL"] = options[:user_agent_acl] if options[:user_agent_acl]
341
346
  headers["X-Referrer-ACL"] = options[:referrer_acl] if options[:referrer_acl]
342
- response = self.connection.cfreq("POST", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
347
+ response = post_with_headers(headers)
343
348
  raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")
344
349
  refresh
345
350
  true
346
351
  end
352
+
353
+ # Only to be used with openstack swift
354
+ def set_write_acl(write_string)
355
+ refresh
356
+ headers = {"X-Container-Write" => write_string}
357
+ post_with_headers(headers)
358
+ true
359
+ end
360
+
361
+ # Only to be used with openstack swift
362
+ def set_read_acl(read_string)
363
+ refresh
364
+ headers = {"X-Container-Read" => read_string}
365
+ post_with_headers(headers)
366
+ true
367
+ end
368
+
369
+ def post_with_headers(headers = {})
370
+ if cdn_enabled?
371
+ response = self.connection.cdn_request("POST", escaped_name, headers)
372
+ else
373
+ response = self.connection.storage_request("POST", escaped_name, headers)
374
+ end
375
+ raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist (response code: #{response.code})" unless (response.code =~ /^20/)
376
+ response
377
+ end
347
378
 
348
379
  # Makes a container private and returns true upon success. Throws NoSuchContainerException
349
380
  # if the container doesn't exist or if the request fails.
@@ -355,7 +386,7 @@ module CloudFiles
355
386
  def make_private
356
387
  raise Exception::CDNNotAvailable unless cdn_available?
357
388
  headers = { "X-CDN-Enabled" => "False" }
358
- response = self.connection.cfreq("POST", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
389
+ response = self.connection.cdn_request("POST", escaped_name, headers)
359
390
  raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")
360
391
  refresh
361
392
  true
@@ -380,15 +411,11 @@ module CloudFiles
380
411
  # => true
381
412
  def purge_from_cdn(email=nil)
382
413
  raise Exception::CDNNotAvailable unless cdn_available?
383
- if email
384
- headers = {"X-Purge-Email" => email}
385
- response = self.connection.cfreq("DELETE", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
386
- raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (response.code > "200" && response.code < "299")
387
- else
388
- response = self.connection.cfreq("DELETE", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
389
- raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (response.code > "200" && response.code < "299")
414
+ headers = {}
415
+ headers = {"X-Purge-Email" => email} if email
416
+ response = self.connection.cdn_request("DELETE", escaped_name, headers)
417
+ raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (response.code > "200" && response.code < "299")
390
418
  true
391
- end
392
419
  end
393
420
 
394
421
  def to_s # :nodoc:
@@ -398,6 +425,10 @@ module CloudFiles
398
425
  def cdn_available?
399
426
  self.connection.cdn_available?
400
427
  end
428
+
429
+ def escaped_name
430
+ CloudFiles.escape(@name)
431
+ end
401
432
 
402
433
  end
403
434
 
@@ -17,16 +17,8 @@ module CloudFiles
17
17
  @containername = container.name
18
18
  @name = objectname
19
19
  @make_path = make_path
20
- @storagehost = self.container.connection.storagehost
21
- @storagepath = self.container.connection.storagepath + "/#{CloudFiles.escape @containername}/#{CloudFiles.escape @name, '/'}"
22
- @storageport = self.container.connection.storageport
23
- @storagescheme = self.container.connection.storagescheme
24
- if self.container.connection.cdn_available?
25
- @cdnmgmthost = self.container.connection.cdnmgmthost
26
- @cdnmgmtpath = self.container.connection.cdnmgmtpath + "/#{CloudFiles.escape @containername}/#{CloudFiles.escape @name, '/'}"
27
- @cdnmgmtport = self.container.connection.cdnmgmtport
28
- @cdnmgmtscheme = self.container.connection.cdnmgmtscheme
29
- end
20
+ @storagepath = "#{CloudFiles.escape @containername}/#{CloudFiles.escape @name, '/'}"
21
+
30
22
  if force_exists
31
23
  raise CloudFiles::Exception::NoSuchObject, "Object #{@name} does not exist" unless container.object_exists?(objectname)
32
24
  end
@@ -42,10 +34,15 @@ module CloudFiles
42
34
  # Retrieves Metadata for the object
43
35
  def object_metadata
44
36
  @object_metadata ||= (
45
- response = self.container.connection.cfreq("HEAD", @storagehost, @storagepath, @storageport, @storagescheme)
37
+ response = self.container.connection.storage_request("HEAD", @storagepath)
46
38
  raise CloudFiles::Exception::NoSuchObject, "Object #{@name} does not exist" unless (response.code =~ /^20/)
47
39
  resphash = {}
48
- response.to_hash.select { |k,v| k.match(/^x-object-meta/) }.each { |x| resphash[x[0]] = x[1].to_s }
40
+ metas = response.to_hash.select { |k,v| k.match(/^x-object-meta/) }
41
+
42
+ metas.each do |x,y|
43
+ resphash[x] = (y.respond_to?(:join) ? y.join('') : y.to_s)
44
+ end
45
+
49
46
  {
50
47
  :manifest => response["x-object-manifest"],
51
48
  :bytes => response["content-length"],
@@ -76,6 +73,10 @@ module CloudFiles
76
73
  def content_type
77
74
  self.object_metadata[:content_type]
78
75
  end
76
+
77
+ def content_type=(type)
78
+ self.copy(:headers => {'Content-Type' => type})
79
+ end
79
80
 
80
81
  # Retrieves the data from an object and stores the data in memory. The data is returned as a string.
81
82
  # Throws a NoSuchObjectException if the object doesn't exist.
@@ -90,7 +91,7 @@ module CloudFiles
90
91
  range = sprintf("bytes=%d-%d", offset.to_i, (offset.to_i + size.to_i) - 1)
91
92
  headers['Range'] = range
92
93
  end
93
- response = self.container.connection.cfreq("GET", @storagehost, @storagepath, @storageport, @storagescheme, headers)
94
+ response = self.container.connection.storage_request("GET", @storagepath, headers)
94
95
  raise CloudFiles::Exception::NoSuchObject, "Object #{@name} does not exist" unless (response.code =~ /^20/)
95
96
  response.body
96
97
  end
@@ -114,7 +115,7 @@ module CloudFiles
114
115
  range = sprintf("bytes=%d-%d", offset.to_i, (offset.to_i + size.to_i) - 1)
115
116
  headers['Range'] = range
116
117
  end
117
- self.container.connection.cfreq("GET", @storagehost, @storagepath, @storageport, @storagescheme, headers, nil) do |response|
118
+ self.container.connection.storage_request("GET", @storagepath, headers, nil) do |response|
118
119
  raise CloudFiles::Exception::NoSuchObject, "Object #{@name} does not exist. Response code #{response.code}" unless (response.code =~ /^20./)
119
120
  response.read_body(&block)
120
121
  end
@@ -139,11 +140,12 @@ module CloudFiles
139
140
  def set_metadata(metadatahash)
140
141
  headers = {}
141
142
  metadatahash.each{ |key, value| headers['X-Object-Meta-' + key.to_s.capitalize] = value.to_s }
142
- response = self.container.connection.cfreq("POST", @storagehost, @storagepath, @storageport, @storagescheme, headers)
143
+ response = self.container.connection.storage_request("POST", @storagepath, headers)
143
144
  raise CloudFiles::Exception::NoSuchObject, "Object #{@name} does not exist" if (response.code == "404")
144
- raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "202")
145
+ raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
145
146
  true
146
147
  end
148
+ alias :metadata= :set_metadata
147
149
 
148
150
 
149
151
  # Returns the object's manifest.
@@ -162,7 +164,7 @@ module CloudFiles
162
164
  # fails.
163
165
  def set_manifest(manifest)
164
166
  headers = {'X-Object-Manifest' => manifest}
165
- response = self.container.connection.cfreq("PUT", @storagehost, @storagepath, @storageport, @storagescheme, headers)
167
+ response = self.container.connection.storage_request("PUT", @storagepath, headers)
166
168
  raise CloudFiles::Exception::NoSuchObject, "Object #{@name} does not exist" if (response.code == "404")
167
169
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
168
170
  true
@@ -209,7 +211,7 @@ module CloudFiles
209
211
  end
210
212
  # If we're taking data from standard input, send that IO object to cfreq
211
213
  data = $stdin if (data.nil? && $stdin.tty? == false)
212
- response = self.container.connection.cfreq("PUT", @storagehost, "#{@storagepath}", @storageport, @storagescheme, headers, data)
214
+ response = self.container.connection.storage_request("PUT", @storagepath, headers, data)
213
215
  code = response.code
214
216
  raise CloudFiles::Exception::InvalidResponse, "Invalid content-length header sent" if (code == "412")
215
217
  raise CloudFiles::Exception::MisMatchedChecksum, "Mismatched etag" if (code == "422")
@@ -237,14 +239,10 @@ module CloudFiles
237
239
  # => true
238
240
  def purge_from_cdn(email=nil)
239
241
  raise Exception::CDNNotAvailable unless cdn_available?
240
- if email
241
- headers = {"X-Purge-Email" => email}
242
- response = self.container.connection.cfreq("DELETE", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
243
- raise CloudFiles::Exception::Connection, "Error Unable to Purge Object: #{@name}" unless (response.code.to_s =~ /^20.$/)
244
- else
245
- response = self.container.connection.cfreq("DELETE", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
246
- raise CloudFiles::Exception::Connection, "Error Unable to Purge Object: #{@name}" unless (response.code.to_s =~ /^20.$/)
247
- end
242
+ headers = {}
243
+ headers = {"X-Purge-Email" => email} if email
244
+ response = self.container.connection.cdn_request("DELETE", @storagepath, headers)
245
+ raise CloudFiles::Exception::Connection, "Error Unable to Purge Object: #{@name}" unless (response.code.to_s =~ /^20.$/)
248
246
  true
249
247
  end
250
248
 
@@ -296,7 +294,7 @@ module CloudFiles
296
294
  # object.save_to_filename("/tmp/owned_by_root.txt")
297
295
  # => Errno::EACCES: Permission denied - /tmp/owned_by_root.txt
298
296
  def save_to_filename(filename)
299
- File.open(filename, 'w+') do |f|
297
+ File.open(filename, 'wb+') do |f|
300
298
  self.data_stream do |chunk|
301
299
  f.write chunk
302
300
  end
@@ -315,7 +313,7 @@ module CloudFiles
315
313
  self.container.public? ? self.container.cdn_url + "/#{CloudFiles.escape @name, '/'}" : nil
316
314
  end
317
315
 
318
- # If the parent container is public (CDN-enabled), returns the SSL CDN URL to this object. Otherwise, return nil
316
+ # If the parent container is public (CDN-enabled), returns the SSL CDN URL to this object. Otherwise, return nil
319
317
  #
320
318
  # public_object.public_ssl_url
321
319
  # => "https://c61.ssl.cf0.rackcdn.com/myfile.jpg"
@@ -326,6 +324,16 @@ module CloudFiles
326
324
  self.container.public? ? self.container.cdn_ssl_url + "/#{CloudFiles.escape @name, '/'}" : nil
327
325
  end
328
326
 
327
+ # If the parent container is public (CDN-enabled), returns the SSL CDN URL to this object. Otherwise, return nil
328
+ #
329
+ # public_object.public_streaming_url
330
+ # => "https://c61.stream.rackcdn.com/myfile.jpg"
331
+ #
332
+ # private_object.public_streaming_url
333
+ # => nil
334
+ def public_streaming_url
335
+ self.container.public? ? self.container.cdn_streaming_url + "/#{CloudFiles.escape @name, '/'}" : nil
336
+ end
329
337
 
330
338
  # Copy this object to a new location (optionally in a new container)
331
339
  #
@@ -342,7 +350,7 @@ module CloudFiles
342
350
  #
343
351
  # Returns the new CloudFiles::StorageObject for the copied item.
344
352
  def copy(options = {})
345
- raise CloudFiles::Exception::Syntax, "You must either provide the :container or the :name for this operation" unless (options[:container] || options[:name])
353
+ raise CloudFiles::Exception::Syntax, "You must provide the :container, :name, or :headers for this operation" unless (options[:container] || options[:name] || options[:headers])
346
354
  new_container = options[:container] || self.container.name
347
355
  new_name = options[:name] || self.name
348
356
  new_headers = options[:headers] || {}
@@ -350,8 +358,8 @@ module CloudFiles
350
358
  new_name.sub!(/^\//,'')
351
359
  headers = {'X-Copy-From' => "#{self.container.name}/#{self.name}", 'Content-Type' => self.content_type.sub(/;.+/, '')}.merge(new_headers)
352
360
  # , 'Content-Type' => self.content_type
353
- new_path = self.container.connection.storagepath + "/#{CloudFiles.escape new_container}/#{CloudFiles.escape new_name, '/'}"
354
- response = self.container.connection.cfreq("PUT", @storagehost, new_path, @storageport, @storagescheme, headers)
361
+ new_path = "#{CloudFiles.escape new_container}/#{CloudFiles.escape new_name, '/'}"
362
+ response = self.container.connection.storage_request("PUT", new_path, headers)
355
363
  code = response.code
356
364
  raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
357
365
  return CloudFiles::Container.new(self.container.connection, new_container).object(new_name)
@@ -1,3 +1,3 @@
1
1
  module CloudFiles
2
- VERSION = '1.4.17'
2
+ VERSION = '1.4.18'
3
3
  end
@@ -81,7 +81,49 @@ class CloudfilesConnectionTest < Test::Unit::TestCase
81
81
  response = @connection.cfreq("HEAD", "test.server.example", "/dummypath", "80", "http")
82
82
  end
83
83
  end
84
-
84
+
85
+ def test_storage_request
86
+ build_net_http_object
87
+ assert_nothing_raised do
88
+ response = @connection.storage_request("HEAD", "dummypath")
89
+ end
90
+ end
91
+
92
+ def test_storage_request_adds_path
93
+ build_net_http_object({}, {:method => "HEAD", :path => "#{@connection.storagepath}/dummypath"})
94
+ assert_nothing_raised do
95
+ response = @connection.storage_request("HEAD", "dummypath")
96
+ end
97
+ end
98
+
99
+ def test_storage_path_does_not_add_path_when_absolute
100
+ build_net_http_object({}, {:method => "HEAD", :path => "/dummypath"})
101
+ assert_nothing_raised do
102
+ response = @connection.storage_request("HEAD", "/dummypath")
103
+ end
104
+ end
105
+
106
+ def test_cdn_request
107
+ build_net_http_object
108
+ assert_nothing_raised do
109
+ response = @connection.cdn_request("HEAD", "dummypath")
110
+ end
111
+ end
112
+
113
+ def test_cdn_request_adds_path
114
+ build_net_http_object({}, {:method => "HEAD", :path => "#{@connection.cdnmgmtpath}/dummypath"})
115
+ assert_nothing_raised do
116
+ response = @connection.cdn_request("HEAD", "dummypath")
117
+ end
118
+ end
119
+
120
+ def test_cdn_path_does_not_add_path_when_absolute
121
+ build_net_http_object({}, {:method => "HEAD", :path => "/dummypath"})
122
+ assert_nothing_raised do
123
+ response = @connection.cdn_request("HEAD", "/dummypath")
124
+ end
125
+ end
126
+
85
127
  def test_net_http_raises_connection_exception
86
128
  Net::HTTP.expects(:new).raises(CloudFiles::Exception::Connection)
87
129
  assert_raises(CloudFiles::Exception::Connection) do
@@ -288,7 +330,8 @@ class CloudfilesConnectionTest < Test::Unit::TestCase
288
330
 
289
331
  private
290
332
 
291
- def build_net_http_object(args={:code => '204' }, cfreq_expectations={})
333
+ def build_net_http_object(args={}, cfreq_expectations={})
334
+ args.merge!(:code => '204') unless args[:code]
292
335
  args[:response] = {} unless args[:response]
293
336
  response = {'x-cdn-management-url' => 'http://cdn.example.com/path', 'x-storage-url' => 'http://cdn.example.com/storage', 'authtoken' => 'dummy_token'}.merge(args[:response])
294
337
  response.stubs(:code).returns(args[:code])
@@ -7,7 +7,8 @@ class CloudfilesContainerTest < Test::Unit::TestCase
7
7
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
8
8
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5'}
9
9
  response.stubs(:code).returns('204')
10
- connection.stubs(:cfreq => response)
10
+ connection.stubs(:storage_request => response)
11
+ connection.stubs(:cdn_request => response)
11
12
  @container = CloudFiles::Container.new(connection, 'test_container')
12
13
  assert_equal @container.name, 'test_container'
13
14
  assert_equal @container.class, CloudFiles::Container
@@ -20,7 +21,7 @@ class CloudfilesContainerTest < Test::Unit::TestCase
20
21
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => false)
21
22
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5'}
22
23
  response.stubs(:code).returns('204')
23
- connection.stubs(:cfreq => response)
24
+ connection.stubs(:storage_request => response)
24
25
  @container = CloudFiles::Container.new(connection, 'test_container')
25
26
  assert_equal 'test_container', @container.name
26
27
  assert_equal CloudFiles::Container, @container.class
@@ -31,7 +32,7 @@ class CloudfilesContainerTest < Test::Unit::TestCase
31
32
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
32
33
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5'}
33
34
  response.stubs(:code).returns('999')
34
- connection.stubs(:cfreq => response)
35
+ connection.stubs(:storage_request => response)
35
36
  assert_raise(CloudFiles::Exception::NoSuchContainer) do
36
37
  @container = CloudFiles::Container.new(connection, 'test_container')
37
38
  end
@@ -41,7 +42,8 @@ class CloudfilesContainerTest < Test::Unit::TestCase
41
42
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
42
43
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5', 'x-cdn-enabled' => 'True', 'x-cdn-uri' => 'http://cdn.test.example/container', 'x-ttl' => '86400'}
43
44
  response.stubs(:code).returns('204')
44
- connection.stubs(:cfreq => response)
45
+ connection.stubs(:storage_request => response)
46
+ connection.stubs(:cdn_request => response)
45
47
  @container = CloudFiles::Container.new(connection, 'test_container')
46
48
  assert_equal @container.name, 'test_container'
47
49
  assert_equal @container.cdn_enabled, true
@@ -56,28 +58,28 @@ class CloudfilesContainerTest < Test::Unit::TestCase
56
58
  end
57
59
 
58
60
  def test_make_private_succeeds
59
- build_net_http_object(:code => '201')
61
+ build_net_http_object(:code => '201', :cdn_request => true)
60
62
  assert_nothing_raised do
61
63
  @container.make_private
62
64
  end
63
65
  end
64
66
 
65
67
  def test_make_private_fails
66
- build_net_http_object(:code => '999')
68
+ build_net_http_object(:code => '999', :cdn_request => true)
67
69
  assert_raises(CloudFiles::Exception::NoSuchContainer) do
68
70
  @container.make_private
69
71
  end
70
72
  end
71
73
 
72
74
  def test_make_public_succeeds
73
- build_net_http_object(:code => '201')
75
+ build_net_http_object(:code => '201', :cdn_request => true)
74
76
  assert_nothing_raised do
75
77
  @container.make_public
76
78
  end
77
79
  end
78
80
 
79
81
  def test_make_public_fails
80
- build_net_http_object(:code => '999')
82
+ build_net_http_object(:code => '999', :cdn_request => true)
81
83
  assert_raises(CloudFiles::Exception::NoSuchContainer) do
82
84
  @container.make_public
83
85
  end
@@ -87,7 +89,7 @@ class CloudfilesContainerTest < Test::Unit::TestCase
87
89
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
88
90
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5'}
89
91
  response.stubs(:code).returns('204')
90
- connection.stubs(:cfreq => response)
92
+ connection.stubs(:storage_request => response)
91
93
  @container = CloudFiles::Container.new(connection, 'test_container')
92
94
  assert_equal @container.empty?, false
93
95
  end
@@ -96,16 +98,80 @@ class CloudfilesContainerTest < Test::Unit::TestCase
96
98
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
97
99
  response = {'x-container-bytes-used' => '0', 'x-container-object-count' => '0'}
98
100
  response.stubs(:code).returns('204')
99
- connection.stubs(:cfreq => response)
101
+ connection.stubs(:storage_request => response)
100
102
  @container = CloudFiles::Container.new(connection, 'test_container')
101
103
  assert_equal @container.empty?, true
102
104
  end
105
+
106
+ def build_acl_test_state(opts={})
107
+ @connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdn_available? => false)
108
+
109
+ @response = {
110
+ 'x-container-bytes-used' => '0',
111
+ 'x-container-object-count' => '0',
112
+ }.merge(opts.fetch(:response, {}))
113
+
114
+ @response.stubs(:code).returns(opts.fetch(:code, '204'))
115
+ @connection.stubs(:storage_request => @response)
116
+ @container = CloudFiles::Container.new(@connection, 'test_container')
117
+ end
103
118
 
119
+ def test_read_acl_is_set_from_headers
120
+ build_acl_test_state :response => { 'x-container-read' => '.r:*' }, :code => '204'
121
+ assert_equal @response["x-container-read"], @container.read_acl
122
+ end
123
+
124
+ def test_set_read_acl_fails
125
+ build_acl_test_state
126
+
127
+ response = stub(:code => '999')
128
+
129
+ @connection.stubs(:storage_request => response)
130
+
131
+ assert_raises(CloudFiles::Exception::NoSuchContainer) do
132
+ @container.set_read_acl('.r:*')
133
+ end
134
+ end
135
+
136
+ def test_set_read_acl_succeeds
137
+ build_acl_test_state
138
+
139
+ @connection.stubs(:storage_request => stub(:code => '204'))
140
+
141
+ assert_equal @container.set_read_acl('.r:*'), true
142
+ end
143
+
144
+ def test_write_acl_is_set_from_headers
145
+ build_acl_test_state :response => { 'x-container-write' => '.r:*' }, :code => '204'
146
+ assert_equal @response["x-container-write"], @container.write_acl
147
+ end
148
+
149
+ def test_set_write_acl_fails
150
+ build_acl_test_state
151
+
152
+ response = stub(:code => '999')
153
+
154
+ @connection.stubs(:storage_request => response)
155
+
156
+ assert_raises(CloudFiles::Exception::NoSuchContainer) do
157
+ @container.set_write_acl('.r:*')
158
+ end
159
+ end
160
+
161
+ def test_set_write_acl_succeeds
162
+ build_acl_test_state
163
+
164
+ @connection.stubs(:storage_request => stub(:code => '204'))
165
+
166
+ assert_equal @container.set_write_acl('.r:*'), true
167
+ end
168
+
104
169
  def test_log_retention_is_true
105
170
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
106
171
  response = {'x-container-bytes-used' => '0', 'x-container-object-count' => '0', 'x-cdn-enabled' => 'True', 'x-log-retention' => 'True'}
107
172
  response.stubs(:code).returns('204')
108
- connection.stubs(:cfreq => response)
173
+ connection.stubs(:cdn_request => response)
174
+ connection.stubs(:storage_request => response)
109
175
  @container = CloudFiles::Container.new(connection, 'test_container')
110
176
  assert_equal @container.log_retention?, true
111
177
  end
@@ -247,12 +313,12 @@ class CloudfilesContainerTest < Test::Unit::TestCase
247
313
  end
248
314
 
249
315
  def test_setting_log_retention
250
- build_net_http_object(:code => '201')
316
+ build_net_http_object(:code => '201', :cdn_request => true)
251
317
  assert(@container.log_retention='false')
252
318
  end
253
319
 
254
320
  def test_purge_from_cdn_succeeds
255
- build_net_http_object
321
+ build_net_http_object(:cdn_request => true)
256
322
  assert_nothing_raised do
257
323
  @container.purge_from_cdn
258
324
  @container.purge_from_cdn("small.fox@hole.org")
@@ -261,7 +327,8 @@ class CloudfilesContainerTest < Test::Unit::TestCase
261
327
 
262
328
  private
263
329
 
264
- def build_net_http_object(args={:code => '204' }, cfreq_expectations={})
330
+ def build_net_http_object(args={}, cfreq_expectations={})
331
+ args.merge!(:code => '204') unless args[:code]
265
332
  CloudFiles::Container.any_instance.stubs(:populate).returns(true)
266
333
  CloudFiles::Container.any_instance.stubs(:metadata).returns()
267
334
  CloudFiles::Container.any_instance.stubs(:container_metadata).returns({:bytes => 99, :count => 2})
@@ -274,20 +341,22 @@ class CloudfilesContainerTest < Test::Unit::TestCase
274
341
  if !cfreq_expectations.empty?
275
342
  #cfreq(method,server,path,port,scheme,headers = {},data = nil,attempts = 0,&block)
276
343
 
277
- parameter_expectations = [anything(), anything(), anything(), anything(), anything(), anything(), anything(), anything()]
344
+ parameter_expectations = [anything(), anything(), anything(), anything(), anything(), anything()]
278
345
  parameter_expectations[0] = cfreq_expectations[:method] if cfreq_expectations[:method]
279
- parameter_expectations[2] = cfreq_expectations[:path] if cfreq_expectations[:path]
346
+ parameter_expectations[1] = cfreq_expectations[:path] if cfreq_expectations[:path]
280
347
 
281
- connection.expects(:cfreq).with(*parameter_expectations).returns(response)
348
+ connection.expects(:cdn_request).with(*parameter_expectations).returns(response) if args[:cdn_request]
349
+ connection.expects(:storage_request).with(*parameter_expectations).returns(response)
282
350
  else
283
- connection.stubs(:cfreq => response)
351
+ connection.stubs(:cdn_request => response) if args[:cdn_request]
352
+ connection.stubs(:storage_request => response)
284
353
  end
285
354
 
286
355
  @container = CloudFiles::Container.new(connection, 'test_container')
287
356
  @container.stubs(:connection).returns(connection)
288
357
  end
289
358
 
290
- def build_net_http_object_with_cfreq_expectations(args={:code => '204'}, cfreq_expectations={})
359
+ def build_net_http_object_with_cfreq_expectations(args={}, cfreq_expectations={})
291
360
  build_net_http_object(args, cfreq_expectations)
292
361
  end
293
362
  end
@@ -7,7 +7,7 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
7
7
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
8
8
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5', 'last-modified' => Time.now.to_s}
9
9
  response.stubs(:code).returns('204')
10
- connection.stubs(:cfreq => response)
10
+ connection.stubs(:storage_request => response)
11
11
  container = CloudFiles::Container.new(connection, 'test_container')
12
12
  @object = CloudFiles::StorageObject.new(container, 'test_object')
13
13
  assert_equal @object.name, 'test_object'
@@ -19,7 +19,7 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
19
19
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => false)
20
20
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5', 'last-modified' => Time.now.to_s}
21
21
  response.stubs(:code).returns('204')
22
- connection.stubs(:cfreq => response)
22
+ connection.stubs(:storage_request => response)
23
23
  container = CloudFiles::Container.new(connection, 'test_container')
24
24
  @object = CloudFiles::StorageObject.new(container, 'test_object')
25
25
  assert_equal @object.name, 'test_object'
@@ -28,7 +28,7 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
28
28
  end
29
29
 
30
30
  def test_public_url_exists
31
- build_net_http_object(:public => true, :name => 'test object')
31
+ build_net_http_object(:public => true, :name => 'test object', :cdn_request => true)
32
32
  assert_equal @object.public_url, "http://cdn.test.example/test%20object"
33
33
  end
34
34
 
@@ -109,12 +109,12 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
109
109
 
110
110
  def test_read_metadata_succeeds
111
111
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
112
- response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5', 'x-object-meta-foo' => 'Bar', 'last-modified' => Time.now.to_s}
112
+ response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5', 'x-object-meta-foo' => 'Bar', 'x-object-meta-spam' => ['peanut', 'butter'], 'last-modified' => Time.now.to_s}
113
113
  response.stubs(:code).returns('204')
114
- connection.stubs(:cfreq => response)
114
+ connection.stubs(:storage_request => response)
115
115
  container = CloudFiles::Container.new(connection, 'test_container')
116
116
  @object = CloudFiles::StorageObject.new(container, 'test_object')
117
- assert_equal @object.metadata, {'foo' => 'Bar'}
117
+ assert_equal @object.metadata, {'foo' => 'Bar', 'spam' => 'peanutbutter'}
118
118
  end
119
119
 
120
120
  def test_write_succeeds
@@ -130,7 +130,7 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
130
130
  connection = stub(:storagehost => 'test.storage.example', :storagepath => '/dummy/path', :storageport => 443, :storagescheme => 'https', :cdnmgmthost => 'cdm.test.example', :cdnmgmtpath => '/dummy/path', :cdnmgmtport => 443, :cdnmgmtscheme => 'https', :cdn_available? => true)
131
131
  response = {'x-container-bytes-used' => '42', 'x-container-object-count' => '5', 'last-modified' => Time.now.to_s}
132
132
  response.stubs(:code).returns('204').then.returns('204').then.returns('201').then.returns('204')
133
- connection.stubs(:cfreq => response)
133
+ connection.stubs(:storage_request => response)
134
134
  CloudFiles::Container.any_instance.stubs(:populate).returns(true)
135
135
  container = CloudFiles::Container.new(connection, 'test_container')
136
136
  @object = CloudFiles::StorageObject.new(container, 'path/to/my/test_object', false, true)
@@ -162,7 +162,7 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
162
162
  end
163
163
 
164
164
  def test_purge_from_cdn_succeeds
165
- build_net_http_object
165
+ build_net_http_object(:cdn_request => true)
166
166
  assert_nothing_raised do
167
167
  @object.purge_from_cdn
168
168
  @object.purge_from_cdn("small.fox@hole.org")
@@ -198,8 +198,9 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
198
198
  end
199
199
 
200
200
  private
201
-
202
- def build_net_http_object(args={:code => '204' })
201
+
202
+ def build_net_http_object(args={})
203
+ args.merge!(:code => '204' ) unless args[:code]
203
204
  CloudFiles::Container.any_instance.stubs(:metadata).returns({})
204
205
  CloudFiles::Container.any_instance.stubs(:populate).returns(true)
205
206
  CloudFiles::Container.any_instance.stubs(:container_metadata).returns({:bytes => 99, :count => 2})
@@ -208,7 +209,11 @@ class CloudfilesStorageObjectTest < Test::Unit::TestCase
208
209
  response = {'x-cdn-management-url' => 'http://cdn.example.com/path', 'x-storage-url' => 'http://cdn.example.com/storage', 'authtoken' => 'dummy_token', 'last-modified' => Time.now.to_s}.merge(args[:response])
209
210
  response.stubs(:code).returns(args[:code])
210
211
  response.stubs(:body).returns args[:body] || nil
211
- connection.stubs(:cfreq => response)
212
+
213
+ connection.stubs(:cdn_request => response) if args[:cdn_request]
214
+
215
+ connection.stubs(:storage_request => response)
216
+
212
217
  container = CloudFiles::Container.new(connection, 'test_container')
213
218
  container.stubs(:connection).returns(connection)
214
219
  container.stubs(:public?).returns(args[:public] || false)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudfiles
3
3
  version: !ruby/object:Gem::Version
4
- hash: 37
4
+ hash: 35
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 4
9
- - 17
10
- version: 1.4.17
9
+ - 18
10
+ version: 1.4.18
11
11
  platform: ruby
12
12
  authors:
13
13
  - H. Wade Minter
@@ -16,7 +16,8 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-05-27 00:00:00 Z
19
+ date: 2011-09-28 00:00:00 -04:00
20
+ default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
22
23
  name: mime-types
@@ -81,6 +82,7 @@ files:
81
82
  - test/cloudfiles_container_test.rb
82
83
  - test/cloudfiles_storage_object_test.rb
83
84
  - test/test_helper.rb
85
+ has_rdoc: true
84
86
  homepage: http://www.rackspacecloud.com/cloud_hosting_products/files
85
87
  licenses: []
86
88
 
@@ -110,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
112
  requirements: []
111
113
 
112
114
  rubyforge_project:
113
- rubygems_version: 1.7.2
115
+ rubygems_version: 1.6.0
114
116
  signing_key:
115
117
  specification_version: 3
116
118
  summary: A Ruby API into Rackspace Cloud Files