gcloud 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,7 +22,16 @@ module Gcloud
22
22
  ##
23
23
  # = File
24
24
  #
25
- # Represents the File/Object that belong to a Bucket.
25
+ # Represents a File
26
+ # ({Object}[https://cloud.google.com/storage/docs/json_api/v1/objects]) that
27
+ # belongs to a Bucket. Files (Objects) are
28
+ # the individual pieces of data that you store in Google Cloud Storage. A
29
+ # file can be up to 5 TB in size. Files have two components:
30
+ # data and metadata. The data component is the data from an external file or
31
+ # other data source that you want to store in Google Cloud Storage. The
32
+ # metadata component is a collection of name-value pairs that describe
33
+ # various qualities of the data. For more information, see {Concepts and
34
+ # Techniques}[https://cloud.google.com/storage/docs/concepts-techniques].
26
35
  #
27
36
  # require "gcloud"
28
37
  #
@@ -92,17 +101,29 @@ module Gcloud
92
101
  end
93
102
 
94
103
  ##
95
- # The url to the file.
96
- def url
104
+ # A URL that can be used to access the file using the REST API.
105
+ def api_url
97
106
  @gapi["selfLink"]
98
107
  end
99
108
 
109
+ ##
110
+ # A URL that can be used to download the file using the REST API.
111
+ def media_url
112
+ @gapi["mediaLink"]
113
+ end
114
+
100
115
  ##
101
116
  # Content-Length of the data in bytes.
102
117
  def size
103
118
  @gapi["size"]
104
119
  end
105
120
 
121
+ ##
122
+ # Creation time of the file.
123
+ def created_at
124
+ @gapi["timeCreated"]
125
+ end
126
+
106
127
  ##
107
128
  # The creation or modification time of the file.
108
129
  # For buckets with versioning enabled, changing an object's
@@ -118,8 +139,9 @@ module Gcloud
118
139
  end
119
140
 
120
141
  ##
121
- # CRC32c checksum, as described in RFC 4960, Appendix B;
122
- # encoded using base64.
142
+ # The CRC32c checksum of the data, as described in
143
+ # {RFC 4960, Appendix B}[http://tools.ietf.org/html/rfc4960#appendix-B].
144
+ # Encoded using base64 in big-endian byte order.
123
145
  def crc32c
124
146
  @gapi["crc32c"]
125
147
  end
@@ -130,6 +152,130 @@ module Gcloud
130
152
  @gapi["etag"]
131
153
  end
132
154
 
