aws-sdk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. data/.yardopts +6 -0
  2. data/LICENSE.txt +171 -0
  3. data/NOTICE.txt +2 -0
  4. data/README.rdoc +189 -0
  5. data/lib/aws-sdk.rb +14 -0
  6. data/lib/aws.rb +63 -0
  7. data/lib/aws/api_config.rb +45 -0
  8. data/lib/aws/api_config/.document +0 -0
  9. data/lib/aws/api_config/EC2-2011-02-28.yml +2314 -0
  10. data/lib/aws/api_config/SNS-2010-03-31.yml +171 -0
  11. data/lib/aws/api_config/SQS-2009-02-01.yml +161 -0
  12. data/lib/aws/api_config/SimpleDB-2009-04-15.yml +278 -0
  13. data/lib/aws/api_config/SimpleEmailService-2010-12-01.yml +147 -0
  14. data/lib/aws/api_config_transform.rb +32 -0
  15. data/lib/aws/async_handle.rb +90 -0
  16. data/lib/aws/authorize_v2.rb +37 -0
  17. data/lib/aws/authorize_v3.rb +37 -0
  18. data/lib/aws/base_client.rb +524 -0
  19. data/lib/aws/cacheable.rb +92 -0
  20. data/lib/aws/common.rb +228 -0
  21. data/lib/aws/configurable.rb +36 -0
  22. data/lib/aws/configuration.rb +272 -0
  23. data/lib/aws/configured_client_methods.rb +81 -0
  24. data/lib/aws/configured_grammars.rb +65 -0
  25. data/lib/aws/configured_option_grammars.rb +46 -0
  26. data/lib/aws/configured_xml_grammars.rb +47 -0
  27. data/lib/aws/default_signer.rb +38 -0
  28. data/lib/aws/ec2.rb +321 -0
  29. data/lib/aws/ec2/attachment.rb +149 -0
  30. data/lib/aws/ec2/attachment_collection.rb +57 -0
  31. data/lib/aws/ec2/availability_zone.rb +80 -0
  32. data/lib/aws/ec2/availability_zone_collection.rb +47 -0
  33. data/lib/aws/ec2/block_device_mappings.rb +53 -0
  34. data/lib/aws/ec2/client.rb +54 -0
  35. data/lib/aws/ec2/client/xml.rb +127 -0
  36. data/lib/aws/ec2/collection.rb +39 -0
  37. data/lib/aws/ec2/config_transform.rb +63 -0
  38. data/lib/aws/ec2/elastic_ip.rb +107 -0
  39. data/lib/aws/ec2/elastic_ip_collection.rb +85 -0
  40. data/lib/aws/ec2/errors.rb +29 -0
  41. data/lib/aws/ec2/filtered_collection.rb +65 -0
  42. data/lib/aws/ec2/has_permissions.rb +46 -0
  43. data/lib/aws/ec2/image.rb +245 -0
  44. data/lib/aws/ec2/image_collection.rb +235 -0
  45. data/lib/aws/ec2/instance.rb +515 -0
  46. data/lib/aws/ec2/instance_collection.rb +276 -0
  47. data/lib/aws/ec2/key_pair.rb +86 -0
  48. data/lib/aws/ec2/key_pair_collection.rb +102 -0
  49. data/lib/aws/ec2/permission_collection.rb +177 -0
  50. data/lib/aws/ec2/region.rb +81 -0
  51. data/lib/aws/ec2/region_collection.rb +55 -0
  52. data/lib/aws/ec2/request.rb +27 -0
  53. data/lib/aws/ec2/reserved_instances.rb +50 -0
  54. data/lib/aws/ec2/reserved_instances_collection.rb +44 -0
  55. data/lib/aws/ec2/reserved_instances_offering.rb +55 -0
  56. data/lib/aws/ec2/reserved_instances_offering_collection.rb +43 -0
  57. data/lib/aws/ec2/resource.rb +340 -0
  58. data/lib/aws/ec2/resource_tag_collection.rb +218 -0
  59. data/lib/aws/ec2/security_group.rb +246 -0
  60. data/lib/aws/ec2/security_group/ip_permission.rb +70 -0
  61. data/lib/aws/ec2/security_group/ip_permission_collection.rb +59 -0
  62. data/lib/aws/ec2/security_group_collection.rb +132 -0
  63. data/lib/aws/ec2/snapshot.rb +138 -0
  64. data/lib/aws/ec2/snapshot_collection.rb +90 -0
  65. data/lib/aws/ec2/tag.rb +88 -0
  66. data/lib/aws/ec2/tag_collection.rb +114 -0
  67. data/lib/aws/ec2/tagged_collection.rb +48 -0
  68. data/lib/aws/ec2/tagged_item.rb +87 -0
  69. data/lib/aws/ec2/volume.rb +190 -0
  70. data/lib/aws/ec2/volume_collection.rb +95 -0
  71. data/lib/aws/errors.rb +129 -0
  72. data/lib/aws/http/builtin_handler.rb +69 -0
  73. data/lib/aws/http/curb_handler.rb +123 -0
  74. data/lib/aws/http/handler.rb +77 -0
  75. data/lib/aws/http/httparty_handler.rb +61 -0
  76. data/lib/aws/http/request.rb +136 -0
  77. data/lib/aws/http/request_param.rb +63 -0
  78. data/lib/aws/http/response.rb +75 -0
  79. data/lib/aws/ignore_result_element.rb +38 -0
  80. data/lib/aws/indifferent_hash.rb +86 -0
  81. data/lib/aws/inflection.rb +46 -0
  82. data/lib/aws/lazy_error_classes.rb +64 -0
  83. data/lib/aws/meta_utils.rb +43 -0
  84. data/lib/aws/model.rb +57 -0
  85. data/lib/aws/naming.rb +32 -0
  86. data/lib/aws/option_grammar.rb +544 -0
  87. data/lib/aws/policy.rb +912 -0
  88. data/lib/aws/rails.rb +209 -0
  89. data/lib/aws/record.rb +79 -0
  90. data/lib/aws/record/attribute.rb +94 -0
  91. data/lib/aws/record/attribute_macros.rb +288 -0
  92. data/lib/aws/record/attributes/boolean.rb +49 -0
  93. data/lib/aws/record/attributes/datetime.rb +86 -0
  94. data/lib/aws/record/attributes/float.rb +48 -0
  95. data/lib/aws/record/attributes/integer.rb +68 -0
  96. data/lib/aws/record/attributes/sortable_float.rb +60 -0
  97. data/lib/aws/record/attributes/sortable_integer.rb +95 -0
  98. data/lib/aws/record/attributes/string.rb +69 -0
  99. data/lib/aws/record/base.rb +728 -0
  100. data/lib/aws/record/conversion.rb +38 -0
  101. data/lib/aws/record/dirty_tracking.rb +286 -0
  102. data/lib/aws/record/errors.rb +153 -0
  103. data/lib/aws/record/exceptions.rb +48 -0
  104. data/lib/aws/record/finder_methods.rb +262 -0
  105. data/lib/aws/record/naming.rb +31 -0
  106. data/lib/aws/record/scope.rb +157 -0
  107. data/lib/aws/record/validations.rb +653 -0
  108. data/lib/aws/record/validator.rb +237 -0
  109. data/lib/aws/record/validators/acceptance.rb +51 -0
  110. data/lib/aws/record/validators/block.rb +38 -0
  111. data/lib/aws/record/validators/confirmation.rb +43 -0
  112. data/lib/aws/record/validators/count.rb +108 -0
  113. data/lib/aws/record/validators/exclusion.rb +43 -0
  114. data/lib/aws/record/validators/format.rb +57 -0
  115. data/lib/aws/record/validators/inclusion.rb +56 -0
  116. data/lib/aws/record/validators/length.rb +107 -0
  117. data/lib/aws/record/validators/numericality.rb +138 -0
  118. data/lib/aws/record/validators/presence.rb +45 -0
  119. data/lib/aws/resource_cache.rb +39 -0
  120. data/lib/aws/response.rb +113 -0
  121. data/lib/aws/response_cache.rb +50 -0
  122. data/lib/aws/s3.rb +109 -0
  123. data/lib/aws/s3/access_control_list.rb +252 -0
  124. data/lib/aws/s3/acl_object.rb +266 -0
  125. data/lib/aws/s3/bucket.rb +320 -0
  126. data/lib/aws/s3/bucket_collection.rb +122 -0
  127. data/lib/aws/s3/bucket_version_collection.rb +85 -0
  128. data/lib/aws/s3/client.rb +999 -0
  129. data/lib/aws/s3/client/xml.rb +190 -0
  130. data/lib/aws/s3/data_options.rb +99 -0
  131. data/lib/aws/s3/errors.rb +43 -0
  132. data/lib/aws/s3/multipart_upload.rb +318 -0
  133. data/lib/aws/s3/multipart_upload_collection.rb +78 -0
  134. data/lib/aws/s3/object_collection.rb +159 -0
  135. data/lib/aws/s3/object_metadata.rb +67 -0
  136. data/lib/aws/s3/object_upload_collection.rb +83 -0
  137. data/lib/aws/s3/object_version.rb +141 -0
  138. data/lib/aws/s3/object_version_collection.rb +78 -0
  139. data/lib/aws/s3/paginated_collection.rb +94 -0
  140. data/lib/aws/s3/policy.rb +76 -0
  141. data/lib/aws/s3/prefix_and_delimiter_collection.rb +56 -0
  142. data/lib/aws/s3/prefixed_collection.rb +84 -0
  143. data/lib/aws/s3/presigned_post.rb +504 -0
  144. data/lib/aws/s3/request.rb +198 -0
  145. data/lib/aws/s3/s3_object.rb +794 -0
  146. data/lib/aws/s3/tree.rb +116 -0
  147. data/lib/aws/s3/tree/branch_node.rb +71 -0
  148. data/lib/aws/s3/tree/child_collection.rb +108 -0
  149. data/lib/aws/s3/tree/leaf_node.rb +99 -0
  150. data/lib/aws/s3/tree/node.rb +22 -0
  151. data/lib/aws/s3/tree/parent.rb +90 -0
  152. data/lib/aws/s3/uploaded_part.rb +82 -0
  153. data/lib/aws/s3/uploaded_part_collection.rb +86 -0
  154. data/lib/aws/service_interface.rb +60 -0
  155. data/lib/aws/simple_db.rb +202 -0
  156. data/lib/aws/simple_db/attribute.rb +159 -0
  157. data/lib/aws/simple_db/attribute_collection.rb +227 -0
  158. data/lib/aws/simple_db/client.rb +52 -0
  159. data/lib/aws/simple_db/client/options.rb +34 -0
  160. data/lib/aws/simple_db/client/xml.rb +68 -0
  161. data/lib/aws/simple_db/consistent_read_option.rb +42 -0
  162. data/lib/aws/simple_db/delete_attributes.rb +64 -0
  163. data/lib/aws/simple_db/domain.rb +118 -0
  164. data/lib/aws/simple_db/domain_collection.rb +116 -0
  165. data/lib/aws/simple_db/domain_metadata.rb +112 -0
  166. data/lib/aws/simple_db/errors.rb +46 -0
  167. data/lib/aws/simple_db/expect_condition_option.rb +45 -0
  168. data/lib/aws/simple_db/item.rb +84 -0
  169. data/lib/aws/simple_db/item_collection.rb +594 -0
  170. data/lib/aws/simple_db/item_data.rb +70 -0
  171. data/lib/aws/simple_db/put_attributes.rb +62 -0
  172. data/lib/aws/simple_db/request.rb +27 -0
  173. data/lib/aws/simple_email_service.rb +373 -0
  174. data/lib/aws/simple_email_service/client.rb +39 -0
  175. data/lib/aws/simple_email_service/client/options.rb +24 -0
  176. data/lib/aws/simple_email_service/client/xml.rb +38 -0
  177. data/lib/aws/simple_email_service/email_address_collection.rb +66 -0
  178. data/lib/aws/simple_email_service/errors.rb +29 -0
  179. data/lib/aws/simple_email_service/quotas.rb +64 -0
  180. data/lib/aws/simple_email_service/request.rb +27 -0
  181. data/lib/aws/sns.rb +69 -0
  182. data/lib/aws/sns/client.rb +37 -0
  183. data/lib/aws/sns/client/options.rb +24 -0
  184. data/lib/aws/sns/client/xml.rb +38 -0
  185. data/lib/aws/sns/errors.rb +29 -0
  186. data/lib/aws/sns/policy.rb +49 -0
  187. data/lib/aws/sns/request.rb +27 -0
  188. data/lib/aws/sns/subscription.rb +100 -0
  189. data/lib/aws/sns/subscription_collection.rb +84 -0
  190. data/lib/aws/sns/topic.rb +384 -0
  191. data/lib/aws/sns/topic_collection.rb +70 -0
  192. data/lib/aws/sns/topic_subscription_collection.rb +58 -0
  193. data/lib/aws/sqs.rb +70 -0
  194. data/lib/aws/sqs/client.rb +38 -0
  195. data/lib/aws/sqs/client/xml.rb +36 -0
  196. data/lib/aws/sqs/errors.rb +33 -0
  197. data/lib/aws/sqs/policy.rb +50 -0
  198. data/lib/aws/sqs/queue.rb +507 -0
  199. data/lib/aws/sqs/queue_collection.rb +105 -0
  200. data/lib/aws/sqs/received_message.rb +184 -0
  201. data/lib/aws/sqs/received_sns_message.rb +112 -0
  202. data/lib/aws/sqs/request.rb +44 -0
  203. data/lib/aws/xml_grammar.rb +923 -0
  204. data/rails/init.rb +15 -0
  205. metadata +298 -0
