aws-sdk 1.0.0

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 (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