s4 0.0.3 → 0.0.4

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.
Files changed (6) hide show
  1. data/CHANGELOG +3 -0
  2. data/README.md +37 -24
  3. data/lib/s4.rb +75 -71
  4. data/s4.gemspec +1 -0
  5. data/test/s4_test.rb +48 -37
  6. metadata +46 -51
@@ -0,0 +1,3 @@
1
+ 0.0.4 - 2011-09-20
2
+
3
+ * S4#put now returns the URL to the uploaded file.
data/README.md CHANGED
@@ -10,45 +10,58 @@ the basics (managing files in a bucket) in a very simple way with a
10
10
  Usage
11
11
  -----
12
12
 
13
- $assets = S4.connect("s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/assets.mysite.com")
14
-
15
- $assets.upload("puppy.jpg", "animals/puppy.jpg")
16
- $assets.upload("penguin.jpg", "animals/penguin.jpg")
17
- $assets.list("animals/") #=> [ "animals/puppy.jpg", "animals/penguin.jpg" ]
18
-
19
- $assets.download("animals/penguin.jpg", "penguin.jpg")
20
-
21
- $assets.delete("animals/penguin.jpg")
22
- $assets.list("animals/") #=> [ "animals/puppy.jpg" ]
23
-
24
- $assets.upload("ufo.jpg")
13
+ $assets = S4.connect url: "s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/assets.mysite.com"
14
+
15
+ $assets.upload "puppy.jpg", "animals/puppy.jpg"
16
+ $assets.upload "penguin.jpg", "animals/penguin.jpg"
17
+ $assets.list "animals/" #=> [ "animals/puppy.jpg", "animals/penguin.jpg" ]
18
+
19
+ $assets.download "animals/penguin.jpg", "penguin.jpg"
20
+
21
+ $assets.delete "animals/penguin.jpg"
22
+ $assets.list "animals/" #=> [ "animals/puppy.jpg" ]
23
+
24
+ $assets.upload "ufo.jpg"
25
25
  $assets.list #=> [ "ufo.jpg", "animals/puppy.jpg" ]
26
26
 
27
+ Without a URL given, S4 will attempt to read one from ENV["S3_URL"]:
28
+
29
+ $ export S3_URL="s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/assets.mysite.com"
30
+ ...
31
+ $assets = S4.connect
32
+
33
+ Handy snippet for multiple buckets w/ the same account:
34
+
35
+ $ export S3_URL="s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/%s"
36
+ ...
37
+ $assets = S4.connect url: ENV["S3_URL"] % "assets"
38
+ $videos = S4.connect url: ENV["S3_URL"] % "videos"
39
+
27
40
  Low-level access:
28
-
29
- $assets.get("animals/gigantic_penguin_movie.mp4") do |response|
30
- File.open("gigantic_penguin_movie.mp4", "wb") do |io|
41
+
42
+ $assets.get "animals/gigantic_penguin_movie.mp4" do |response|
43
+ File.open "gigantic_penguin_movie.mp4", "wb" do |io|
31
44
  response.read_body do |chunk|
32
- io.write(chunk)
45
+ io.write chunk
33
46
  puts "."
34
47
  end
35
48
  end
36
49
  end
37
-
38
- $assets.put(StringIO.new("My Novel -- By Ben Alavi...", "r"), "novel.txt", "text/plain")
50
+
51
+ $assets.put StringIO.new("My Novel -- By Ben Alavi...", "r"), "novel.txt", "text/plain"
39
52
 
40
53
  Create a bucket (returns the bucket if it already exists and is accessible):
41
54
 
42
- $musics = S4.create("s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/musics.mysite.com")
43
-
55
+ $musics = S4.create url: "s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/musics.mysite.com"
56
+
44
57
  Make a bucket into a static website:
45
58
 
46
- $site = S4.connect("s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/website.mysite.com")
59
+ $site = S4.connect url: "s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/website.mysite.com"
47
60
  $site.website!
48
- $site.put(StringIO.new("<!DOCTYPE html><html><head><title>My Website</title></head><body><h1><blink><font color="yellow">HELLO! WELCOME TO MY WEBSITE</font></blink></h1></body></html>", "r"), "index.html")
49
- Net::HTTP.get("http://#{$site.website}/") #=> ...HELLO! WELCOME TO MY WEBSITE...
61
+ $site.put StringIO.new("<!DOCTYPE html><html><head><title>My Website</title></head><body><h1><blink><font color="yellow">HELLO! WELCOME TO MY WEBSITE</font></blink></h1></body></html>", "r"), "index.html", "text/html"
62
+ Net::HTTP.get "http://#{$site.website}/" #=> ...HELLO! WELCOME TO MY WEBSITE...
50
63
 