155
+ ##
156
+ # The {Cache-Control}[https://tools.ietf.org/html/rfc7234#section-5.2]
157
+ # directive for the file data.
158
+ def cache_control
159
+ @gapi["cacheControl"]
160
+ end
161
+
162
+ ##
163
+ # Updates the
164
+ # {Cache-Control}[https://tools.ietf.org/html/rfc7234#section-5.2]
165
+ # directive for the file data.
166
+ def cache_control= cache_control
167
+ patch_gapi! cache_control: cache_control
168
+ end
169
+
170
+ ##
171
+ # The {Content-Disposition}[https://tools.ietf.org/html/rfc6266] of the
172
+ # file data.
173
+ def content_disposition
174
+ @gapi["contentDisposition"]
175
+ end
176
+
177
+ ##
178
+ # Updates the {Content-Disposition}[https://tools.ietf.org/html/rfc6266]
179
+ # of the file data.
180
+ def content_disposition= content_disposition
181
+ patch_gapi! content_disposition: content_disposition
182
+ end
183
+
184
+ ##
185
+ # The {Content-Encoding
186
+ # }[https://tools.ietf.org/html/rfc7231#section-3.1.2.2] of the file data.
187
+ def content_encoding
188
+ @gapi["contentEncoding"]
189
+ end
190
+
191
+ ##
192
+ # Updates the {Content-Encoding
193
+ # }[https://tools.ietf.org/html/rfc7231#section-3.1.2.2] of the file data.
194
+ def content_encoding= content_encoding
195
+ patch_gapi! content_encoding: content_encoding
196
+ end
197
+
198
+ ##
199
+ # The {Content-Language}[http://tools.ietf.org/html/bcp47] of the file
200
+ # data.
201
+ def content_language
202
+ @gapi["contentLanguage"]
203
+ end
204
+
205
+ ##
206
+ # Updates the {Content-Language}[http://tools.ietf.org/html/bcp47] of the
207
+ # file data.
208
+ def content_language= content_language
209
+ patch_gapi! content_language: content_language
210
+ end
211
+
212
+ ##
213
+ # The {Content-Type}[https://tools.ietf.org/html/rfc2616#section-14.17] of
214
+ # the file data.
215
+ def content_type
216
+ @gapi["contentType"]
217
+ end
218
+
219
+ ##
220
+ # Updates the
221
+ # {Content-Type}[https://tools.ietf.org/html/rfc2616#section-14.17] of the
222
+ # file data.
223
+ def content_type= content_type
224
+ patch_gapi! content_type: content_type
225
+ end
226
+
227
+ ##
228
+ # A hash of custom, user-provided web-safe keys and arbitrary string
229
+ # values that will returned with requests for the file as "x-goog-meta-"
230
+ # response headers.
231
+ def metadata
232
+ m = @gapi["metadata"]
233
+ m = m.to_hash if m.respond_to? :to_hash
234
+ m.freeze
235
+ end
236
+
237
+ ##
238
+ # Updates the hash of custom, user-provided web-safe keys and arbitrary
239
+ # string values that will returned with requests for the file as
240
+ # "x-goog-meta-" response headers.
241
+ def metadata= metadata
242
+ patch_gapi! metadata: metadata
243
+ end
244
+
245
+ ##
246
+ # Updates the file with changes made in the given block in a single
247
+ # PATCH request. The following attributes may be set: #cache_control=,
248
+ # #content_disposition=, #content_encoding=, #content_language=,
249
+ # #content_type=, and #metadata=. The #metadata hash accessible in the
250
+ # block is completely mutable and will be included in the request.
251
+ #
252
+ # === Examples
253
+ #
254
+ # require "gcloud"
255
+ #
256
+ # gcloud = Gcloud.new
257
+ # storage = gcloud.storage
258
+ #
259
+ # bucket = storage.bucket "my-bucket"
260
+ #
261
+ # file = bucket.file "path/to/my-file.ext"
262
+ #
263
+ # file.update do |f|
264
+ # f.cache_control = "private, max-age=0, no-cache"
265
+ # f.content_disposition = "inline; filename=filename.ext"
266
+ # f.content_encoding = "deflate"
267
+ # f.content_language = "de"
268
+ # f.content_type = "application/json"
269
+ # f.metadata["player"] = "Bob"
270
+ # f.metadata["score"] = "10"
271
+ # end
272
+ #
273
+ def update
274
+ updater = Updater.new metadata
275
+ yield updater
276
+ patch_gapi! updater.updates unless updater.updates.empty?
277
+ end
278
+
133
279
  ##
134
280
  # Download the file's contents to a local file.
135
281
  #
@@ -229,6 +375,9 @@ module Gcloud
229
375
  # path to copy the file to in the given bucket. (+String+)
230
376
  # +options+::
231
377
  # An optional Hash for controlling additional behavior. (+Hash+)
378
+ # <code>options[:generation]</code>::
379
+ # Select a specific revision of the file to copy. The default is the
380
+ # latest version. (+Integer+)
232
381
  # <code>options[:acl]</code>::
233
382
  # A predefined set of access controls to apply to new file.
234
383
  # (+String+)
@@ -278,6 +427,11 @@ module Gcloud
278
427
  # file.copy "new-destination-bucket",
279
428
  # "path/to/destination/file.ext"
280
429
  #
