cloudfiles 1.4.17 → 1.4.18

Sign up to get free protection for your applications and to get access to all the features.
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