51
- Plus a handful of other miscellaneous things...
64
+ Plus a handful of other miscellaneous things (see [RDoc](http://rubydoc.info/gems/s4))...
52
65
 
53
66
  Acknowledgements
54
67
  ----------------
data/lib/s4.rb CHANGED
@@ -6,7 +6,7 @@ require "json"
6
6
 
7
7
  # Simpler AWS S3 library
8
8
  class S4
9
- VERSION = "0.0.3"
9
+ VERSION = "0.0.4"
10
10
 
11
11
  # sub-resource names which may appear in the query string and also must be
12
12
  # signed against.
@@ -15,11 +15,11 @@ class S4
15
15
  # Header over-rides which may appear in the query string and also must be
16
16
  # signed against (in addition those which begin w/ 'x-amz-')
17
17
  HeaderValues = %w( response-content-type response-content-language response-expires reponse-cache-control response-content-disposition response-content-encoding )
18
-
18
+
19
19
  # List of available ACLs on buckets, first is used as default
20
20
  # http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTBucketPUT.html
21
21
  BucketACLs = %w( private public-read public-read-write authenticated-read bucket-owner-read bucket-owner-full-control )
22
-
22
+
23
23
  # Named policies
24
24
  Policy = {
25
25
  public_read: %Q{\{
@@ -35,18 +35,18 @@ class S4
35
35
  }.freeze
36
36
 
37
37
  attr_reader :connection, :access_key_id, :secret_access_key, :bucket, :host
38
-
38
+
39
39
  # Cannot call #new explicitly (no reason to), use #connect instead
40
40
  private_class_method :new
41
-
41
+
42
42
  class << self
43
43
  # Connect to an S3 bucket.
44
- #
44
+ #
45
45
  # Pass your S3 connection parameters as URL, or read from ENV["S3_URL"] if
46
46
  # none is passed.
47
- #
47
+ #
48
48
  # S3_URL format is s3://<access key id>:<secret access key>@s3.amazonaws.com/<bucket>
49
- #
49
+ #
50
50
  # i.e.
51
51
  # bucket = S4.connect #=> Connects to ENV["S3_URL"]
52
52
  # bucket = S4.connect(url: "s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/bucket")
@@ -55,31 +55,31 @@ class S4
55
55
  s4.connect
56
56
  end
57
57
  end
58
-
58
+
59
59
  # Create a new S3 bucket.
60
- #
60
+ #
61
61
  # See #connect for S3_URL parameters.
62
- #
62
+ #
63
63
  # Will create the bucket on S3 and connect to it, or just connect if the
64
64
  # bucket already exists and is owned by you.
65
- #
65
+ #
66
66
  # i.e.
67
67
  # bucket = S4.create
68
68
  def create(options={})
69
69
  init(options) do |s4|
70
70
  s4.create(options[:acl] || BucketACLs.first)
71
71
  end
72
- end
73
-
72
+ end
73
+
74
74
  private
75
-
75
+
76
76
  def init(options={}, &block)
77
77
  new(options.has_key?(:url) ? options[:url] : ENV["S3_URL"]).tap do |s4|
78
78
  yield(s4) if block_given?
79
79
  end
80
80
  end
81
81
  end
82
-
82
+
83
83
  def initialize(s3_url=ENV["S3_URL"])
84
84
  raise ArgumentError, "No S3 URL provided. You can set ENV['S3_URL'], too." if s3_url.nil? || s3_url.empty?
85
85
 
@@ -95,31 +95,31 @@ class S4
95
95
  @host = url.host
96
96
  @bucket = url.path[1..-1]
97
97
  end
98
-
98
+
99
99
  # Connect to the S3 bucket.
100
- #
100
+ #
101
101
  # Since S3 doesn't really require a persistent connection this really just
102
102
  # makes sure that it *can* connect (i.e. the bucket exists and you own it).
103
103
  def connect
104
104
  location
105
- end
106
-
105
+ end
106
+
107
107
  # Create the S3 bucket.
108
- #
108
+ #
109
109
  # If the bucket exists and you own it will not do anything, if it exists and
110
110
  # you don't own it will raise an error.
111
- #
111
+ #
112
112
  # Optionally pass an ACL for the new bucket, see BucketACLs for valid ACLs.
113
- #
113
+ #
114
114
  # Default ACL is "private"
115
115
  def create(acl=BucketACLs.first)
116
116
  raise ArgumentError.new("Invalid ACL '#{acl}' for bucket. Available ACLs are: #{BucketACLs.join(", ")}.") unless BucketACLs.include?(acl)
117
-
117
+
118
118
  uri = uri("/")
119
119
  req = Net::HTTP::Put.new(uri.request_uri)
120
-
120
+
121
121
  req.add_field "x-amz-acl", acl
122
-
122
+
123
123
  request uri, req
124
124
  end
125
125
 
@@ -132,7 +132,7 @@ class S4
132
132
  end
133
133
 
134
134
  # Download the file with the given filename to the given destination.
135
- #
135
+ #
136
136
  # i.e.
137
137
  # bucket.download("images/palm_trees.jpg", "./palm_trees.jpg")
138
138
  def download(name, destination=nil)
@@ -152,64 +152,68 @@ class S4
152
152
 
153
153
  # Upload the file with the given filename to the given destination in your S3
154
154
  # bucket.
155
- #
155
+ #
156
156
  # If no destination is given then uploads it with the same filename to the
157
157
  # root of your bucket.
158
- #
158
+ #
159
159
  # i.e.
160
160
  # bucket.upload("./images/1996_animated_explosion.gif", "website_background.gif")
161
161
  def upload(name, destination=nil)
162
162
  put File.open(name, "rb"), destination || File.basename(name)
163
163
  end
164
-
164
+
165
165
  # Write an IO stream to a file in this bucket.
166
- #
166
+ #
167
167
  # Will write file with content_type if given, otherwise will attempt to
168
168
  # determine content type by shelling out to POSIX `file` command (if IO
169
169
  # stream responds to #path). If no content_type could be determined, will
170
170
  # default to application/x-www-form-urlencoded.
171
- #
171
+ #
172
172
  # i.e.
173
173
  # bucket.put(StringIO.new("Awesome!"), "awesome.txt", "text/plain")
174
174
  def put(io, name, content_type=nil)
175
175
  uri = uri(name)
176
176
  req = Net::HTTP::Put.new(uri.request_uri)
177
-
177
+
178
178
  content_type = `file -ib #{io.path}`.chomp if !content_type && io.respond_to?(:path)
179
-
179
+
180
180
  req.add_field "Content-Type", content_type
181
181
  req.add_field "Content-Length", io.size
182
182
  req.body_stream = io
183
183
 
184
- request uri("/#{name}"), req
184
+ target_uri = uri("/#{name}")
185
+
186
+ request(target_uri, req)
187
+
188
+ target_uri.to_s
185
189
  end
186
190
 
187
191
  # List bucket contents.
188
- #
192
+ #
189
193
  # Optionally pass a prefix to list from (useful for paths).
190
- #
194
+ #
191
195
  # i.e.
192
196
  # bucket.list("images/") #=> [ "birds.jpg", "bees.jpg" ]
193
197
  def list(prefix = "")
194
198
  REXML::Document.new(request(uri("", query: "prefix=#{prefix}"))).elements.collect("//Key", &:text)
195
199
  end
196
-
200
+
197
201
  # Turns this bucket into a S3 static website bucket.
198
- #
202
+ #
199
203
  # IMPORTANT: by default a policy will be applied to the bucket allowing read
200
204
  # access to all files contained in the bucket.
201
- #
205
+ #
202
206
  # i.e.
203
207
  # site = S4.connect(url: "s3://0PN5J17HBGZHT7JJ3X82:k3nL7gH3+PadhTEVn5EXAMPLE@s3.amazonaws.com/mywebsite")
204
208
  # site.website!
205
209
  # site.put(StringIO.new("<!DOCTYPE html><html><head><title>Robots!</title></head><body><h1>So many robots!!!</h1></body></html>", "r"), "index.html")
206
- # Net::HTTP.get(URI.parse("http://mywebsite.s3.amazonaws.com/")) #=> ...<h1>So many robots!!!</h1>...
210
+ # Net::HTTP.get(URI.parse("http://mywebsite.s3.amazonaws.com/")) #=> ...<h1>So many robots!!!</h1>...
207
211
  def website!
208
212
  self.policy = Policy[:public_read] % bucket
209
-
213
+
210
214
  uri = uri("/", query: "website")
211
215
  req = Net::HTTP::Put.new(uri.request_uri)
212
-
216
+
213
217
  req.body = <<-XML
214
218
  <WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
215
219
  <IndexDocument>
@@ -220,23 +224,23 @@ class S4
220
224
  </ErrorDocument>
221
225
  </WebsiteConfiguration>
222
226
  XML
223
-
224
- request uri, req
227
+
228
+ request uri, req
225
229
  end
226
-
230
+
227
231
  # The URL of the bucket for use as a website.
228
232
  def website
229
233
  "#{bucket}.s3-website-#{location}.amazonaws.com"
230
234
  end
231
-
235
+
232
236
  # Sets the given policy on the bucket.
233
- #
237
+ #
234
238
  # Policy can be given as a string which will be applied as given, a hash
235
239
  # which will be converted to json, or the name of a pre-defined policy as a
236
240
  # symbol.
237
- #
241
+ #
238
242
  # See S4::Policy for pre-defined policies.
239
- #
243
+ #
240
244
  # i.e.
241
245
  # $s4 = S4.connect
242
246
  # $s4.policy = :public_read #=> apply named policy
@@ -244,38 +248,38 @@ class S4
244
248
  # $s4.policy = "{\"Statement\": \"...\"}" #=> apply policy as string
245
249
  def policy=(policy)
246
250
  policy = Policy[policy] % bucket if policy.is_a?(Symbol)
247
-
251
+
248
252
  uri = uri("/", query: "policy")
249
253
  req = Net::HTTP::Put.new(uri.request_uri)
250
-
254
+
251
255
  req.body = policy.is_a?(String) ? policy : policy.to_json
252
-
256
+
253
257
  request uri, req
254
258
  end
255
-
259
+
256
260
  # Gets the policy on the bucket.
257
261
  def policy
258
262
  request uri("/", query: "policy")
259
263
  end
260
-
264
+
261
265
  # Gets information about the buckets location.
262
266
  def location
263
267
  response = request uri("/", query: "location")
264
268
  location = REXML::Document.new(response).elements["LocationConstraint"].text
265
-
269
+
266
270
  location || "us-east-1"
267
271
  end
268
-
272
+
269
273
  def inspect
270
274
  "#<S4: bucket='#{bucket}'>"
271
275
  end
272
-
276
+
273
277
  private
274
-
278
+
275
279
  def connection
276
280
  @connection ||= Net::HTTP::Persistent.new("aws-s3/#{bucket}")
277
281
  end
278
-
282
+
279
283
  def uri(path, options={})
280
284
  URI::HTTP.build(options.merge(host: host, path: "/#{bucket}/#{URI.escape(path.sub(/^\//, ""))}"))
281
285
  end
@@ -283,7 +287,7 @@ class S4
283
287
  # Makes a request to the S3 API.
284
288
  def request(uri, request=nil)
285
289
  request ||= Net::HTTP::Get.new(uri.request_uri)
286
-
290
+
287
291
  connection.request(uri, sign(uri, request)) do |response|
288
292
  case response
289
293
  when Net::HTTPSuccess
@@ -310,9 +314,9 @@ class S4
310
314
 
311
315
  def signature(uri, request)
312
316
  query = signed_params(uri.query) if uri.query
313
-
317
+
314
318
  string_to_sign = "#{request.class::METHOD}\n\n#{request["Content-Type"]}\n#{request["Date"]}\n#{canonicalized_headers(request)}" + "#{uri.path}" + (query ? "?#{query}" : "")
315
-
319
+
316
320
  Base64.encode64(
317
321
  OpenSSL::HMAC.digest(
318
322
  OpenSSL::Digest::Digest.new("sha1"),
@@ -321,7 +325,7 @@ class S4
321
325
  )
322
326
  ).chomp
323
327
  end
324
-
328
+
325
329
  # Returns the given query string consisting only of query parameters which
326
330
  # need to be signed against, or nil if there are none in the query string.
327
331
  def signed_params(query)
@@ -332,31 +336,31 @@ class S4
332
336
  collect{ |pair| pair.join("=") }.
333
337
  sort.
334
338
  join("&")
335
-
339
+
336
340
  signed unless signed.empty?
337
341
  end
338
-
342
+
339
343
  def canonicalized_headers(request)
340
344
  headers = request.to_hash.
341
345
  reject{ |k, v| k !~ /x-amz-/ && !HeaderValues.include?(k) }.
342
346
  collect{ |k, v| "#{k}:#{v.join(",")}" }.
343
347
  sort.
344
348
  join("\n")
345
-
349
+
346
350
  "#{headers}\n" unless headers.empty?
347
351
  end
348
-
352
+
349
353
  # Base class of all S3 Errors
350
354
  class Error < ::RuntimeError
351
355
  attr_reader :code, :status, :response
352
-
356
+
353
357
  def initialize(response)
354
358
  @response = REXML::Document.new(response.body).elements["//Error"]
355
359
 
356
360
  @status = response.code
357
361
  @code = @response.elements["Code"].text
358
-
362
+
359
363
  super "#{@status}: #{@code} -- " + @response.elements["Message"].text
360
364
  end
361
- end
365
+ end
362
366
  end
data/s4.gemspec CHANGED
@@ -10,6 +10,7 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "http://github.com/benalavi/s4"
11
11
 
12
12
  s.files = Dir[
13
+ "CHANGELOG*",
13
14
  "README.md",
14
15
  "rakefile",
15
16
  "s4.gemspec",
@@ -1,5 +1,5 @@
1
1
  raise "You need to have ENV[\"S3_URL\"] set for the tests to connect to your testing bucket on S3. Format is: 's3://<access key id>:<secret access key>@s3.amazonaws.com/<s4 test bucket>'." unless ENV["S3_URL"]
2
- raise "You need to have ENV[\"S4_NEW_BUCKET\"], which will be dynamically created and destroyed for testing bucket creation. i.e.: 's4-test-bucketthatdoesntexist'." unless ENV["S3_URL"]
2
+ raise "You need to have ENV[\"S4_NEW_BUCKET\"], which will be dynamically created and destroyed for testing bucket creation. i.e.: 's4-test-bucketthatdoesntexist'." unless ENV["S3_NEW_BUCKET"]
3
3
 
4
4
  require "contest"
5
5
  require "timecop"
@@ -30,28 +30,28 @@ class S4Test < Test::Unit::TestCase
30
30
  `s3cmd del 's3://#{TestBucket}/*' 2>&1`
31
31
  `s3cmd rb 's3://#{TestBucket}' 2>&1`
32
32
  end
33
-
33
+
34
34
  setup do
35
35
  FileUtils.rm_rf(output)
36
36
  FileUtils.mkdir_p(output)
37
37
  end
38
-
38
+
39
39
  context "connecting to S3" do
40
40
  should "return connected bucket if can connect" do
41
41
  s4 = S4.connect
42
42
  assert s4
43
43
  end
44
-
44
+
45
45
  should "bark when no URL is provided" do
46
46
  assert_raise(ArgumentError) { S4.connect(url: "") }
47
47
  assert_raise(ArgumentError) { S4.connect(url: nil) }
48
48
  assert_raise(URI::InvalidURIError) { S4.connect(url: "s3://foo:bar/baz") }
49
49
  end
50
-
50
+
51
51
  should "raise error if cannot connect" do
52
52
  `s3cmd del 's3://#{NewBucket}/*' 2>&1`
53
53
  `s3cmd rb 's3://#{NewBucket}' 2>&1`
54
-
54
+
55
55
  assert_raise(S4::Error) do
56
56
  S4.connect url: ENV["S3_URL"].sub(TestBucket, NewBucket)
57
57
  end
@@ -65,7 +65,7 @@ class S4Test < Test::Unit::TestCase
65
65
  S4.create url: ENV["S3_URL"].sub(TestBucket, "foo")
66
66
  end
67
67
  end
68
-
68
+
69
69
  should "capture code of S3 error" do
70
70
  begin
71
71
  S4.create url: ENV["S3_URL"].sub(TestBucket, "foo")
@@ -74,31 +74,31 @@ class S4Test < Test::Unit::TestCase
74
74
  end
75
75
  end
76
76
  end
77
-
78
- context "creating a bucket" do
77
+
78
+ context "creating a bucket" do
79
79
  setup do
80
80
  `s3cmd del 's3://#{NewBucket}/*' 2>&1`
81
81
  `s3cmd rb 's3://#{NewBucket}' 2>&1`
82
82
  end
83
-
83
+
84
84
  should "create a bucket" do
85
85
  assert_equal "ERROR: Bucket '#{NewBucket}' does not exist", `s3cmd ls 's3://#{NewBucket}' 2>&1`.chomp
86
-
86
+
87
87
  S4.create url: ENV["S3_URL"].sub(TestBucket, NewBucket)
88
-
88
+
89
89
  assert_equal "", `s3cmd ls 's3://#{NewBucket}' 2>&1`.chomp
90
90
  end
91
-
91
+
92
92
  should "create bucket with public-read ACL" do
93
93
  # TODO...
94
94
  end
95
-
95
+
96
96
  should "raise if bucket creation failed" do
97
97
  assert_raise(S4::Error) do
98
98
  S4.create url: ENV["S3_URL"].sub(TestBucket, "foo")
99
99
  end
100
100
  end
101
-
101
+
102
102
  should "raise if given invalid ACL" do
103
103
  begin
104
104
  S4.create url: ENV["S3_URL"], acl: "foo"
@@ -107,27 +107,27 @@ class S4Test < Test::Unit::TestCase
107
107
  end
108
108
  end
109
109
  end
110
-
110
+
111
111
  context "making a website" do
112
112
  setup do
113
113
  delete_test_bucket
114
114
  end
115
-
115
+
116
116
  should "make bucket a website" do
117
117
  s4 = S4.create
118
-
118
+
119
119
  begin
120
120
  open("http://#{s4.website}/")
121
121
  rescue OpenURI::HTTPError => e
122
122
  assert_match /NoSuchWebsiteConfiguration/, e.io.read
123
123
  end
124
-
124
+
125
125
  s4.put(StringIO.new("<!DOCTYPE html><html><head><title>Robot Page</title></head><body><h1>Robots!</h1></body></html>", "r"), "index.html", "text/html")
126
126
  s4.put(StringIO.new("<!DOCTYPE html><html><head><title>404!</title></head><body><h1>Oh No 404!!!</h1></body></html>", "r"), "404.html", "text/html")
127
127
  s4.website!
128
-
128
+
129
129
  assert_match /Robots!/, open("http://#{s4.website}/").read
130
-
130
+
131
131
  begin
132
132
  open("http://#{s4.website}/foo.html")
133
133
  rescue OpenURI::HTTPError => e
@@ -136,55 +136,66 @@ class S4Test < Test::Unit::TestCase
136
136
  end
137
137
  end
138
138
  end
139
-
139
+
140
140
  context "setting policy on a bucket" do
141
141
  setup do
142
142
  delete_test_bucket
143
143
  @s4 = S4.create
144
144
  end
145
-
145
+
146
146
  should "make all objects public by policy" do
147
147
  @s4.upload(fixture("foo.txt"))
148
-
148
+
149
149
  begin
150
150
  open("http://s3.amazonaws.com/#{TestBucket}/foo.txt")
151
151
  rescue OpenURI::HTTPError => e
152
152
  assert_match /403 Forbidden/, e.message
153
153
  end
154
-
154
+
155
155
  @s4.policy = :public_read
156
-
156
+
157
157
  assert_equal "abc123", open("http://s3.amazonaws.com/#{TestBucket}/foo.txt").read
158
158
  end
159
159
  end
160
-
160
+
161
161
  context "uploading to bucket" do
162
162
  setup do
163
163
  delete_test_bucket
164
164
  @s4 = S4.create
165
165
  @s4.policy = :public_read
166
166
  end
167
-
167
+
168
168
  should "upload foo.txt" do
169
169
  @s4.upload(fixture("foo.txt"))
170
-
170
+
171
171
  foo = open("http://s3.amazonaws.com/#{TestBucket}/foo.txt")
172
-
172
+
173
173
  assert_equal "abc123", foo.read
174
- assert_equal "text/plain", foo.content_type
174
+ assert_equal "text/plain", foo.content_type
175
175
  end
176
-
176
+
177
177
  should "use given content_type" do
178
- @s4.put(StringIO.new("abcdef", "r"), "bar.txt", "text/foobar")
178
+ @s4.put StringIO.new("abcdef", "r"), "bar.txt", "text/foobar"
179
179
  assert_equal "text/foobar", open("http://s3.amazonaws.com/#{TestBucket}/bar.txt").content_type
180
180
  end
181
+
182
+ should "upload to a path" do
183
+ @s4.put StringIO.new("zoinks!", "r"), "foo/bar.txt", "text/plain"
184
+ assert_equal "zoinks!", open("http://s3.amazonaws.com/#{TestBucket}/foo/bar.txt").read
185
+ end
186
+
187
+ should "return the URL to the uploaded file" do
188
+ url = @s4.put StringIO.new("zoinks!", "r"), "foo/bar.txt", "text/plain"
189
+ assert_kind_of URI::HTTP, URI.parse(url)
190
+ assert_equal "zoinks!", open(url).read
191
+ end
181
192
  end
182
-
193
+
183
194
  context "when connected" do
184
195
  setup do
185
196
  @s4 = S4.connect
186
197
  end
187
-
198
+
188
199
  should "download foo.txt" do
189
200
  `s3cmd put #{fixture("foo.txt")} s3://#{@s4.bucket}/foo.txt`
190
201
  @s4.download("foo.txt", output("foo.txt"))
@@ -198,7 +209,7 @@ class S4Test < Test::Unit::TestCase
198
209
 
199
210
  assert !File.exists?(output("foo.txt"))
200
211
  end
201
-
212
+
202
213
  should "return false when downloading non-existent files" do
203
214
  `s3cmd del 's3://#{@s4.bucket}/foo.txt'`
204
215
  assert_equal nil, @s4.download("foo.txt", output("foo.txt"))
@@ -223,7 +234,7 @@ class S4Test < Test::Unit::TestCase
223
234
  should "return list of items in bucket" do
224
235
  `s3cmd del 's3://#{@s4.bucket}/*'`
225
236
  `s3cmd del 's3://#{@s4.bucket}/abc/*'`
226
-
237
+
227
238
  `s3cmd put #{fixture("foo.txt")} s3://#{@s4.bucket}/foo.txt`
228
239
  `s3cmd put #{fixture("foo.txt")} s3://#{@s4.bucket}/bar.txt`
229
240
  `s3cmd put #{fixture("foo.txt")} s3://#{@s4.bucket}/baz.txt`
metadata CHANGED
@@ -1,91 +1,86 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: s4
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
4
5
  prerelease:
5
- version: 0.0.3
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Ben Alavi
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-09-04 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2011-09-20 00:00:00.000000000 -03:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
16
  name: net-http-persistent
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2153639160 !ruby/object:Gem::Requirement
19
18
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "1.7"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '1.7'
24
23
  type: :runtime
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: cutest
28
24
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2153639160
26
+ - !ruby/object:Gem::Dependency
27
+ name: cutest
28
+ requirement: &2153638740 !ruby/object:Gem::Requirement
30
29
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
35
34
  type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: timecop
39
35
  prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
36
+ version_requirements: *2153638740
37
+ - !ruby/object:Gem::Dependency
38
+ name: timecop
39
+ requirement: &2153638200 !ruby/object:Gem::Requirement
41
40
  none: false
42
- requirements:
41
+ requirements:
43
42
  - - ~>
44
- - !ruby/object:Gem::Version
45
- version: "0.3"
43
+ - !ruby/object:Gem::Version
44
+ version: '0.3'
46
45
  type: :development
47
- version_requirements: *id003
46
+ prerelease: false
47
+ version_requirements: *2153638200
48
48
  description: Simple API for AWS S3
49
- email:
49
+ email:
50
50
  - ben.alavi@citrusbyte.com
51
51
  executables: []
52
-
53
52
  extensions: []
54
-
55
53
  extra_rdoc_files: []
56
-
57
- files:
54
+ files:
55
+ - CHANGELOG
58
56
  - README.md
59
57
  - rakefile
60
58
  - s4.gemspec
61
59
  - lib/s4.rb
62
60
  - test/s4_test.rb
61
+ has_rdoc: true
63
62
  homepage: http://github.com/benalavi/s4
64
63
  licenses: []
65
-
66
64
  post_install_message:
67
65
  rdoc_options: []
68
-
69
- require_paths:
66
+ require_paths:
70
67
  - lib
71
- required_ruby_version: !ruby/object:Gem::Requirement
68
+ required_ruby_version: !ruby/object:Gem::Requirement
72
69
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: "0"
77
- required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
75
  none: false
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: "0"
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
83
80
  requirements: []
84
-
85
81
  rubyforge_project:
86
- rubygems_version: 1.8.10
82
+ rubygems_version: 1.6.2
87
83
  signing_key:
88
84
  specification_version: 3
89
85
  summary: Simple API for AWS S3
90
86
  test_files: []
91
-