@@ -0,0 +1,198 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/http/request'
15
+ require 'aws/base_client'
16
+ require 'uri'
17
+ require 'time'
18
+
19
+ module AWS
20
+ class S3
21
+
22
+ # @private
23
+ class Request < AWS::Http::Request
24
+
25
+ # @param [bucket] S3 bucket name
26
+ attr_accessor :bucket
27
+
28
+ # @param [String] S3 object key
29
+ attr_accessor :key
30
+
31
+ attr_accessor :body_stream
32
+
33
+ def metadata= metadata
34
+ Array(metadata).each do |name, value|
35
+ headers["x-amz-meta-#{name}"] = value
36
+ end
37
+ end
38
+
39
+ def canned_acl= acl
40
+ if acl.kind_of?(Symbol)
41
+ headers["x-amz-acl"] = acl.to_s.gsub("_", "-")
42
+ elsif acl
43
+ headers["x-amz-acl"] = acl
44
+ end
45
+ end
46
+
47
+ def storage_class= storage_class
48
+ if storage_class.kind_of?(Symbol)
49
+ headers["x-amz-storage-class"] = storage_class.to_s.upcase
50
+ elsif storage_class
51
+ headers["x-amz-storage-class"] = storage_class
52
+ end
53
+ end
54
+
55
+ def host
56
+ Client.dns_compatible_bucket_name?(bucket) ?
57
+ "#{bucket}.#{@host}" :
58
+ @host
59
+ end
60
+
61
+ def path
62
+ parts = []
63
+ unless bucket.nil? or Client.dns_compatible_bucket_name?(bucket)
64
+ parts << bucket
65
+ end
66
+ parts << key if key
67
+ "/#{parts.join('/')}"
68
+ end
69
+
70
+ def querystring
71
+ url_encoded_params
72
+ end
73
+
74
+ # @param [String, IO] The http request body. This can be a string or
75
+ # any object that responds to #read and #eof? (like an IO object).
76
+ def body= body
77
+ @body_stream = StringIO.new(body)
78
+ end
79
+
80
+ # @return [String, nil] The http request body.
81
+ def body
82
+ if @body_stream
83
+ string = @body_stream.read
84
+ @body_stream.rewind
85
+ string
86
+ else
87
+ nil
88
+ end
89
+ end
90
+
91
+ # From the S3 developer guide:
92
+ #
93
+ # StringToSign =
94
+ # HTTP-Verb + "\n" +
95
+ # content-md5 + "\n" +
96
+ # content-type + "\n" +
97
+ # date + "\n" +
98
+ # CanonicalizedAmzHeaders + CanonicalizedResource;
99
+ #
100
+ def string_to_sign
101
+ [
102
+ http_method,
103
+ headers.values_at('content-md5', 'content-type').join("\n"),
104
+ signing_string_date,
105
+ canonicalized_headers,
106
+ canonicalized_resource,
107
+ ].flatten.compact.join("\n")
108
+ end
109
+
110
+ def signing_string_date
111
+ # if a date is provided via x-amz-date then we should omit the
112
+ # Date header from the signing string (should appear as a blank line)
113
+ if headers.detect{|k,v| k.to_s =~ /^x-amz-date$/i }
114
+ ''
115
+ else
116
+ headers['date'] ||= Time.now.rfc822
117
+ end
118
+ end
119
+
120
+ # From the S3 developer guide
121
+ #
122
+ # CanonicalizedResource =
123
+ # [ "/" + Bucket ] +
124
+ # <HTTP-Request-URI, from the protocol name up to the querystring> +
125
+ # [ sub-resource, if present. e.g. "?acl", "?location",
126
+ # "?logging", or "?torrent"];
127
+ #
128
+ def canonicalized_resource
129
+
130
+ parts = []
131
+
132
+ # virtual hosted-style requests require the hostname to appear
133
+ # in the canonicalized resource prefixed by a forward slash.
134
+ if Client.dns_compatible_bucket_name?(bucket)
135
+ parts << "/#{bucket}"
136
+ end
137
+
138
+ # all requests require the portion of the un-decoded uri up to
139
+ # but not including the query string
140
+ parts << path
141
+
142
+ # lastly any sub resource querystring params need to be appened
143
+ # in lexigraphical ordered joined by '&' and prefixed by '?'
144
+ params = (sub_resource_params +
145
+ query_parameters_for_signature)
146
+ unless params.empty?
147
+ parts << '?'
148
+ parts << params.sort.collect{|p| p.to_s }.join('&')
149
+ end
150
+
151
+ parts.join
152
+ end
153
+
154
+ # CanonicalizedAmzHeaders
155
+ #
156
+ # See the developer guide for more information on how this element
157
+ # is generated.
158
+ #
159
+ def canonicalized_headers
160
+ x_amz = headers.select{|name, value| name.to_s =~ /^x-amz-/i }
161
+ x_amz = x_amz.collect{|name, value| [name.downcase, value] }
162
+ x_amz = x_amz.sort_by{|name, value| name }
163
+ x_amz = x_amz.collect{|name, value| "#{name}:#{value}" }.join("\n")
164
+ x_amz == '' ? nil : x_amz
165
+ end
166
+
167
+ def sub_resource_params
168
+ params.select{|p| self.class.sub_resources.include?(p.name) }
169
+ end
170
+
171
+ def query_parameters_for_signature
172
+ params.select { |p| self.class.query_parameters.include?(p.name) }
173
+ end
174
+
175
+ def add_authorization!(signer)
176
+ signature = URI.escape(signer.sign(string_to_sign, 'sha1'))
177
+ headers["authorization"] = "AWS #{signer.access_key_id}:#{signature}"
178
+ end
179
+
180
+ class << self
181
+
182
+ def sub_resources
183
+ %w(acl location logging notification partNumber policy
184
+ requestPayment torrent uploadId uploads versionId
185
+ versioning versions)
186
+ end
187
+
188
+ def query_parameters
189
+ %w(response-content-type response-content-language
190
+ response-expires response-cache-control
191
+ response-content-disposition response-content-encoding)
192
+ end
193
+
194
+ end
195
+
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,794 @@
1
+ # Copyright 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/model'
15
+ require 'aws/s3/request'
16
+ require 'aws/s3/object_metadata'
17
+ require 'aws/s3/multipart_upload'
18
+ require 'aws/s3/object_upload_collection'
19
+ require 'aws/s3/presigned_post'
20
+ require 'aws/s3/data_options'
21
+ require 'uri'
22
+
23
+ module AWS
24
+ class S3
25
+
26
+ # Represents an object in S3 identified by a key.
27
+ #
28
+ # object = bucket.objects["key-to-my-object"]
29
+ # object.key #=> 'key-to-my-object'
30
+ #
31
+ # See {ObjectCollection} for more information on finding objects.
32
+ #
33
+ # == Writing and Reading S3Objects
34
+ #
35
+ # obj = bucket.objects["my-text-object"]
36
+ #
37
+ # obj.write("MY TEXT")
38
+ # obj.read
39
+ # #=> "MY TEXT"
40
+ #
41
+ # obj.write(File.new("README.txt"))
42
+ # obj.read
43
+ # # should equal File.read("README.txt")
44
+ #
45
+ class S3Object
46
+
47
+ include Model
48
+ include DataOptions
49
+
50
+ # @param [Bucket] bucket The bucket this object belongs to.
51
+ # @param [String] key The object's key.
52
+ def initialize(bucket, key, opts = {})
53
+ super
54
+ @key = key
55
+ @bucket = bucket
56
+ end
57
+
58
+ # @return [String] The objects unique key
59
+ attr_reader :key
60
+
61
+ # @return [Bucket] The bucket this object is in.
62
+ attr_reader :bucket
63
+
64
+ # @private
65
+ def inspect
66
+ "<#{self.class}:#{bucket.name}/#{key}>"
67
+ end
68
+
69
+ # @return [Boolean] Returns true if the other object belongs to the
70
+ # same bucket and has the same key.
71
+ def ==(other)
72
+ other.kind_of?(S3Object) and other.bucket == bucket and other.key == key
73
+ end
74
+
75
+ alias_method :eql?, :==
76
+
77
+ # Performs a HEAD request against this object and returns an object
78
+ # with useful information about the object, including:
79
+ #
80
+ # * metadata (hash of user-supplied key-value pairs)
81
+ # * content_length (integer, number of bytes)
82
+ # * content_type (as sent to S3 when uploading the object)
83
+ # * etag (typically the object's MD5)
84
+ #
85
+ # @param [Hash] options
86
+ # @option options [String] :version_id Which version of this object
87
+ # to make a HEAD request against.
88
+ # @return [Response] A head object response with metatadata,
89
+ # content_length, content_type and etag.
90
+ def head options = {}
91
+ client.head_object(options.merge(
92
+ :bucket_name => bucket.name, :key => key))
93
+ end
94
+
95
+ # Returns the object's ETag.
96
+ #
97
+ # Generally the ETAG is the MD5 of the object. If the object was
98
+ # uploaded using multipart upload then this is the MD5 all of the
99
+ # upload-part-md5s.
100
+ #
101
+ # @return [String] Returns the object's ETag
102
+ def etag
103
+ head.etag
104
+ end
105
+
106
+ # @return [Integer] Size of the object in bytes.
107
+ def content_length
108
+ head.content_length
109
+ end
110
+
111
+ # @note S3 does not compute content-type. It reports the content-type
112
+ # as was reported during the file upload.
113
+ # @return [String] Returns the content type as reported by S3,
114
+ # defaults to an empty string when not provided during upload.
115
+ def content_type
116
+ head.content_type
117
+ end
118
+
119
+ # Deletes the object from its S3 bucket.
120
+ #
121
+ # @param [Hash] options
122
+ # @option [String] :version_id (nil) If present the specified version
123
+ # of this object will be deleted. Only works for buckets that have
124
+ # had versioning enabled.
125
+ # @return [Response]
126
+ def delete options = {}
127
+ options[:bucket_name] = bucket.name
128
+ options[:key] = key
129
+ client.delete_object(options)
130
+ end
131
+
132
+ # @option [String] :version_id (nil) If present the metadata object
133
+ # will be for the specified version.
134
+ # @return [ObjectMetadata] Returns an instance of ObjectMetadata
135
+ # representing the metadata for this object.
136
+ def metadata options = {}
137
+ options[:config] = config
138
+ ObjectMetadata.new(self, options)
139
+ end
140
+
141
+ # Returns a colletion representing all the object versions
142
+ # for this object.
143
+ #
144
+ # bucket.versioning_enabled? # => true
145
+ # version = bucket.objects["mykey"].versions.latest
146
+ #
147
+ # @return [ObjectVersionCollection]
148
+ def versions
149
+ ObjectVersionCollection.new(self)
150
+ end
151
+
152
+ # Writes data to the object in S3. This method will attempt
153
+ # to intelligently choose between uploading in one request and
154
+ # using {#multipart_upload}.
155
+ #
156
+ # Unless versioning is enabled, any data currently in S3 at {#key}
157
+ # will be replaced.
158
+ #
159
+ # You can pass +:data+ or +:file+ as the first argument or as
160
+ # options. Example usage:
161
+ #
162
+ # obj = s3.buckets.mybucket.objects.mykey
163
+ # obj.write("HELLO")
164
+ # obj.write(:data => "HELLO")
165
+ # obj.write(Pathname.new("myfile"))
166
+ # obj.write(:file => "myfile")
167
+ #
168
+ # # writes zero-length data
169
+ # obj.write(:metadata => { "avg-rating" => "5 stars" })
170
+ #
171
+ # @overload write(options = {})
172
+ # @overload write(data, options = {})
173
+ #
174
+ # @param data The data to upload (see the +:data+
175
+ # option).
176
+ #
177
+ # @param options [Hash] Additional upload options.
178
+ #
179
+ # @option options :data The data to upload. Valid values include:
180
+ # * A string
181
+ #
182
+ # * A Pathname object
183
+ #
184
+ # * Any object responding to +read+ and +eof?+; the object
185
+ # must support the following access methods:
186
+ # read # all at once
187
+ # read(length) until eof? # in chunks
188
+ #
189
+ # If you specify data this way, you must also include the
190
+ # +:content_length+ option.
191
+ #
192
+ # @option options [String] :file Can be specified instead of +:data+;
193
+ # its value specifies the path of a file to upload.
194
+ #
195
+ # @option options [Boolean] :single_request If this option is
196
+ # true, the method will always generate exactly one request
197
+ # to S3 regardless of how much data is being uploaded.
198
+ #
199
+ # @option options [Integer] :content_length If provided, this
200
+ # option must match the total number of bytes written to S3
201
+ # during the operation. This option is required if +:data+
202
+ # is an IO-like object without a +size+ method.
203
+ #
204
+ # @option options [Integer] :multipart_threshold Specifies the
205
+ # maximum size in bytes of a single-request upload. If the
206
+ # data being uploaded is larger than this threshold, it will
207
+ # be uploaded using {#multipart_upload}.
208
+ #
209
+ # @option options [Integer] :multipart_min_part_size The
210
+ # minimum size of a part if a multi-part upload is used. S3
211
+ # will reject non-final parts smaller than 5MB, and the
212
+ # default for this option is 5MB.
213
+ #
214
+ # @option options [Hash] :metadata A hash of metadata to be
215
+ # included with the object. These will be sent to S3 as
216
+ # headers prefixed with +x-amz-meta+.
217
+ #
218
+ # @option options [Symbol] :acl A canned access control
219
+ # policy. Valid values are:
220
+ # * +:private+
221
+ # * +:public_read+
222
+ # * +:public_read_write+
223
+ # * +:authenticated_read+
224
+ # * +:bucket_owner_read+
225
+ # * +:bucket_owner_full_control+
226
+ #
227
+ # @option options [Symbol] :storage_class Controls whether
228
+ # Reduced Redundancy Storage is enabled for the object.
229
+ # Valid values are +:standard+ (the default) or
230
+ # +:reduced_redundancy+.
231
+ #
232
+ # @option options :cache_control [String] Can be used to specify
233
+ # caching behavior. See
234
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
235
+ #
236
+ # @option options :content_disposition [String] Specifies
237
+ # presentational information for the object. See
238
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec19.5.1
239
+ #
240
+ # @option options :content_encoding [String] Specifies what
241
+ # content encodings have been applied to the object and thus
242
+ # what decoding mechanisms must be applied to obtain the
243
+ # media-type referenced by the +Content-Type+ header field.
244
+ # See
245
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
246
+ #
247
+ # @option options :content_type A standard MIME type
248
+ # describing the format of the object data.
249
+ #
250
+ # @return [S3Object, ObjectVersion] If the bucket has versioning
251
+ # enabled, returns the {ObjectVersion} representing the
252
+ # version that was uploaded. If versioning is disabled,
253
+ # returns self.
254
+ def write(options_or_data = nil, options = nil)
255
+
256
+ (data_options, put_options) =
257
+ compute_put_options(options_or_data, options)
258
+
259
+ if use_multipart?(data_options, put_options)
260
+ put_options.delete(:multipart_threshold)
261
+ multipart_upload(put_options) do |upload|
262
+ each_part(data_options, put_options) do |part|
263
+ upload.add_part(part)
264
+ end
265
+ end
266
+ else
267
+ opts = { :bucket_name => bucket.name, :key => key }
268
+ resp = client.put_object(opts.merge(put_options).merge(data_options))
269
+ if resp.version_id
270
+ ObjectVersion.new(self, resp.version_id)
271
+ else
272
+ self
273
+ end
274
+ end
275
+ end
276
+
277
+ # Performs a multipart upload. Use this if you have specific
278
+ # needs for how the upload is split into parts, or if you want
279
+ # to have more control over how the failure of an individual
280
+ # part upload is handled. Otherwise, {#write} is much simpler
281
+ # to use.
282
+ #
283
+ # @example Uploading an object in two parts
284
+ # bucket.objects.myobject.multipart_upload do |upload|
285
+ # upload.add_part("a" * 5242880)
286
+ # upload.add_part("b" * 2097152)
287
+ # end
288
+ #
289
+ # @example Uploading parts out of order
290
+ # bucket.objects.myobject.multipart_upload do |upload|
291
+ # upload.add_part("b" * 2097152, :part_number => 2)
292
+ # upload.add_part("a" * 5242880, :part_number => 1)
293
+ # end
294
+ #
295
+ # @example Aborting an upload after parts have been added
296
+ # bucket.objects.myobject.multipart_upload do |upload|
297
+ # upload.add_part("b" * 2097152, :part_number => 2)
298
+ # upload.abort
299
+ # end
300
+ #
301
+ # @example Starting an upload and completing it later by ID
302
+ # upload = bucket.objects.myobject.multipart_upload
303
+ # upload.add_part("a" * 5242880)
304
+ # upload.add_part("b" * 2097152)
305
+ # id = upload.id
306
+ #
307
+ # # later or in a different process
308
+ # upload = bucket.objects.myobject.multipart_uploads[id]
309
+ # upload.complete(:remote_parts)
310
+ #
311
+ # @yieldparam [MultipartUpload] upload A handle to the upload.
312
+ # {MultipartUpload#close} is called in an +ensure+ clause so
313
+ # that the upload will always be either completed or
314
+ # aborted.
315
+ #
316
+ # @param [Hash] options Options for the upload.
317
+ #
318
+ # @option options [Hash] :metadata A hash of metadata to be
319
+ # included with the object. These will be sent to S3 as
320
+ # headers prefixed with +x-amz-meta+.
321
+ #
322
+ # @option options [Symbol] :acl A canned access control
323
+ # policy. Valid values are:
324
+ # * +:private+
325
+ # * +:public_read+
326
+ # * +:public_read_write+
327
+ # * +:authenticated_read+
328
+ # * +:bucket_owner_read+
329
+ # * +:bucket_owner_full_control+
330
+ #
331
+ # @option options [Boolean] :reduced_redundancy If true,
332
+ # Reduced Redundancy Storage will be enabled for the
333
+ # uploaded object.
334
+ #
335
+ # @option options :cache_control [String] Can be used to specify
336
+ # caching behavior. See
337
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
338
+ #
339
+ # @option options :content_disposition [String] Specifies
340
+ # presentational information for the object. See
341
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec19.5.1
342
+ #
343
+ # @option options :content_encoding [String] Specifies what
344
+ # content encodings have been applied to the object and thus
345
+ # what decoding mechanisms must be applied to obtain the
346
+ # media-type referenced by the +Content-Type+ header field.
347
+ # See
348
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
349
+ #
350
+ # @option options :content_type A standard MIME type
351
+ # describing the format of the object data.
352
+ #
353
+ # @return [S3Object, ObjectVersion] If the bucket has versioning
354
+ # enabled, returns the {ObjectVersion} representing the
355
+ # version that was uploaded. If versioning is disabled,
356
+ # returns self.
357
+ def multipart_upload(options = {})
358
+ upload = multipart_uploads.create(options)
359
+
360
+ if block_given?
361
+ result = nil
362
+ begin
363
+ yield(upload)
364
+ ensure
365
+ result = upload.close
366
+ end
367
+ result
368
+ else
369
+ upload
370
+ end
371
+ end
372
+
373
+ # @example Abort any in-progress uploads for the object:
374
+ #
375
+ # object.multipart_uploads.each(&:abort)
376
+ #
377
+ # @return [ObjectUploadCollection] Returns an object representing the
378
+ # collection of uploads that are in progress for this object.
379
+ def multipart_uploads
380
+ ObjectUploadCollection.new(self)
381
+ end
382
+
383
+ # Copies data from one S3 object to another.
384
+ #
385
+ # S3 handles the copy so the clients does not need to fetch the data
386
+ # and upload it again. You can also change the storage class and
387
+ # metadata of the object when copying.
388
+ #
389
+ # @param [Mixed] source
390
+ # @param [Hash] options
391
+ # @option options [String] :bucket_name The name of the bucket
392
+ # the source object can be found in. Defaults to the current
393
+ # object's bucket.
394
+ # @option options [Bucket] :bucket The bucket the source object can
395
+ # be found in. Defaults to the current object's bucket.
396
+ # @option options [Hash] :metadata A hash of metadata to save with
397
+ # the copied object. When blank, the sources metadata is copied.
398
+ # @option options [Boolean] :reduced_redundancy (false) If true the
399
+ # object is stored with reduced redundancy in S3 for a lower cost.
400
+ # @option options [String] :version_id (nil) Causes the copy to
401
+ # read a specific version of the source object.
402
+ # @return [nil]
403
+ def copy_from source, options = {}
404
+
405
+ copy_opts = { :bucket_name => bucket.name, :key => key }
406
+
407
+ copy_opts[:copy_source] = case source
408
+ when S3Object
409
+ "#{source.bucket.name}/#{source.key}"
410
+ when ObjectVersion
411
+ copy_opts[:version_id] = source.version_id
412
+ "#{source.object.bucket.name}/#{source.object.key}"
413
+ else
414
+ case
415
+ when options[:bucket] then "#{options[:bucket].name}/#{source}"
416
+ when options[:bucket_name] then "#{options[:bucket_name]}/#{source}"
417
+ else "#{self.bucket.name}/#{source}"
418
+ end
419
+ end
420
+
421
+ if options[:metadata]
422
+ copy_opts[:metadata] = options[:metadata]
423
+ copy_opts[:metadata_directive] = 'REPLACE'
424
+ else
425
+ copy_opts[:metadata_directive] = 'COPY'
426
+ end
427
+
428
+ copy_opts[:version_id] = options[:version_id] if options[:version_id]
429
+
430
+ copy_opts[:storage_class] = 'REDUCED_REDUNDANCY' if
431
+ options[:reduced_redundancy]
432
+
433
+ client.copy_object(copy_opts)
434
+
435
+ nil
436
+
437
+ end
438
+
439
+ # Copies data from the current object to another object in S3.
440
+ #
441
+ # S3 handles the copy so the client does not need to fetch the data
442
+ # and upload it again. You can also change the storage class and
443
+ # metadata of the object when copying.
444
+ #
445
+ # @param [S3Object,String] target An S3Object, or a string key of
446
+ # and object to copy to.
447
+ # @param [Hash] options
448
+ # @option options [String] :bucket_name The name of the bucket
449
+ # the object should be copied into. Defaults to the current object's
450
+ # bucket.
451
+ # @option options [Bucket] :bucket The bucket the target object
452
+ # should be copied into. Defaults to the current object's bucket.
453
+ # @option options [Hash] :metadata A hash of metadata to save with
454
+ # the copied object. When blank, the sources metadata is copied.
455
+ # @option options [Boolean] :reduced_redundancy (false) If true the
456
+ # object is stored with reduced redundancy in S3 for a lower cost.
457
+ # @return (see #copy_from)
458
+ def copy_to target, options = {}
459
+
460
+ unless target.is_a?(S3Object)
461
+
462
+ bucket = case
463
+ when options[:bucket] then options[:bucket]
464
+ when options[:bucket_name]
465
+ Bucket.new(options[:bucket_name], :config => config)
466
+ else self.bucket
467
+ end
468
+
469
+ target = S3Object.new(bucket, target)
470
+ end
471
+
472
+ copy_opts = options.dup
473
+ copy_opts.delete(:bucket)
474
+ copy_opts.delete(:bucket_name)
475
+
476
+ target.copy_from(self, copy_opts)
477
+
478
+ end
479
+
480
+ # Fetches the object data from S3.
481
+ #
482
+ # @example Reading data as a string
483
+ # object.write('some data')
484
+ # object.read
485
+ # #=> 'some data'
486
+ #
487
+ # @param [Hash] options
488
+ # @option options [String] :version_id Reads data from a
489
+ # specific version of this object.
490
+ # @option options [Time] :if_unmodified_since Causes #read
491
+ # to return nil if the object was modified since the
492
+ # given time.
493
+ # @option options [Time] :if_modified_since Causes #read
494
+ # to return nil unless the object was modified since the
495
+ # given time.
496
+ # @option options [String] :if_match If specified, the method
497
+ # will return nil (and not fetch any data) unless the object ETag
498
+ # @option options [Range] :range A byte range to read data from
499
+ def read(options = {}, &blk)
500
+ options[:bucket_name] = bucket.name
501
+ options[:key] = key
502
+ client.get_object(options).data
503
+ end
504
+
505
+ # @private
506
+ module ACLProxy
507
+
508
+ attr_accessor :object
509
+
510
+ def change
511
+ yield(self)
512
+ object.acl = self
513
+ end
514
+
515
+ end
516
+
517
+ # Returns the object's access control list. This will be an
518
+ # instance of AccessControlList, plus an additional +change+
519
+ # method:
520
+ #
521
+ # object.acl.change do |acl|
522
+ # # remove any grants to someone other than the bucket owner
523
+ # owner_id = object.bucket.owner.id
524
+ # acl.grants.reject! do |g|
525
+ # g.grantee.canonical_user_id != owner_id
526
+ # end
527
+ # end
528
+ #
529
+ # Note that changing the ACL is not an atomic operation; it
530
+ # fetches the current ACL, yields it to the block, and then
531
+ # sets it again. Therefore, it's possible that you may
532
+ # overwrite a concurrent update to the ACL using this
533
+ # method.
534
+ #
535
+ # @return [AccessControlList]
536
+ #
537
+ def acl
538
+ acl = client.get_object_acl(
539
+ :bucket_name => bucket.name,
540
+ :key => key
541
+ ).acl
542
+ acl.extend ACLProxy
543
+ acl.object = self
544
+ acl
545
+ end
546
+
547
+ # Sets the object's access control list. +acl+ can be:
548
+ # * An XML policy as a string (which is passed to S3 uninterpreted)
549
+ # * An AccessControlList object
550
+ # * Any object that responds to +to_xml+
551
+ # * Any Hash that is acceptable as an argument to
552
+ # AccessControlList#initialize.
553
+ #
554
+ # @param (see Bucket#acl=)
555
+ # @return [Response]
556
+ #
557
+ def acl=(acl)
558
+ client.set_object_acl(
559
+ :bucket_name => bucket.name,
560
+ :key => key,
561
+ :acl => acl)
562
+ end
563
+
564
+ # @private
565
+ REQUEST_PARAMETERS = Request.query_parameters.map do |p|
566
+ p.tr("-","_").to_sym
567
+ end
568
+
569
+ # Generates a presigned URL for an operation on this object.
570
+ # This URL can be used by a regular HTTP client to perform the
571
+ # desired operation without credentials and without changing
572
+ # the permissions of the object.
573
+ #
574
+ # @example Generate a url to read an object
575
+ # bucket.objects.myobject.url_for(:read)
576
+ #
577
+ # @example Generate a url to delete an object
578
+ # bucket.objects.myobject.url_for(:delete)
579
+ #
580
+ # @example Override response headers for reading an object
581
+ # object = bucket.objects.myobject
582
+ # url = object.url_for(:read, :response_content_type => "application/json")
583
+ #
584
+ # @example Generate a url that expires in 10 minutes
585
+ # bucket.objects.myobject.url_for(:read, :expires => 10*60)
586
+ #
587
+ # @param [Symbol, String] method The HTTP verb or object
588
+ # method for which the returned URL will be valid. Valid
589
+ # values:
590
+ #
591
+ # * +:get+ or +:read+
592
+ # * +:put+ or +:write+
593
+ # * +:delete+
594
+ #
595
+ # @param [Hash] options Additional options for generating the URL.
596
+ #
597
+ # @option options :expires Sets the expiration time of the
598
+ # URL; after this time S3 will return an error if the URL is
599
+ # used. This can be an integer (to specify the number of
600
+ # seconds after the current time), a string (which is parsed
601
+ # as a date using Time#parse), a Time, or a DateTime object.
602
+ # This option defaults to one hour after the current time.
603
+ #
604
+ # @option options [String] :secure Whether to generate a
605
+ # secure (HTTPS) URL or a plain HTTP url.
606
+ #
607
+ # @option options [String] :response_content_type Sets the
608
+ # Content-Type header of the response when performing an
609
+ # HTTP GET on the returned URL.
610
+ #
611
+ # @option options [String] :response_content_language Sets the
612
+ # Content-Language header of the response when performing an
613
+ # HTTP GET on the returned URL.
614
+ #
615
+ # @option options [String] :response_expires Sets the Expires
616
+ # header of the response when performing an HTTP GET on the
617
+ # returned URL.
618
+ #
619
+ # @option options [String] :response_cache_control Sets the
620
+ # Cache-Control header of the response when performing an
621
+ # HTTP GET on the returned URL.
622
+ #
623
+ # @option options [String] :response_content_disposition Sets
624
+ # the Content-Disposition header of the response when
625
+ # performing an HTTP GET on the returned URL.
626
+ #
627
+ # @option options [String] :response_content_encoding Sets the
628
+ # Content-Encoding header of the response when performing an
629
+ # HTTP GET on the returned URL.
630
+ # @return [URI::HTTP, URI::HTTPS]
631
+ def url_for(method, options = {})
632
+ req = request_for_signing(options)
633
+
634
+ method = http_method(method)
635
+ expires = expiration_timestamp(options[:expires])
636
+ req.add_param("AWSAccessKeyId", config.signer.access_key_id)
637
+ req.add_param("Signature", signature(method, expires, req))
638
+ req.add_param("Expires", expires)
639
+
640
+ build_uri(options[:secure] != false, req)
641
+ end
642
+
643
+ # Generates a public (not authenticated) URL for the object.
644
+ #
645
+ # @param [Hash] options Options for generating the URL.
646
+ #
647
+ # @option options [Boolean] :secure Whether to generate a
648
+ # secure (HTTPS) URL or a plain HTTP url.
649
+ # @return [URI::HTTP, URI::HTTPS]
650
+ def public_url(options = {})
651
+ req = request_for_signing(options)
652
+ build_uri(options[:secure] != false, req)
653
+ end
654
+
655
+ # Generates fields for a presigned POST to this object. This
656
+ # method adds a constraint that the key must match the key of
657
+ # this object. All options are sent to the PresignedPost
658
+ # constructor.
659
+ #
660
+ # @see PresignedPost
661
+ # @return [PresignedPost]
662
+ def presigned_post(options = {})
663
+ PresignedPost.new(bucket, options.merge(:key => key))
664
+ end
665
+
666
+ # @private
667
+ private
668
+ def build_uri(secure, request)
669
+ uri_class = secure ? URI::HTTPS : URI::HTTP
670
+ uri_class.build(:host => request.host,
671
+ :path => request.path,
672
+ :query => request.querystring)
673
+ end
674
+
675
+ # @private
676
+ private
677
+ def signature(method, expires, request)
678
+ string_to_sign = [method,
679
+ "", "",
680
+ expires,
681
+ request.canonicalized_resource].join("\n")
682
+ config.signer.sign(string_to_sign, "sha1")
683
+ end
684
+
685
+ # @private
686
+ private
687
+ def expiration_timestamp(input)
688
+ case input
689
+ when Time
690
+ expires = input.to_i
691
+ when DateTime
692
+ expires = Time.parse(input.to_s).to_i
693
+ when Integer
694
+ expires = (Time.now + input).to_i
695
+ when String
696
+ expires = Time.parse(input).to_i
697
+ else
698
+ expires = (Time.now + 60*60).to_i
699
+ end
700
+ end
701
+
702
+ # @private
703
+ private
704
+ def http_method(input)
705
+ symbol = case input
706
+ when :read then :get
707
+ when :write then :put
708
+ else
709
+ input
710
+ end
711
+ symbol.to_s.upcase
712
+ end
713
+
714
+ # @private
715
+ private
716
+ def request_for_signing(options)
717
+ req = Request.new
718
+
719
+ req.bucket = bucket.name
720
+ req.key = key
721
+ req.host = config.s3_endpoint
722
+
723
+ REQUEST_PARAMETERS.each do |param|
724
+ req.add_param(param.to_s.tr("_","-"),
725
+ options[param]) if options.key?(param)
726
+ end
727
+
728
+ req
729
+ end
730
+
731
+ # @private
732
+ private
733
+ def compute_put_options(options_or_data, options)
734
+ put_opts = {}
735
+
736
+ if options
737
+
738
+ raise ArgumentError.new("Object data passed twice (argument and file path)") if
739
+ options[:file]
740
+
741
+ raise ArgumentError.new("Object data passed twice (argument and option)") if
742
+ options[:data]
743
+
744
+ [{ :data => options_or_data }, options]
745
+ elsif options_or_data.kind_of?(Hash)
746
+ if file = options_or_data[:file]
747
+ data_options = { :file => file }
748
+ else
749
+ data_options = { :data => options_or_data[:data] || "" }
750
+ end
751
+
752
+ [data_options, options_or_data]
753
+ else
754
+ [{ :data => options_or_data || "" }, {}]
755
+ end
756
+ end
757
+
758
+ # @private
759
+ private
760
+ def use_multipart?(data_options, options)
761
+ threshold = options[:multipart_threshold] ||
762
+ config.s3_multipart_threshold
763
+
764
+ !options[:single_request] and
765
+ size = content_length_from(data_options.merge(options)) and
766
+ size > threshold
767
+ end
768
+
769
+ # @private
770
+ private
771
+ def each_part(data_options, options)
772
+ total_size = content_length_from(data_options.merge(options))
773
+ part_size = optimal_part_size(total_size, options)
774
+ stream = data_stream_from(data_options.merge(options))
775
+ while !stream.eof?
776
+ yield(stream.read(part_size))
777
+ end
778
+ end
779
+
780
+ # @private
781
+ private
782
+ def optimal_part_size(total_size, options)
783
+ maximum_parts = config.s3_multipart_max_parts
784
+ min_size = options[:multipart_min_part_size] ||
785
+ config.s3_multipart_min_part_size
786
+
787
+ [(total_size.to_f / maximum_parts.to_f).ceil,
788
+ min_size].max.to_i
789
+ end
790
+
791
+ end
792
+
793
+ end
794
+ end