s3 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.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