s33r 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/cli/simple.rb +17 -0
- data/examples/fores33r/app/controllers/browser_controller.rb +32 -8
- data/examples/fores33r/app/views/browser/_create_bucket.rhtml +6 -0
- data/examples/fores33r/app/views/browser/_upload.rhtml +5 -0
- data/examples/fores33r/app/views/browser/index.rhtml +1 -8
- data/examples/fores33r/app/views/browser/plain_bucket.rhtml +3 -0
- data/examples/fores33r/app/views/browser/s3_index.rhtml +9 -0
- data/examples/fores33r/app/views/browser/show_bucket.rhtml +6 -8
- data/examples/fores33r/app/views/layouts/application.rhtml +2 -0
- data/examples/fores33r/app/views/layouts/s3_layout.rhtml +14 -0
- data/examples/fores33r/config/environment.rb +1 -1
- data/examples/fores33r/config/routes.rb +2 -0
- data/examples/fores33r/public/stylesheets/core.css +2 -2
- data/html/classes/Net/HTTPResponse.html +3 -0
- data/html/classes/S33r.html +127 -116
- data/html/classes/S33r/BucketListing.html +119 -94
- data/html/classes/S33r/Client.html +602 -536
- data/html/classes/S33r/LoggingResource.html +3 -3
- data/html/classes/S33r/NamedBucket.html +235 -191
- data/html/classes/S33r/OrderlyXmlMarkup.html +7 -7
- data/html/classes/S33r/S33rException.html +1 -0
- data/html/classes/S33r/S33rException/TryingToPutEmptyResource.html +117 -0
- data/html/classes/S33r/S3ACL/ACLDoc.html +94 -94
- data/html/classes/S33r/S3ACL/AmazonCustomer.html +5 -5
- data/html/classes/S33r/S3ACL/CanonicalUser.html +12 -12
- data/html/classes/S33r/S3ACL/Grant.html +64 -64
- data/html/classes/S33r/S3ACL/Grantee.html +36 -36
- data/html/classes/S33r/S3ACL/Group.html +8 -8
- data/html/classes/S33r/S3Object.html +387 -79
- data/html/classes/XML.html +15 -15
- data/html/created.rid +1 -1
- data/html/files/CHANGELOG.html +7 -1
- data/html/files/MIT-LICENSE.html +1 -1
- data/html/files/README_txt.html +3 -7
- data/html/files/lib/s33r/bucket_listing_rb.html +1 -9
- data/html/files/lib/s33r/builder_rb.html +1 -1
- data/html/files/lib/s33r/client_rb.html +1 -1
- data/html/files/lib/s33r/core_rb.html +1 -2
- data/html/files/lib/s33r/libxml_extensions_rb.html +1 -7
- data/html/files/lib/s33r/libxml_loader_rb.html +109 -0
- data/html/files/lib/s33r/logging_rb.html +1 -2
- data/html/files/lib/s33r/mimetypes_rb.html +1 -1
- data/html/files/lib/s33r/named_bucket_rb.html +1 -1
- data/html/files/lib/s33r/s33r_exception_rb.html +1 -1
- data/html/files/lib/s33r/s33r_http_rb.html +1 -1
- data/html/files/lib/s33r/s3_acl_rb.html +1 -2
- data/html/files/lib/s33r/s3_obj_rb.html +108 -0
- data/html/files/lib/s33r/sync_rb.html +1 -1
- data/html/files/lib/s33r_rb.html +1 -1
- data/html/fr_class_index.html +1 -0
- data/html/fr_file_index.html +2 -0
- data/html/fr_method_index.html +80 -71
- data/lib/s33r/bucket_listing.rb +30 -55
- data/lib/s33r/client.rb +70 -28
- data/lib/s33r/core.rb +9 -4
- data/lib/s33r/libxml_extensions.rb +2 -0
- data/lib/s33r/libxml_loader.rb +6 -0
- data/lib/s33r/logging.rb +3 -3
- data/lib/s33r/named_bucket.rb +33 -15
- data/lib/s33r/s33r_exception.rb +4 -0
- data/lib/s33r/s33r_http.rb +1 -1
- data/lib/s33r/s3_acl.rb +3 -2
- data/lib/s33r/s3_obj.rb +186 -0
- data/test/cases/spec_bucket_listing.rb +9 -33
- data/test/cases/spec_s3_object.rb +35 -0
- data/test/files/suspect_bucket_listing.xml +19 -0
- metadata +94 -89
- data/examples/fores33r/log/development.log +0 -5960
- data/examples/fores33r/log/production.log +0 -0
- data/examples/fores33r/log/server.log +0 -0
- data/examples/fores33r/log/test.log +0 -0
- data/examples/fores33r/tmp/sessions/ruby_sess.2ea325f604aa5fb9 +0 -0
- data/examples/fores33r/tmp/sessions/ruby_sess.39d37e054d21d545 +0 -0
- data/examples/fores33r/tmp/sessions/ruby_sess.acf71fc73aa74983 +0 -0
- data/examples/fores33r/tmp/sessions/ruby_sess.c1697b7d6670f3cd +0 -0
data/lib/s33r/core.rb
CHANGED
@@ -3,8 +3,8 @@ require 'time'
|
|
3
3
|
require 'net/http'
|
4
4
|
require 'net/https'
|
5
5
|
require 'openssl'
|
6
|
-
require 'xml/libxml'
|
7
6
|
require 'parsedate'
|
7
|
+
require File.join(File.dirname(__FILE__), 'libxml_loader')
|
8
8
|
|
9
9
|
# Module to handle S3 operations which don't require an internet connection,
|
10
10
|
# i.e. data validation and request-building operations;
|
@@ -157,12 +157,17 @@ module S33r
|
|
157
157
|
headers
|
158
158
|
end
|
159
159
|
|
160
|
-
# Add metadata headers, correctly prefixing them first
|
160
|
+
# Add metadata headers, correctly prefixing them first,
|
161
|
+
# e.g. you might do metadata_headers({}, {'myname' => 'elliot', 'myage' => 36})
|
162
|
+
# to add two headers to the request:
|
163
|
+
#
|
164
|
+
# x-amz-meta-myname: elliot
|
165
|
+
# x-amz-meta-myage: 36
|
161
166
|
#
|
162
167
|
# Returns headers with the metadata headers appended.
|
163
168
|
def metadata_headers(headers, metadata={})
|
164
169
|
unless metadata.empty?
|
165
|
-
metadata.each { |key, value| headers[METADATA_PREFIX + key] = value }
|
170
|
+
metadata.each { |key, value| headers[METADATA_PREFIX + key] = value.to_s }
|
166
171
|
end
|
167
172
|
headers
|
168
173
|
end
|
@@ -285,7 +290,7 @@ module S33r
|
|
285
290
|
# Remove the namespace declaration from S3 XML response bodies (libxml
|
286
291
|
# isn't fond of it).
|
287
292
|
def S33r.remove_namespace(xml_in)
|
288
|
-
namespace = RESPONSE_NAMESPACE_URI.gsub('/', '\/')
|
293
|
+
namespace = S33r::RESPONSE_NAMESPACE_URI.gsub('/', '\/')
|
289
294
|
xml_in.gsub(/ xmlns="#{namespace}"/, '')
|
290
295
|
end
|
291
296
|
end
|
data/lib/s33r/logging.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'xml/libxml'
|
3
2
|
require_gem 'builder'
|
3
|
+
require File.join(File.dirname(__FILE__), 'libxml_loader')
|
4
4
|
|
5
5
|
module S33r
|
6
6
|
# For manipulating logging directives on resources
|
7
7
|
# (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/LoggingHowTo.html).
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# Creating a LoggingResource instance using new and no arguments will generate a "blank" instance;
|
10
|
+
# this can be put to the ?logging URL for a resource to remove logging from it.
|
11
11
|
class LoggingResource
|
12
12
|
attr_reader :log_target, :log_prefix
|
13
13
|
|
data/lib/s33r/named_bucket.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
#-- specify a prefix and/or delimiter
|
3
3
|
|
4
4
|
require File.join(File.dirname(__FILE__), 'client')
|
5
|
+
require File.join(File.dirname(__FILE__), 's3_obj')
|
6
|
+
require File.join(File.dirname(__FILE__), 's33r_exception')
|
5
7
|
|
6
8
|
module S33r
|
7
9
|
# Wraps the S33r::Client class to make it more convenient for use with a single bucket.
|
@@ -33,10 +35,7 @@ module S33r
|
|
33
35
|
a :default_bucket option"
|
34
36
|
end
|
35
37
|
|
36
|
-
#
|
37
|
-
@listing = nil
|
38
|
-
|
39
|
-
# all content should be created as public-read
|
38
|
+
# all content inside the bucket should be created as public-read
|
40
39
|
@public_contents = (true == options[:public_contents])
|
41
40
|
@client_headers.merge!(canned_acl_header('public-read')) if @public_contents
|
42
41
|
|
@@ -58,14 +57,31 @@ module S33r
|
|
58
57
|
@strict
|
59
58
|
end
|
60
59
|
|
61
|
-
# Get a single object from a bucket as
|
62
|
-
|
63
|
-
|
60
|
+
# Get a single object from a bucket as an S3Object.
|
61
|
+
#
|
62
|
+
# To get a bare object (with no content):
|
63
|
+
#
|
64
|
+
# bucket['key']
|
65
|
+
#
|
66
|
+
# To get the object and load its content:
|
67
|
+
#
|
68
|
+
# bucket['key', :load]
|
69
|
+
def [](key, eager=false)
|
70
|
+
obj = listing.contents[key]
|
71
|
+
obj.named_bucket = self
|
72
|
+
obj.load if :load == eager
|
73
|
+
obj
|
74
|
+
end
|
75
|
+
|
76
|
+
# Get a raw response for a key inside the bucket.
|
77
|
+
def get_raw(key, headers={})
|
78
|
+
get_resource(@name, key, headers)
|
64
79
|
end
|
65
80
|
|
66
81
|
# Get a BucketListing instance for the content of this bucket.
|
82
|
+
# Uses the Client.list_bucket method to get the listing.
|
67
83
|
def listing
|
68
|
-
list_bucket(@name)
|
84
|
+
list_bucket(@name)[1]
|
69
85
|
end
|
70
86
|
|
71
87
|
# Does this bucket exist?
|
@@ -84,9 +100,12 @@ module S33r
|
|
84
100
|
listing.pretty
|
85
101
|
end
|
86
102
|
|
87
|
-
# List content of the bucket, and attach each item to this
|
88
|
-
|
89
|
-
|
103
|
+
# List content of the bucket, and attach each item to this NamedBucket
|
104
|
+
# instance as it is yielded (to enable easier manipulation directly from the S3Object).
|
105
|
+
# Note that the objects are incomplete, as the data associated with them has not been
|
106
|
+
# "got" from S3 yet.
|
107
|
+
def each_object
|
108
|
+
listing.contents.each_value { |obj| obj.named_bucket = self; yield obj }
|
90
109
|
end
|
91
110
|
|
92
111
|
# Does the given key exist in the bucket?
|
@@ -114,9 +133,8 @@ module S33r
|
|
114
133
|
# NB S3 doesn't discriminate between successfully deleting a key
|
115
134
|
# and trying to delete a non-existent key (both return a 204).
|
116
135
|
# If you want to test for existence first, use key_exists?.
|
117
|
-
def
|
118
|
-
|
119
|
-
listing
|
136
|
+
def delete(key, headers={})
|
137
|
+
delete_resource(@name, key, headers)
|
120
138
|
end
|
121
139
|
|
122
140
|
# Generate an authenticated URL (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/)
|
@@ -127,4 +145,4 @@ module S33r
|
|
127
145
|
super(@aws_access_key, @aws_secret_access_key, @name, resource_key, expires)
|
128
146
|
end
|
129
147
|
end
|
130
|
-
end
|
148
|
+
end
|
data/lib/s33r/s33r_exception.rb
CHANGED
@@ -41,6 +41,10 @@ module S33r
|
|
41
41
|
# (i.e. if LogDelivery permissions not set - see S33r::S3ACL::ACLDoc.add_log_target_grants)
|
42
42
|
class BucketNotLogTargetable < Exception
|
43
43
|
end
|
44
|
+
|
45
|
+
# Raised if you try to do a put with empty body
|
46
|
+
class TryingToPutEmptyResource < Exception
|
47
|
+
end
|
44
48
|
|
45
49
|
end
|
46
50
|
end
|
data/lib/s33r/s33r_http.rb
CHANGED
data/lib/s33r/s3_acl.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'xml/libxml'
|
3
2
|
require_gem 'builder'
|
4
|
-
|
3
|
+
base = File.dirname(__FILE__)
|
4
|
+
require File.join(base, 'libxml_loader')
|
5
|
+
require File.join(base, 's33r_exception')
|
5
6
|
|
6
7
|
module S33r
|
7
8
|
# S3 ACL handling.
|
data/lib/s33r/s3_obj.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
# Representation of an object stored in a bucket.
|
4
|
+
module S33r
|
5
|
+
class S3Object
|
6
|
+
attr_accessor :key, :last_modified, :etag, :size, :owner, :storage_class, :value, :named_bucket,
|
7
|
+
:content_type, :mime_type
|
8
|
+
|
9
|
+
# Metadata set by x-amz-meta- style headers. Note that the bit after x-amz-meta-
|
10
|
+
# is stored for each key, rather than the full key.
|
11
|
+
attr_accessor :meta
|
12
|
+
|
13
|
+
def initialize(key, metadata={}, amz_meta={}, value=nil)
|
14
|
+
@key = key
|
15
|
+
@meta = amz_meta
|
16
|
+
@value = value
|
17
|
+
set_properties(metadata) unless metadata.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set the properties of the object from some metadata name-value pairs.
|
21
|
+
#
|
22
|
+
# +metadata+ is a hash of properties and their values, used to set the
|
23
|
+
# corresponding properties on the object.
|
24
|
+
#
|
25
|
+
# +value+ is the data associated with the object on S3.
|
26
|
+
def set_properties(metadata)
|
27
|
+
# required properties
|
28
|
+
@etag = metadata[:etag].gsub("\"", "") if metadata[:etag]
|
29
|
+
@last_modified = DateTime.parse(metadata[:last_modified]) if metadata[:last_modified]
|
30
|
+
@size = metadata[:size].to_i if metadata[:size]
|
31
|
+
|
32
|
+
# only set if creating object from XML (not available otherwise)
|
33
|
+
@owner = metadata[:owner]
|
34
|
+
|
35
|
+
# only set if creating object from HTTP response
|
36
|
+
@content_type = metadata[:content_type]
|
37
|
+
end
|
38
|
+
|
39
|
+
# To create an object which reads the content in from a file;
|
40
|
+
# this is not very efficient - it's actually better to use NamedBucket.put_file,
|
41
|
+
# as this will stream out of a file to S3, rather than load the file in
|
42
|
+
# memory first.
|
43
|
+
def self.from_file(key, filename)
|
44
|
+
mime_type = guess_mime_type(filename)
|
45
|
+
content_type = mime_type.simplified
|
46
|
+
value = File.open(filename).read
|
47
|
+
self.new(key, { :content_type => content_type }, {}, value)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Set properties of the object from an XML string.
|
51
|
+
#
|
52
|
+
# +xml_str+ should be a string representing a full XML document,
|
53
|
+
# containing a <Contents> element as its root element.
|
54
|
+
def self.from_xml_string(xml_str)
|
55
|
+
self.from_xml_node(XML.get_xml_doc(xml_str))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create a new instance from an XML document.
|
59
|
+
def self.from_xml_node(doc)
|
60
|
+
metadata = self.parse_xml_node(doc)
|
61
|
+
self.new(metadata[:key], metadata)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get properties of the object from an XML document, e.g. as returned in a bucket listing.
|
65
|
+
#
|
66
|
+
# +doc+: XML::Document instance to parse to get properties for this object.
|
67
|
+
#
|
68
|
+
# Returns the metadata relating to the object, as stored on S3.
|
69
|
+
#-- TODO: include amz-meta elements
|
70
|
+
def self.parse_xml_node(doc)
|
71
|
+
metadata = {}
|
72
|
+
metadata[:key] = doc.xget('Key')
|
73
|
+
metadata[:last_modified] = doc.xget('LastModified')
|
74
|
+
metadata[:etag] = doc.xget('ETag')
|
75
|
+
metadata[:size] = doc.xget('Size')
|
76
|
+
|
77
|
+
# Build representation of the owner.
|
78
|
+
user_xml_doc = doc.find('Owner').to_a.first
|
79
|
+
metadata[:owner] = S3ACL::CanonicalUser.from_xml(user_xml_doc)
|
80
|
+
|
81
|
+
metadata
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create a new instance from a HTTP response.
|
85
|
+
# This is useful if you do a GET for a resource key and
|
86
|
+
# want to convert the response into an object; NB the response
|
87
|
+
# doesn't necessarily contain all the metadata you might want - you need to
|
88
|
+
# do a HEAD for that.
|
89
|
+
#
|
90
|
+
# +key+ is the key for the resource (not part of the response).
|
91
|
+
#
|
92
|
+
# Note that if the resp returns nil, a blank object is created.
|
93
|
+
def self.from_response(key, resp)
|
94
|
+
result = self.parse_response(resp)
|
95
|
+
if result
|
96
|
+
metadata, amz_meta, value = result
|
97
|
+
else
|
98
|
+
metadata = {}
|
99
|
+
amz_meta = {}
|
100
|
+
value = nil
|
101
|
+
end
|
102
|
+
self.new(key, metadata, amz_meta, value)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Parse the response returned by GET on a resource key
|
106
|
+
# within a bucket.
|
107
|
+
#
|
108
|
+
# +resp+ is a Net::HTTPResponse instance.
|
109
|
+
#
|
110
|
+
# Returns an array [+metadata+, +response.body+]; or nil if the object
|
111
|
+
# doesn't exist.
|
112
|
+
def self.parse_response(resp)
|
113
|
+
resp_headers = resp.to_hash
|
114
|
+
|
115
|
+
# If there's no etag, there's no content in the resource.
|
116
|
+
if resp_headers['etag']
|
117
|
+
metadata = {}
|
118
|
+
metadata[:last_modified] = resp_headers['last-modified'][0]
|
119
|
+
metadata[:etag] = resp_headers['etag'][0]
|
120
|
+
metadata[:size] = resp_headers['content-length'][0]
|
121
|
+
metadata[:content_type] = resp_headers['content-type'][0]
|
122
|
+
|
123
|
+
# x-amz-meta- response headers.
|
124
|
+
interesting_header = Regexp.new(METADATA_PREFIX)
|
125
|
+
amz_meta = {}
|
126
|
+
resp.each_header do |key, value|
|
127
|
+
amz_meta[key.gsub(interesting_header, '')] = value if interesting_header =~ key
|
128
|
+
end
|
129
|
+
|
130
|
+
# The actual content of the S3 object.
|
131
|
+
value = resp.body
|
132
|
+
|
133
|
+
[metadata, amz_meta, value]
|
134
|
+
else
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Load content into this object from S3; will perform
|
140
|
+
# an HTTP request to "refresh" the object (providing the object
|
141
|
+
# has an association with a bucket it can use for piggybacking).
|
142
|
+
#
|
143
|
+
# Returns false if value cannot be retrieved.
|
144
|
+
def load
|
145
|
+
if @named_bucket and @named_bucket.key_exists?(@key)
|
146
|
+
resp = @named_bucket.get_raw(@key)
|
147
|
+
if resp.ok?
|
148
|
+
@value = resp.body
|
149
|
+
@content_type = resp.to_hash['content-type']
|
150
|
+
return true
|
151
|
+
else
|
152
|
+
return false
|
153
|
+
end
|
154
|
+
else
|
155
|
+
return false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Remove this object from its associated NamedBucket.
|
160
|
+
#
|
161
|
+
# Returns false if this object is not associated with a bucket;
|
162
|
+
# otherwise returns the response from S3.
|
163
|
+
def delete
|
164
|
+
if @named_bucket.nil?
|
165
|
+
return false
|
166
|
+
else
|
167
|
+
@named_bucket.delete_resource(@key)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Save this object back into its bucket.
|
172
|
+
#
|
173
|
+
# Only works if the object has an associated NamedBucket;
|
174
|
+
# returns false if it doesn't.
|
175
|
+
def save
|
176
|
+
if @named_bucket.nil?
|
177
|
+
return false
|
178
|
+
else
|
179
|
+
headers = {}
|
180
|
+
headers["Content-Type"] = @content_type || ''
|
181
|
+
headers = metadata_headers(headers, meta)
|
182
|
+
@named_bucket.put_stream(@value, @key, headers)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -12,6 +12,8 @@ context 'S33r bucket listing' do
|
|
12
12
|
@with_empty_bucket_listing_xml = File.open(xml_file3) { |f| f.read }
|
13
13
|
xml_file4 = File.join(base, '../files/bucket_listing_broken.xml')
|
14
14
|
@with_broken_bucket_listing_xml = File.open(xml_file4) { |f| f.read }
|
15
|
+
xml_file5 = File.join(base, '../files/suspect_bucket_listing.xml')
|
16
|
+
@with_suspect_bl_xml = File.open(xml_file5) { |f| f.read }
|
15
17
|
|
16
18
|
@bucket_listing = BucketListing.new(@with_bucket_listing_xml)
|
17
19
|
@bucket_properties = %w(name prefix marker max_keys is_truncated)
|
@@ -85,38 +87,12 @@ context 'S33r bucket listing' do
|
|
85
87
|
specify 'should provide easy access to <CommonPrefixes> elements as a hash' do
|
86
88
|
todo
|
87
89
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
specify 'can be initialised from XML fragment with correct data types' do
|
98
|
-
@s3obj.key.should.equal '/home/ell/dir1/four.txt'
|
99
|
-
d = @s3obj.last_modified
|
100
|
-
[d.year, d.month, d.day, d.hour, d.min, d.sec].should.equal [2006, 8, 19, 22, 53, 29]
|
101
|
-
@s3obj.etag.should.equal '24ce59274b89287b3960c184153ac24b'
|
102
|
-
@s3obj.size.should.equal 14
|
103
|
-
end
|
104
|
-
|
105
|
-
specify 'should treat the owner as an object in his/her own right' do
|
106
|
-
[@s3obj.owner.user_id, @s3obj.owner.display_name].should.equal \
|
107
|
-
['56efddfead5aa65da942f156fb2b294f44d78fd932d701331edc5fba19620fd4', 'elliotsmith3']
|
108
|
-
@s3obj.owner.should_be_instance_of S3ACL::CanonicalUser
|
109
|
-
end
|
110
|
-
|
111
|
-
specify 'can be associated with a NamedBucket' do
|
112
|
-
todo
|
113
|
-
end
|
114
|
-
|
115
|
-
specify 'can be saved by proxing through the NamedBucket it is associated with' do
|
116
|
-
todo
|
117
|
-
end
|
118
|
-
|
119
|
-
specify 'cannot be saved unless associated with a NamedBucket' do
|
120
|
-
todo
|
90
|
+
|
91
|
+
# attempting to fix ListBucketResult errors reported by Alex Payne
|
92
|
+
specify 'should handle suspect bucket listing' do
|
93
|
+
puts @with_suspect_bl_xml
|
94
|
+
bl = BucketListing.new(@with_suspect_bl_xml)
|
95
|
+
bl.contents.size.should_be 1
|
96
|
+
bl.contents.keys.should.include 'orly.jpg'
|
121
97
|
end
|
122
98
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
base = File.dirname(__FILE__)
|
2
|
+
require base + '/../test_setup'
|
3
|
+
|
4
|
+
context 'S3 object' do
|
5
|
+
setup do
|
6
|
+
@s3_object_xml = File.open(File.join(base, '../files/s3_object.xml')).read
|
7
|
+
@s3obj = S3Object.from_xml_string(@s3_object_xml)
|
8
|
+
end
|
9
|
+
|
10
|
+
specify 'can be initialised from XML fragment with correct data types' do
|
11
|
+
@s3obj.key.should.equal '/home/ell/dir1/four.txt'
|
12
|
+
d = @s3obj.last_modified
|
13
|
+
[d.year, d.month, d.day, d.hour, d.min, d.sec].should.equal [2006, 8, 19, 22, 53, 29]
|
14
|
+
@s3obj.etag.should.equal '24ce59274b89287b3960c184153ac24b'
|
15
|
+
@s3obj.size.should.equal 14
|
16
|
+
end
|
17
|
+
|
18
|
+
specify 'should treat the owner as an object in his/her own right' do
|
19
|
+
[@s3obj.owner.user_id, @s3obj.owner.display_name].should.equal \
|
20
|
+
['56efddfead5aa65da942f156fb2b294f44d78fd932d701331edc5fba19620fd4', 'elliotsmith3']
|
21
|
+
@s3obj.owner.should_be_instance_of S3ACL::CanonicalUser
|
22
|
+
end
|
23
|
+
|
24
|
+
specify 'can be associated with a NamedBucket' do
|
25
|
+
todo
|
26
|
+
end
|
27
|
+
|
28
|
+
specify 'can be saved by proxing through the NamedBucket it is associated with' do
|
29
|
+
todo
|
30
|
+
end
|
31
|
+
|
32
|
+
specify 'cannot be saved unless associated with a NamedBucket' do
|
33
|
+
todo
|
34
|
+
end
|
35
|
+
end
|