430
+ # The file can also be copied by specifying a generation:
431
+ #
432
+ # file.copy "copy/of/previous/generation/file.ext",
433
+ # generation: 123456
434
+ #
281
435
  def copy dest_bucket_or_path, dest_path = nil, options = {}
282
436
  ensure_connection!
283
437
  dest_bucket, dest_path, options = fix_copy_args dest_bucket_or_path,
@@ -293,7 +447,7 @@ module Gcloud
293
447
  end
294
448
 
295
449
  ##
296
- # Permenently deletes the file.
450
+ # Permanently deletes the file.
297
451
  #
298
452
  # === Returns
299
453
  #
@@ -321,6 +475,50 @@ module Gcloud
321
475
  end
322
476
  end
323
477
 
478
+ ##
479
+ # Public URL to access the file. If the file is not public, requests to
480
+ # the URL will return an error. (See File::Acl#public! and
481
+ # Bucket::DefaultAcl#public!) For more information, read [Accessing Public
482
+ # Data]{https://cloud.google.com/storage/docs/access-public-data}.
483
+ #
484
+ # To share a file that is not public see #signed_url.
485
+ #
486
+ # === Parameters
487
+ #
488
+ # +options+::
489
+ # An optional Hash for controlling additional behavior. (+Hash+)
490
+ # <code>options[:protocol]</code>::
491
+ # The protocol to use for the URL. Default is +HTTPS+. (+String+)
492
+ #
493
+ # === Examples
494
+ #
495
+ # require "gcloud"
496
+ #
497
+ # gcloud = Gcloud.new
498
+ # storage = gcloud.storage
499
+ #
500
+ # bucket = storage.bucket "my-todo-app"
501
+ # file = bucket.file "avatars/heidi/400x400.png"
502
+ # public_url = file.public_url
503
+ #
504
+ # To generate the URL with a protocol other than HTTPS, use the +protocol+
505
+ # option:
506
+ #
507
+ # require "gcloud"
508
+ #
509
+ # gcloud = Gcloud.new
510
+ # storage = gcloud.storage
511
+ #
512
+ # bucket = storage.bucket "my-todo-app"
513
+ # file = bucket.file "avatars/heidi/400x400.png"
514
+ # public_url = file.public_url protocol: "http"
515
+ #
516
+ def public_url options = {}
517
+ protocol = options[:protocol] || :https
518
+ "#{protocol}://storage.googleapis.com/#{bucket}/#{name}"
519
+ end
520
+ alias_method :url, :public_url
521
+
324
522
  ##
325
523
  # Access without authentication can be granted to a File for a specified
326
524
  # period of time. This URL uses a cryptographic signature
@@ -500,6 +698,16 @@ module Gcloud
500
698
  fail "Must have active connection" unless connection
501
699
  end
502
700
 
701
+ def patch_gapi! options = {}
702
+ ensure_connection!
703
+ resp = connection.patch_file bucket, name, options
704
+ if resp.success?
705
+ @gapi = resp.data
706
+ else
707
+ fail ApiError.from_response(resp)
708
+ end
709
+ end
710
+
503
711
  def fix_copy_args dest_bucket, dest_path, options = {}
504
712
  if dest_path.respond_to?(:to_hash) && options.empty?
505
713
  options, dest_path = dest_path, nil
@@ -587,6 +795,38 @@ module Gcloud
587
795
  "&Signature=#{CGI.escape signature}"
588
796
  end
589
797
  end
798
+
799
+ ##
800
+ # Yielded to a block to accumulate changes for a patch request.
801
+ class Updater
802
+ attr_reader :updates
803
+ ##
804
+ # Create an Updater object.
805
+ def initialize metadata
806
+ @metadata = if metadata.nil?
807
+ {}
808
+ else
809
+ metadata.dup
810
+ end
811
+ @updates = {}
812
+ end
813
+
814
+ ATTRS = [:cache_control, :content_disposition, :content_encoding,
815
+ :content_language, :content_type, :metadata]
816
+
817
+ ATTRS.each do |attr|
818
+ define_method "#{attr}=" do |arg|
819
+ updates[attr] = arg
820
+ end
821
+ end
822
+
823
+ ##
824
+ # Return metadata for mutation. Also adds metadata to @updates so that
825
+ # it is included in the patch request.
826
+ def metadata
827
+ updates[:metadata] ||= @metadata
828
+ end
829
+ end
590
830
  end
