s3 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -4,4 +4,4 @@ coverage
4
4
  rdoc
5
5
  pkg
6
6
  *.gem
7
- *.gemspec
7
+
data/Rakefile CHANGED
@@ -8,12 +8,13 @@ begin
8
8
  Jeweler::Tasks.new do |gem|
9
9
  gem.name = "s3"
10
10
  gem.summary = %Q{Library for accessing S3 objects and buckets, with command line tool}
11
+ gem.description = %Q{S3 library provides access to Amazon's Simple Storage Service. It supports both: European and US buckets through REST API.}
11
12
  gem.email = "qoobaa@gmail.com"
12
13
  gem.homepage = "http://jah.pl/projects/s3.html"
13
14
  gem.authors = ["Jakub Kuźma", "Mirosław Boruta"]
14
15
  gem.add_dependency "trollop", ">=1.14"
15
16
  gem.add_development_dependency "test-unit", ">= 2.0"
16
- gem.add_development_dependency "rr", "=0.10.2"
17
+ gem.add_development_dependency "mocha"
17
18
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
19
  end
19
20
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.2.5
data/lib/s3.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "base64"
2
+ require "cgi"
2
3
  require "digest/md5"
3
4
  require "forwardable"
4
5
  require "net/http"
@@ -26,15 +26,15 @@ module S3
26
26
  end
27
27
  end
28
28
 
29
- # Compares the bucket with other bucket. Returns true if the key
30
- # of the objects are the same, and both have the same buckets (see
31
- # bucket equality)
29
+ # Compares the bucket with other bucket. Returns true if the names
30
+ # of the buckets are the same, and both have the same services
31
+ # (see Service equality)
32
32
  def ==(other)
33
33
  self.name == other.name and self.service == other.service
34
34
  end
35
35
 
36
- # Similar to retrieve, but catches NoSuchBucket exceptions and
37
- # returns false instead.
36
+ # Similar to retrieve, but catches S3::Error::NoSuchBucket
37
+ # exceptions and returns false instead.
38
38
  def exists?
39
39
  retrieve
40
40
  true
@@ -42,9 +42,9 @@ module S3
42
42
  false
43
43
  end
44
44
 
45
- # Destroys given bucket. Raises an BucketNotEmpty exception if the
46
- # bucket is not empty. You can destroy non-empty bucket passing
47
- # true (to force destroy)
45
+ # Destroys given bucket. Raises an S3::Error::BucketNotEmpty
46
+ # exception if the bucket is not empty. You can destroy non-empty
47
+ # bucket passing true (to force destroy)
48
48
  def destroy(force = false)
49
49
  delete_bucket
50
50
  true
@@ -58,33 +58,32 @@ module S3
58
58
  end
59
59
 
60
60
  # Saves the newly built bucket. Optionally you can pass location
61
- # of the bucket (:eu or :us)
61
+ # of the bucket (<tt>:eu</tt> or <tt>:us</tt>)
62
62
  def save(location = nil)
63
63
  create_bucket_configuration(location)
64
64
  true
65
65
  end
66
66
 
67
- # Returns true if the name of the bucket can be used like VHOST
67
+ # Returns true if the name of the bucket can be used like +VHOST+
68
68
  # name. If the bucket contains characters like underscore it can't
69
- # be used as VHOST (e.g. bucket_name.s3.amazonaws.com)
69
+ # be used as +VHOST+ (e.g. <tt>bucket_name.s3.amazonaws.com</tt>)
70
70
  def vhost?
71
71
  "#@name.#{HOST}" =~ /\A#{URI::REGEXP::PATTERN::HOSTNAME}\Z/
72
72
  end
73
73
 
74
- # Returns host name of the bucket according (see vhost?)
74
+ # Returns host name of the bucket according (see #vhost? method)
75
75
  def host
76
76
  vhost? ? "#@name.#{HOST}" : "#{HOST}"
77
77
  end
78
78
 
79
- # Returns path prefix for non VHOST bucket. Path prefix is used
80
- # instead of VHOST name,
81
- # e.g. "bucket_name/"
79
+ # Returns path prefix for non +VHOST+ bucket. Path prefix is used
80
+ # instead of +VHOST+ name, e.g. "bucket_name/"
82
81
  def path_prefix
83
82
  vhost? ? "" : "#@name/"
84
83
  end
85
84
 
86
85
  # Returns the objects in the bucket and caches the result (see
87
- # reload).
86
+ # #reload method).
88
87
  def objects(reload = false)
89
88
  if reload or @objects.nil?
90
89
  @objects = list_bucket
@@ -109,11 +108,17 @@ module S3
109
108
  alias :find :find_first
110
109
 
111
110
  # Finds the objects in the bucket.
112
- # ==== Options:
113
- # +prefix+:: Limits the response to keys which begin with the indicated prefix
114
- # +marker+:: Indicates where in the bucket to begin listing
115
- # +max_keys+:: The maximum number of keys you'd like to see
116
- # +delimiter+:: Causes keys that contain the same string between the prefix and the first occurrence of the delimiter to be rolled up into a single result element
111
+ #
112
+ # ==== Options
113
+ # * <tt>:prefix</tt> - Limits the response to keys which begin
114
+ # with the indicated prefix
115
+ # * <tt>:marker</tt> - Indicates where in the bucket to begin
116
+ # listing
117
+ # * <tt>:max_keys</tt> - The maximum number of keys you'd like
118
+ # to see
119
+ # * <tt>:delimiter</tt> - Causes keys that contain the same
120
+ # string between the prefix and the first occurrence of the
121
+ # delimiter to be rolled up into a single result element
117
122
  def find_all(options = {})
118
123
  proxy_owner.send(:list_bucket, options)
119
124
  end
@@ -7,46 +7,52 @@ module S3
7
7
  attr_accessor :access_key_id, :secret_access_key, :use_ssl, :timeout, :debug
8
8
  alias :use_ssl? :use_ssl
9
9
 
10
- # ==== Parameters:
11
- # +options+:: Hash of options
10
+ # Creates new connection object.
12
11
  #
13
- # ==== Options:
14
- # +access_key_id+:: access key id
15
- # +secret_access_key+:: secret access key
16
- # +use_ssl+:: optional, defaults to false
17
- # +debug+:: optional, defaults to false
18
- # +timeout+:: optional, for Net::HTTP
12
+ # ==== Options
13
+ # * <tt>:access_key_id</tt> - Access key id (REQUIRED)
14
+ # * <tt>:secret_access_key</tt> - Secret access key (REQUIRED)
15
+ # * <tt>:use_ssl</tt> - Use https or http protocol (false by
16
+ # default)
17
+ # * <tt>:debug</tt> - Display debug information on the STDOUT
18
+ # (false by default)
19
+ # * <tt>:timeout</tt> - Timeout to use by the Net::HTTP object
20
+ # (60 by default)
19
21
  def initialize(options = {})
20
- @access_key_id = options[:access_key_id]
21
- @secret_access_key = options[:secret_access_key]
22
- @use_ssl = options[:use_ssl] || false
23
- @debug = options[:debug]
24
- @timeout = options[:timeout]
22
+ @access_key_id = options.fetch(:access_key_id)
23
+ @secret_access_key = options.fetch(:secret_access_key)
24
+ @use_ssl = options.fetch(:use_ssl, false)
25
+ @debug = options.fetch(:debug, false)
26
+ @timeout = options.fetch(:timeout, 60)
25
27
  end
26
28
 
27
29
  # Makes request with given HTTP method, sets missing parameters,
28
30
  # adds signature to request header and returns response object
29
31
  # (Net::HTTPResponse)
30
32
  #
31
- # ==== Parameters:
32
- # +method+:: HTTP Method symbol, can be :get, :put, :delete
33
- # +options+:: hash of options
33
+ # ==== Parameters
34
+ # * <tt>method</tt> - HTTP Method symbol, can be <tt>:get</tt>,
35
+ # <tt>:put</tt>, <tt>:delete</tt>
34
36
  #
35
37
  # ==== Options:
36
- # +host+:: hostname to connecto to, optional, defaults to s3.amazonaws.com[s3.amazonaws.com]
37
- # +path+:: path to send request to, required, throws ArgumentError if not given
38
- # +body+:: request body, only meaningful for :put request
39
- # +params+:: parameters to add to query string for request, can be String or Hash
40
- # +headers+:: Hash of headers fields to add to request header
38
+ # * <tt>:host</tt> - Hostname to connecto to, defaults
39
+ # to <tt>s3.amazonaws.com</tt>
40
+ # * <tt>:path</tt> - path to send request to (REQUIRED)
41
+ # * <tt>:body</tt> - Request body, only meaningful for
42
+ # <tt>:put</tt> request
43
+ # * <tt>:params</tt> - Parameters to add to query string for
44
+ # request, can be String or Hash
45
+ # * <tt>:headers</tt> - Hash of headers fields to add to request
46
+ # header
41
47
  #
42
- # ==== Returns:
43
- # Net::HTTPResponse object -- response from remote server
48
+ # ==== Returns
49
+ # Net::HTTPResponse object -- response from the server
44
50
  def request(method, options)
45
- host = options[:host] || HOST
46
- path = options[:path] or raise ArgumentError.new("no path given")
47
- body = options[:body]
48
- params = options[:params]
49
- headers = options[:headers]
51
+ host = options.fetch(:host, HOST)
52
+ path = options.fetch(:path)
53
+ body = options.fetch(:body, "")
54
+ params = options.fetch(:params, {})
55
+ headers = options.fetch(:headers, {})
50
56
 
51
57
  if params
52
58
  params = params.is_a?(String) ? params : self.class.parse_params(params)
@@ -66,13 +72,13 @@ module S3
66
72
  send_request(host, request)
67
73
  end
68
74
 
69
- # Helper function to parser parameters and create single string of params
70
- # added to questy string
75
+ # Helper function to parser parameters and create single string of
76
+ # params added to questy string
71
77
  #
72
- # ==== Parameters:
73
- # +params+: Hash of parameters if form <tt>key => value|nil</tt>
78
+ # ==== Parameters
79
+ # * <tt>params</tt> - Hash of parameters
74
80
  #
75
- # ==== Returns:
81
+ # ==== Returns
76
82
  # String -- containing all parameters joined in one params string,
77
83
  # i.e. <tt>param1=val&param2&param3=0</tt>
78
84
  def self.parse_params(params)
@@ -96,14 +102,14 @@ module S3
96
102
  # Helper function to change headers from symbols, to in correct
97
103
  # form (i.e. with '-' instead of '_')
98
104
  #
99
- # ==== Parameters:
100
- # +headers+:: Hash of pairs <tt>headername => value</tt>,
101
- # where value can be Range (for Range header) or any other
102
- # value which can be translated to string
105
+ # ==== Parameters
106
+ # * <tt>headers</tt> - Hash of pairs <tt>headername => value</tt>,
107
+ # where value can be Range (for Range header) or any other value
108
+ # which can be translated to string
103
109
  #
104
- # ==== Returns:
105
- # Hash of headers translated from symbol to string,
106
- # containing only interesting headers
110
+ # ==== Returns
111
+ # Hash of headers translated from symbol to string, containing
112
+ # only interesting headers
107
113
  def self.parse_headers(headers)
108
114
  interesting_keys = [:content_type, :x_amz_acl, :range,
109
115
  :if_modified_since, :if_unmodified_since,
@@ -170,9 +176,9 @@ module S3
170
176
  end
171
177
 
172
178
  request["Authorization"] = Signature.generate(:host => host,
173
- :request => request,
174
- :access_key_id => access_key_id,
175
- :secret_access_key => secret_access_key)
179
+ :request => request,
180
+ :access_key_id => access_key_id,
181
+ :secret_access_key => secret_access_key)
176
182
  http.request(request)
177
183
  end
178
184
 
@@ -12,23 +12,25 @@ module S3
12
12
  class ResponseError < StandardError
13
13
  attr_reader :response
14
14
 
15
- # ==== Parameters:
16
- # +message+:: what went wrong
17
- # +response+:: Net::HTTPResponse object or nil
15
+ # Creates new S3::ResponseError.
16
+ #
17
+ # ==== Parameters
18
+ # * <tt>message</tt> - what went wrong
19
+ # * <tt>response</tt> - Net::HTTPResponse object or nil
18
20
  def initialize(message, response)
19
21
  @response = response
20
22
  super(message)
21
23
  end
22
24
 
23
- # Factory for all other Exception classes in module, each for every
24
- # error response available from AmazonAWS
25
+ # Factory for all other Exception classes in module, each for
26
+ # every error response available from AmazonAWS
25
27
  #
26
- # ==== Parameters:
27
- # +code+:: code name of exception
28
+ # ==== Parameters
29
+ # * <tt>code</tt> - Code name of exception
28
30
  #
29
- # ==== Returns:
30
- # Descendant of ResponseError suitable for that exception code or ResponseError class
31
- # if no class found
31
+ # ==== Returns
32
+ # Descendant of ResponseError suitable for that exception code
33
+ # or ResponseError class if no class found
32
34
  def self.exception(code)
33
35
  S3::Error.const_get(code)
34
36
  rescue NameError
@@ -10,17 +10,17 @@ module S3
10
10
  attr_writer :content
11
11
 
12
12
  def_instance_delegators :bucket, :name, :service, :bucket_request, :vhost?, :host, :path_prefix
13
- def_instance_delegators :service, :protocol, :port
13
+ def_instance_delegators :service, :protocol, :port, :secret_access_key
14
14
  private_class_method :new
15
15
 
16
16
  # Compares the object with other object. Returns true if the key
17
17
  # of the objects are the same, and both have the same buckets (see
18
- # bucket equality)
18
+ # Bucket equality)
19
19
  def ==(other)
20
20
  self.key == other.key and self.bucket == other.bucket
21
21
  end
22
22
 
23
- # Returns full key of the object: e.g. +bucket-name/object/key.ext+
23
+ # Returns full key of the object: e.g. <tt>bucket-name/object/key.ext</tt>
24
24
  def full_key
25
25
  [name, key].join("/")
26
26
  end
@@ -34,6 +34,7 @@ module S3
34
34
 
35
35
  # Assigns a new ACL to the object. Please note that ACL is not
36
36
  # retrieved from the server and set to "public-read" by default.
37
+ #
37
38
  # ==== Example
38
39
  # object.acl = :public_read
39
40
  def acl=(acl)
@@ -41,17 +42,17 @@ module S3
41
42
  end
42
43
 
43
44
  # Retrieves the object from the server. Method is used to download
44
- # object information only (content-type, size and so on). It does
45
- # NOT download the content of the object (use the content method
45
+ # object information only (content type, size and so on). It does
46
+ # NOT download the content of the object (use the #content method
46
47
  # to do it).
47
48
  def retrieve
48
49
  get_object(:headers => { :range => 0..0 })
49
50
  self
50
51
  end
51
52
 
52
- # Retrieves the object from the server, returns true if the
53
- # object exists or false otherwise. Uses retrieve method, but
54
- # catches NoSuchKey exception and returns false when it happens
53
+ # Retrieves the object from the server, returns true if the object
54
+ # exists or false otherwise. Uses #retrieve method, but catches
55
+ # S3::Error::NoSuchKey exception and returns false when it happens
55
56
  def exists?
56
57
  retrieve
57
58
  true
@@ -75,11 +76,15 @@ module S3
75
76
  end
76
77
 
77
78
  # Copies the file to another key and/or bucket.
78
- # ==== Options:
79
- # +key+:: new key to store object in
80
- # +bucket+:: new bucket to store object in (instance of S3::Bucket)
81
- # +acl+:: acl of the copied object (default: "public-read")
82
- # +content_type+:: content type of the copied object (default: "application/octet-stream")
79
+ #
80
+ # ==== Options
81
+ # * <tt>:key</tt> - New key to store object in
82
+ # * <tt>:bucket</tt> - New bucket to store object in (instance of
83
+ # S3::Bucket)
84
+ # * <tt>:acl</tt> - ACL of the copied object (default:
85
+ # "public-read")
86
+ # * <tt>:content_type</tt> - Content type of the copied object
87
+ # (default: "application/octet-stream")
83
88
  def copy(options = {})
84
89
  copy_object(options)
85
90
  end
@@ -90,16 +95,28 @@ module S3
90
95
  true
91
96
  end
92
97
 
93
- # Returns Object's URL using protocol specified in Service,
94
- # e.g. http://domain.com.s3.amazonaws.com/key/with/path.extension
98
+ # Returns Object's URL using protocol specified in service,
99
+ # e.g. <tt>http://domain.com.s3.amazonaws.com/key/with/path.extension</tt>
95
100
  def url
96
101
  URI.escape("#{protocol}#{host}/#{path_prefix}#{key}")
97
102
  end
98
103
 
99
- # Returns Object's CNAME URL (without s3.amazonaws.com suffix)
100
- # using protocol specified in Service,
101
- # e.g. http://domain.com/key/with/path.extension. (you have to set
102
- # the CNAME in your DNS before you use the CNAME URL schema).
104
+ # Returns a temporary url to the object that expires on the
105
+ # timestamp given. Defaults to one hour expire time.
106
+ def temporary_url(expires_at = Time.now + 3600)
107
+ signature = Signature.generate_temporary_url_signature(:bucket => name,
108
+ :resource => key,
109
+ :expires_on => expires_at,
110
+ :secret_access_key => secret_access_key)
111
+
112
+ "#{url}?Signature=#{URI.escape(signature)}&Expires=#{URI.escape(expires_at.to_i)}"
113
+ end
114
+
115
+ # Returns Object's CNAME URL (without <tt>s3.amazonaws.com</tt>
116
+ # suffix) using protocol specified in Service,
117
+ # e.g. <tt>http://domain.com/key/with/path.extension</tt>. (you
118
+ # have to set the CNAME in your DNS before using the CNAME URL
119
+ # schema).
103
120
  def cname_url
104
121
  URI.escape("#{protocol}#{name}/#{key}") if bucket.vhost?
105
122
  end
@@ -5,29 +5,33 @@ module S3
5
5
 
6
6
  attr_reader :access_key_id, :secret_access_key, :use_ssl
7
7
 
8
- # Compares service to other, by access_key_id and secret_access_key
8
+ # Compares service to other, by <tt>access_key_id</tt> and
9
+ # <tt>secret_access_key</tt>
9
10
  def ==(other)
10
11
  self.access_key_id == other.access_key_id and self.secret_access_key == other.secret_access_key
11
12
  end
12
13
 
13
- # ==== Parameters:
14
- # +options+:: a hash of options described below
14
+ # Creates new service.
15
15
  #
16
- # ==== Options:
17
- # +access_key_id+:: Amazon access key id, required
18
- # +secret_access_key+:: Amazon secret access key, required
19
- # +use_ssl+:: true if use ssl in connection, otherwise false
20
- # +timeout+:: parameter for Net::HTTP module
21
- # +debug+:: prints the raw requests to STDOUT
16
+ # ==== Options
17
+ # * <tt>:access_key_id</tt> - Access key id (REQUIRED)
18
+ # * <tt>:secret_access_key</tt> - Secret access key (REQUIRED)
19
+ # * <tt>:use_ssl</tt> - Use https or http protocol (false by
20
+ # default)
21
+ # * <tt>:debug</tt> - Display debug information on the STDOUT
22
+ # (false by default)
23
+ # * <tt>:timeout</tt> - Timeout to use by the Net::HTTP object
24
+ # (60 by default)
22
25
  def initialize(options)
23
- @access_key_id = options[:access_key_id] or raise ArgumentError, "No access key id given"
24
- @secret_access_key = options[:secret_access_key] or raise ArgumentError, "No secret access key given"
25
- @use_ssl = options[:use_ssl]
26
- @timeout = options[:timeout]
27
- @debug = options[:debug]
26
+ @access_key_id = options.fetch(:access_key_id)
27
+ @secret_access_key = options.fetch(:secret_access_key)
28
+ @use_ssl = options.fetch(:use_ssl, false)
29
+ @timeout = options.fetch(:timeout, 60)
30
+ @debug = options.fetch(:debug, false)
28
31
  end
29
32
 
30
- # Returns all buckets in the service and caches the result (see reload)
33
+ # Returns all buckets in the service and caches the result (see
34
+ # +reload+)
31
35
  def buckets(reload = false)
32
36
  if reload or @buckets.nil?
33
37
  @buckets = list_all_my_buckets
@@ -36,12 +40,14 @@ module S3
36
40
  end
37
41
  end
38
42
 
39
- # Returns "http://" or "https://", depends on use_ssl value from initializer
43
+ # Returns "http://" or "https://", depends on <tt>:use_ssl</tt>
44
+ # value from initializer
40
45
  def protocol
41
46
  use_ssl ? "https://" : "http://"
42
47
  end
43
48
 
44
- # Return 443 or 80, depends on use_ssl value from initializer
49
+ # Returns 443 or 80, depends on <tt>:use_ssl</tt> value from
50
+ # initializer
45
51
  def port
46
52
  use_ssl ? 443 : 80
47
53
  end
@@ -97,12 +103,11 @@ module S3
97
103
 
98
104
  def connection
99
105
  if @connection.nil?
100
- @connection = Connection.new
101
- @connection.access_key_id = @access_key_id
102
- @connection.secret_access_key = @secret_access_key
103
- @connection.use_ssl = @use_ssl
104
- @connection.timeout = @timeout
105
- @connection.debug = @debug
106
+ @connection = Connection.new(:access_key_id => @access_key_id,
107
+ :secret_access_key => @secret_access_key,
108
+ :use_ssl => @use_ssl,
109
+ :timeout => @timeout,
110
+ :debug => @debug)
106
111
  end
107
112
  @connection
108
113
  end