591
831
  end
592
832
  end
@@ -352,7 +352,7 @@ module Gcloud
352
352
  end
353
353
 
354
354
  ##
355
- # Permenently deletes the entity from the file's access control list.
355
+ # Permanently deletes the entity from the file's access control list.
356
356
  #
357
357
  # === Parameters
358
358
  #
@@ -536,11 +536,20 @@ module Gcloud
536
536
 
537
537
  protected
538
538
 
539
+ def clear!
540
+ @owners = nil
541
+ @writers = nil
542
+ @readers = nil
543
+ self
544
+ end
545
+
539
546
  def update_predefined_acl! acl_role
540
547
  resp = @connection.patch_file @bucket, @file,
541
- acl: acl_role
548
+ predefined_acl: acl_role,
549
+ acl: []
542
550
 
543
- resp.success?
551
+ return clear! if resp.success?
552
+ fail Gcloud::Storage::ApiError.from_response(resp)
544
553
  end
545
554
 
546
555
  def entities_from_acls acls, role
@@ -18,6 +18,7 @@ require "gcloud/storage/errors"
18
18
  require "gcloud/storage/connection"
19
19
  require "gcloud/storage/credentials"
20
20
  require "gcloud/storage/bucket"
21
+ require "gcloud/storage/bucket/cors"
21
22
  require "gcloud/storage/file"
22
23
 
23
24
  module Gcloud
@@ -190,7 +191,13 @@ module Gcloud
190
191
  alias_method :find_bucket, :bucket
191
192
 
192
193
  ##
193
- # Creates a new bucket.
194
+ # Creates a new bucket with optional attributes. Also accepts a block for
195
+ # defining the CORS configuration for a static website served from the
196
+ # bucket. See Bucket::Cors for details. For more information about
197
+ # configuring buckets as static websites, see {How to Host a Static
198
+ # Website }[https://cloud.google.com/storage/docs/website-configuration].
199
+ # For more information about CORS, see {Cross-Origin Resource Sharing
200
+ # (CORS)}[https://cloud.google.com/storage/docs/cross-origin].
194
201
  #
195
202
  # === Parameters
196
203
  #
@@ -198,9 +205,53 @@ module Gcloud
198
205
  # Name of a bucket. (+String+)
199
206
  # +options+::
200
207
  # An optional Hash for controlling additional behavior. (+Hash+)
208
+ # <code>options[:cors]</code>::
209
+ # The CORS rules for the bucket. Accepts an array of hashes containing
210
+ # the attributes specified for the {resource description of
211
+ # cors}[https://cloud.google.com/storage/docs/json_api/v1/buckets#cors].
212
+ # <code>options[:location]</code>::
213
+ # The location of the bucket. Object data for objects in the bucket
214
+ # resides in physical storage within this region. Possible values
215
+ # include +ASIA+, +EU+, and +US+.(See the {developer's
216
+ # guide}[https://cloud.google.com/storage/docs/bucket-locations] for the
217
+ # authoritative list. The default value is +US+. (+String+)
218
+ # <code>options[:logging_bucket]</code>::
219
+ # The destination bucket for the bucket's logs. For more information,
220
+ # see {Access
221
+ # Logs}[https://cloud.google.com/storage/docs/access-logs]. (+String+)
222
+ # <code>options[:logging_prefix]</code>::
223
+ # The prefix used to create log object names for the bucket. It can be
224
+ # at most 900 characters and must be a {valid object
225
+ # name}[https://cloud.google.com/storage/docs/bucket-naming#objectnames]
226
+ # . By default, the object prefix is the name
227
+ # of the bucket for which the logs are enabled. For more information,
228
+ # see {Access Logs}[https://cloud.google.com/storage/docs/access-logs].
229
+ # (+String+)
201
230
  # <code>options[:retries]</code>::
202
231
  # The number of times the API call should be retried.
203
232
  # Default is Gcloud::Backoff.retries. (+Integer+)
233
+ # <code>options[:storage_class]</code>::
234
+ # Defines how objects in the bucket are stored and determines the SLA
235
+ # and the cost of storage. Values include +:standard+, +:nearline+, and
236
+ # +:dra+ (Durable Reduced Availability), as well as the strings returned
237
+ # by Bucket#storage_class. For more information, see {Storage
238
+ # Classes}[https://cloud.google.com/storage/docs/storage-classes].
239
+ # The default value is +:standard+. (+Symbol+ or +String+)
240
+ # <code>options[:versioning]</code>::
241
+ # Whether {Object
242
+ # Versioning}[https://cloud.google.com/storage/docs/object-versioning]
243
+ # is to be enabled for the bucket. The default value is +false+.
244
+ # (+Boolean+)
245
+ # <code>options[:website_main]</code>::
246
+ # The index page returned from a static website served from the bucket
247
+ # when a site visitor requests the top level directory. For more
248
+ # information, see {How to Host a Static Website
249
+ # }[https://cloud.google.com/storage/docs/website-configuration#step4].
250
+ # <code>options[:website_404]</code>::
251
+ # The page returned from a static website served from the bucket when a
252
+ # site visitor requests a resource that does not exist. For more
253
+ # information, see {How to Host a Static Website
254
+ # }[https://cloud.google.com/storage/docs/website-configuration#step4].
204
255
  #
205
256
  # === Returns
206
257
  #
@@ -226,12 +277,45 @@ module Gcloud
226
277
  #
227
278
  # bucket = storage.create_bucket "my-bucket", retries: 5
228
279
  #
280
+ # You can pass {website
281
+ # settings}[https://cloud.google.com/storage/docs/website-configuration]
282
+ # for the bucket, including a block that defines CORS rule. See
283
+ # Bucket::Cors for details.
284
+ #
285
+ # require "gcloud"
286
+ #
287
+ # gcloud = Gcloud.new
288
+ # storage = gcloud.storage
289
+ #
290
+ # options = {
291
+ # website_main: "index.html"
292
+ # website_404: "not_found.html"
293
+ # }
294
+ # bucket = storage.create_bucket "my-bucket", options do |c|
295
+ # c.add_rule ["http://example.org", "https://example.org"],
296
+ # "*",
297
+ # response_headers: ["X-My-Custom-Header"],
298
+ # max_age: 300
299
+ # end
300
+ #
229
301
  def create_bucket bucket_name, options = {}
230
- options[:acl] = Bucket::Acl.predefined_rule_for options[:acl]
231
- default_acl = options[:default_acl]
232
- default_acl = Bucket::DefaultAcl.predefined_rule_for default_acl
233
- options[:default_acl] = default_acl
302
+ options[:acl] = acl_rule options[:acl]
303
+ options[:default_acl] = acl_rule options[:default_acl]
304
+ if block_given?
305
+ cors_builder = Bucket::Cors.new
306
+ yield cors_builder
307
+ options[:cors] = cors_builder if cors_builder.changed?
308
+ end
309
+ insert_bucket bucket_name, options
310
+ end
311
+
312
+ protected
313
+
314
+ def acl_rule option_name
315
+ Bucket::Acl.predefined_rule_for option_name
316
+ end
234
317
 
318
+ def insert_bucket bucket_name, options
235
319
  resp = connection.insert_bucket bucket_name, options
236
320
  if resp.success?
237
321
  Bucket.from_gapi resp.data, connection