google-cloud-storage 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,783 @@
1
+ # Copyright 2014 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/storage/bucket/acl"
17
+ require "google/cloud/storage/bucket/list"
18
+ require "google/cloud/storage/bucket/cors"
19
+ require "google/cloud/storage/file"
20
+ require "pathname"
21
+
22
+ module Google
23
+ module Cloud
24
+ module Storage
25
+ ##
26
+ # # Bucket
27
+ #
28
+ # Represents a Storage bucket. Belongs to a Project and has many Files.
29
+ #
30
+ # @example
31
+ # require "google/cloud"
32
+ #
33
+ # gcloud = Google::Cloud.new
34
+ # storage = gcloud.storage
35
+ #
36
+ # bucket = storage.bucket "my-bucket"
37
+ # file = bucket.file "path/to/my-file.ext"
38
+ #
39
+ class Bucket
40
+ ##
41
+ # @private The Service object.
42
+ attr_accessor :service
43
+
44
+ ##
45
+ # @private The Google API Client object.
46
+ attr_accessor :gapi
47
+
48
+ ##
49
+ # @private Create an empty Bucket object.
50
+ def initialize
51
+ @service = nil
52
+ @gapi = Google::Apis::StorageV1::Bucket.new
53
+ end
54
+
55
+ ##
56
+ # The kind of item this is.
57
+ # For buckets, this is always `storage#bucket`.
58
+ def kind
59
+ @gapi.kind
60
+ end
61
+
62
+ ##
63
+ # The ID of the bucket.
64
+ def id
65
+ @gapi.id
66
+ end
67
+
68
+ ##
69
+ # The name of the bucket.
70
+ def name
71
+ @gapi.name
72
+ end
73
+
74
+ ##
75
+ # A URL that can be used to access the bucket using the REST API.
76
+ def api_url
77
+ @gapi.self_link
78
+ end
79
+
80
+ ##
81
+ # Creation time of the bucket.
82
+ def created_at
83
+ @gapi.time_created
84
+ end
85
+
86
+ ##
87
+ # Returns the current CORS configuration for a static website served
88
+ # from the bucket.
89
+ #
90
+ # The return value is a frozen (unmodifiable) array of hashes containing
91
+ # the attributes specified for the Bucket resource field
92
+ # [cors](https://cloud.google.com/storage/docs/json_api/v1/buckets#cors).
93
+ #
94
+ # This method also accepts a block for updating the bucket's CORS rules.
95
+ # See {Bucket::Cors} for details.
96
+ #
97
+ # @see https://cloud.google.com/storage/docs/cross-origin Cross-Origin
98
+ # Resource Sharing (CORS)
99
+ #
100
+ # @yield [cors] a block for setting CORS rules
101
+ # @yieldparam [Bucket::Cors] cors the object accepting CORS rules
102
+ #
103
+ # @example Retrieving the bucket's CORS rules.
104
+ # require "google/cloud"
105
+ #
106
+ # gcloud = Google::Cloud.new
107
+ # storage = gcloud.storage
108
+ #
109
+ # bucket = storage.bucket "my-todo-app"
110
+ # bucket.cors #=> [{"origin"=>["http://example.org"],
111
+ # # "method"=>["GET","POST","DELETE"],
112
+ # # "responseHeader"=>["X-My-Custom-Header"],
113
+ # # "maxAgeSeconds"=>3600}]
114
+ #
115
+ # @example Updating the bucket's CORS rules inside a block.
116
+ # require "google/cloud"
117
+ #
118
+ # gcloud = Google::Cloud.new
119
+ # storage = gcloud.storage
120
+ # bucket = storage.bucket "my-todo-app"
121
+ #
122
+ # bucket.update do |b|
123
+ # b.cors do |c|
124
+ # c.add_rule ["http://example.org", "https://example.org"],
125
+ # "*",
126
+ # response_headers: ["X-My-Custom-Header"],
127
+ # max_age: 3600
128
+ # end
129
+ # end
130
+ #
131
+ def cors
132
+ cors_builder = Bucket::Cors.from_gapi @gapi.cors_configurations
133
+ if block_given?
134
+ yield cors_builder
135
+ if cors_builder.changed?
136
+ @gapi.cors_configurations = cors_builder.to_gapi
137
+ patch_gapi! :cors_configurations
138
+ end
139
+ end
140
+ cors_builder.freeze # always return frozen objects
141
+ end
142
+
143
+ ##
144
+ # The location of the bucket.
145
+ # Object data for objects in the bucket resides in physical
146
+ # storage within this region. Defaults to US.
147
+ # See the developer's guide for the authoritative list.
148
+ #
149
+ # @see https://cloud.google.com/storage/docs/concepts-techniques
150
+ def location
151
+ @gapi.location
152
+ end
153
+
154
+ ##
155
+ # The destination bucket name for the bucket's logs.
156
+ #
157
+ # @see https://cloud.google.com/storage/docs/access-logs Access Logs
158
+ #
159
+ def logging_bucket
160
+ @gapi.logging.log_bucket if @gapi.logging
161
+ end
162
+
163
+ ##
164
+ # Updates the destination bucket for the bucket's logs.
165
+ #
166
+ # @see https://cloud.google.com/storage/docs/access-logs Access Logs
167
+ #
168
+ # @param [String] logging_bucket The bucket to hold the logging output
169
+ #
170
+ def logging_bucket= logging_bucket
171
+ @gapi.logging ||= Google::Apis::StorageV1::Bucket::Logging.new
172
+ @gapi.logging.log_bucket = logging_bucket
173
+ patch_gapi! :logging
174
+ end
175
+
176
+ ##
177
+ # The logging object prefix for the bucket's logs. For more information,
178
+ #
179
+ # @see https://cloud.google.com/storage/docs/access-logs Access Logs
180
+ #
181
+ def logging_prefix
182
+ @gapi.logging.log_object_prefix if @gapi.logging
183
+ end
184
+
185
+ ##
186
+ # Updates the logging object prefix. This prefix will be used to create
187
+ # log object names for the bucket. It can be at most 900 characters and
188
+ # must be a [valid object
189
+ # name](https://cloud.google.com/storage/docs/bucket-naming#objectnames).
190
+ # By default, the object prefix is the name of the bucket for which the
191
+ # logs are enabled.
192
+ #
193
+ # @see https://cloud.google.com/storage/docs/access-logs Access Logs
194
+ #
195
+ def logging_prefix= logging_prefix
196
+ @gapi.logging ||= Google::Apis::StorageV1::Bucket::Logging.new
197
+ @gapi.logging.log_object_prefix = logging_prefix
198
+ patch_gapi! :logging
199
+ end
200
+
201
+ ##
202
+ # The bucket's storage class. This defines how objects in the bucket are
203
+ # stored and determines the SLA and the cost of storage. Values include
204
+ # `STANDARD`, `NEARLINE`, and `DURABLE_REDUCED_AVAILABILITY`.
205
+ def storage_class
206
+ @gapi.storage_class
207
+ end
208
+
209
+ ##
210
+ # Whether [Object
211
+ # Versioning](https://cloud.google.com/storage/docs/object-versioning)
212
+ # is enabled for the bucket.
213
+ def versioning?
214
+ @gapi.versioning.enabled? unless @gapi.versioning.nil?
215
+ end
216
+
217
+ ##
218
+ # Updates whether [Object
219
+ # Versioning](https://cloud.google.com/storage/docs/object-versioning)
220
+ # is enabled for the bucket.
221
+ #
222
+ # @return [Boolean]
223
+ #
224
+ def versioning= new_versioning
225
+ @gapi.versioning ||= Google::Apis::StorageV1::Bucket::Versioning.new
226
+ @gapi.versioning.enabled = new_versioning
227
+ patch_gapi! :versioning
228
+ end
229
+
230
+ ##
231
+ # The index page returned from a static website served from the bucket
232
+ # when a site visitor requests the top level directory.
233
+ #
234
+ # @see https://cloud.google.com/storage/docs/website-configuration#step4
235
+ # How to Host a Static Website
236
+ #
237
+ def website_main
238
+ @gapi.website.main_page_suffix if @gapi.website
239
+ end
240
+
241
+ ##
242
+ # Updates the index page returned from a static website served from the
243
+ # bucket when a site visitor requests the top level directory.
244
+ #
245
+ # @see https://cloud.google.com/storage/docs/website-configuration#step4
246
+ # How to Host a Static Website
247
+ #
248
+ def website_main= website_main
249
+ @gapi.website ||= Google::Apis::StorageV1::Bucket::Website.new
250
+ @gapi.website.main_page_suffix = website_main
251
+ patch_gapi! :website
252
+ end
253
+
254
+ ##
255
+ # The page returned from a static website served from the bucket when a
256
+ # site visitor requests a resource that does not exist.
257
+ #
258
+ # @see https://cloud.google.com/storage/docs/website-configuration#step4
259
+ # How to Host a Static Website
260
+ #
261
+ def website_404
262
+ @gapi.website.not_found_page if @gapi.website
263
+ end
264
+
265
+ ##
266
+ # Updates the page returned from a static website served from the bucket
267
+ # when a site visitor requests a resource that does not exist.
268
+ #
269
+ # @see https://cloud.google.com/storage/docs/website-configuration#step4
270
+ # How to Host a Static Website
271
+ #
272
+ def website_404= website_404
273
+ @gapi.website ||= Google::Apis::StorageV1::Bucket::Website.new
274
+ @gapi.website.not_found_page = website_404
275
+ patch_gapi! :website
276
+ end
277
+
278
+ ##
279
+ # Updates the bucket with changes made in the given block in a single
280
+ # PATCH request. The following attributes may be set: {#cors},
281
+ # {#logging_bucket=}, {#logging_prefix=}, {#versioning=},
282
+ # {#website_main=}, and {#website_404=}. In addition, the #cors
283
+ # configuration accessible in the block is completely mutable and will
284
+ # be included in the request. (See {Bucket::Cors})
285
+ #
286
+ # @yield [bucket] a block yielding a delegate object for updating the
287
+ # file
288
+ #
289
+ # @example
290
+ # require "google/cloud"
291
+ #
292
+ # gcloud = Google::Cloud.new
293
+ # storage = gcloud.storage
294
+ #
295
+ # bucket = storage.bucket "my-bucket"
296
+ # bucket.update do |b|
297
+ # b.website_main = "index.html"
298
+ # b.website_404 = "not_found.html"
299
+ # b.cors[0]["method"] = ["GET","POST","DELETE"]
300
+ # b.cors[1]["responseHeader"] << "X-Another-Custom-Header"
301
+ # end
302
+ #
303
+ # @example New CORS rules can also be added in a nested block:
304
+ # require "google/cloud"
305
+ #
306
+ # gcloud = Google::Cloud.new
307
+ # storage = gcloud.storage
308
+ # bucket = storage.bucket "my-todo-app"
309
+ #
310
+ # bucket.update do |b|
311
+ # b.cors do |c|
312
+ # c.add_rule ["http://example.org", "https://example.org"],
313
+ # "*",
314
+ # response_headers: ["X-My-Custom-Header"],
315
+ # max_age: 300
316
+ # end
317
+ # end
318
+ #
319
+ def update
320
+ updater = Updater.new @gapi
321
+ yield updater
322
+ # Add check for mutable cors
323
+ updater.check_for_mutable_cors!
324
+ patch_gapi! updater.updates unless updater.updates.empty?
325
+ end
326
+
327
+ ##
328
+ # Permanently deletes the bucket.
329
+ # The bucket must be empty before it can be deleted.
330
+ #
331
+ # The API call to delete the bucket may be retried under certain
332
+ # conditions. See {Google::Cloud#storage} to control this behavior.
333
+ #
334
+ # @return [Boolean] Returns `true` if the bucket was deleted.
335
+ #
336
+ # @example
337
+ # require "google/cloud"
338
+ #
339
+ # gcloud = Google::Cloud.new
340
+ # storage = gcloud.storage
341
+ #
342
+ # bucket = storage.bucket "my-bucket"
343
+ # bucket.delete
344
+ #
345
+ def delete
346
+ ensure_service!
347
+ service.delete_bucket name
348
+ true
349
+ end
350
+
351
+ ##
352
+ # Retrieves a list of files matching the criteria.
353
+ #
354
+ # @param [String] prefix Filter results to files whose names begin with
355
+ # this prefix.
356
+ # @param [String] delimiter Returns results in a directory-like mode.
357
+ # `items` will contain only objects whose names, aside from the
358
+ # `prefix`, do not contain `delimiter`. Objects whose names, aside
359
+ # from the `prefix`, contain `delimiter` will have their name,
360
+ # truncated after the `delimiter`, returned in `prefixes`. Duplicate
361
+ # `prefixes` are omitted.
362
+ # @param [String] token A previously-returned page token representing
363
+ # part of the larger set of results to view.
364
+ # @param [Integer] max Maximum number of items plus prefixes to return.
365
+ # As duplicate prefixes are omitted, fewer total results may be
366
+ # returned than requested. The default value of this parameter is
367
+ # 1,000 items.
368
+ # @param [Boolean] versions If `true`, lists all versions of an object
369
+ # as distinct results. The default is `false`. For more information,
370
+ # see [Object Versioning
371
+ # ](https://cloud.google.com/storage/docs/object-versioning).
372
+ #
373
+ # @return [Array<Google::Cloud::Storage::File>] (See
374
+ # {Google::Cloud::Storage::File::List})
375
+ #
376
+ # @example
377
+ # require "google/cloud"
378
+ #
379
+ # gcloud = Google::Cloud.new
380
+ # storage = gcloud.storage
381
+ #
382
+ # bucket = storage.bucket "my-bucket"
383
+ # files = bucket.files
384
+ # files.each do |file|
385
+ # puts file.name
386
+ # end
387
+ #
388
+ # @example Retrieve all files: (See {File::List#all})
389
+ # require "google/cloud"
390
+ #
391
+ # gcloud = Google::Cloud.new
392
+ # storage = gcloud.storage
393
+ #
394
+ # bucket = storage.bucket "my-bucket"
395
+ # files = bucket.files
396
+ # files.all do |file|
397
+ # puts file.name
398
+ # end
399
+ #
400
+ def files prefix: nil, delimiter: nil, token: nil, max: nil,
401
+ versions: nil
402
+ ensure_service!
403
+ options = {
404
+ prefix: prefix,
405
+ delimiter: delimiter,
406
+ token: token,
407
+ max: max,
408
+ versions: versions
409
+ }
410
+ gapi = service.list_files name, options
411
+ File::List.from_gapi gapi, service, name, prefix, delimiter, max,
412
+ versions
413
+ end
414
+ alias_method :find_files, :files
415
+
416
+ ##
417
+ # Retrieves a file matching the path.
418
+ #
419
+ # If a [customer-supplied encryption
420
+ # key](https://cloud.google.com/storage/docs/encryption#customer-supplied)
421
+ # was used with {#create_file}, the `encryption_key` and
422
+ # `encryption_key_sha256` options must be provided or else the file's
423
+ # CRC32C checksum and MD5 hash will not be returned.
424
+ #
425
+ # @param [String] path Name (path) of the file.
426
+ # @param [Integer] generation When present, selects a specific revision
427
+ # of this object. Default is the latest version.
428
+ # @param [String] encryption_key Optional. The customer-supplied,
429
+ # AES-256 encryption key used to encrypt the file, if one was provided
430
+ # to {#create_file}. Must be provided if `encryption_key_sha256` is
431
+ # provided.
432
+ # @param [String] encryption_key_sha256 Optional. The SHA256 hash of the
433
+ # customer-supplied, AES-256 encryption key used to encrypt the file,
434
+ # if one was provided to {#create_file}. Must be provided if
435
+ # `encryption_key` is provided.
436
+ #
437
+ # @return [Google::Cloud::Storage::File, nil] Returns nil if file does
438
+ # not exist
439
+ #
440
+ # @example
441
+ # require "google/cloud"
442
+ #
443
+ # gcloud = Google::Cloud.new
444
+ # storage = gcloud.storage
445
+ #
446
+ # bucket = storage.bucket "my-bucket"
447
+ #
448
+ # file = bucket.file "path/to/my-file.ext"
449
+ # puts file.name
450
+ #
451
+ def file path, generation: nil, encryption_key: nil,
452
+ encryption_key_sha256: nil
453
+ ensure_service!
454
+ options = { generation: generation, key: encryption_key,
455
+ key_sha256: encryption_key_sha256 }
456
+ gapi = service.get_file name, path, options
457
+ File.from_gapi gapi, service
458
+ rescue Google::Cloud::NotFoundError
459
+ nil
460
+ end
461
+ alias_method :find_file, :file
462
+
463
+ ##
464
+ # Creates a new {File} object by providing a path to a local file to
465
+ # upload and the path to store it with in the bucket.
466
+ #
467
+ # #### Customer-supplied encryption keys
468
+ #
469
+ # By default, Google Cloud Storage manages server-side encryption keys
470
+ # on your behalf. However, a [customer-supplied encryption
471
+ # key](https://cloud.google.com/storage/docs/encryption#customer-supplied)
472
+ # can be provided with the `encryption_key` and `encryption_key_sha256`
473
+ # options. If given, the same key and SHA256 hash also must be provided
474
+ # to subsequently download or copy the file. If you use
475
+ # customer-supplied encryption keys, you must securely manage your keys
476
+ # and ensure that they are not lost. Also, please note that file
477
+ # metadata is not encrypted, with the exception of the CRC32C checksum
478
+ # and MD5 hash. The names of files and buckets are also not encrypted,
479
+ # and you can read or update the metadata of an encrypted file without
480
+ # providing the encryption key.
481
+ #
482
+ # @param [String] file Path of the file on the filesystem to upload.
483
+ # @param [String] path Path to store the file in Google Cloud Storage.
484
+ # @param [String] acl A predefined set of access controls to apply to
485
+ # this file.
486
+ #
487
+ # Acceptable values are:
488
+ #
489
+ # * `auth`, `auth_read`, `authenticated`, `authenticated_read`,
490
+ # `authenticatedRead` - File owner gets OWNER access, and
491
+ # allAuthenticatedUsers get READER access.
492
+ # * `owner_full`, `bucketOwnerFullControl` - File owner gets OWNER
493
+ # access, and project team owners get OWNER access.
494
+ # * `owner_read`, `bucketOwnerRead` - File owner gets OWNER access,
495
+ # and project team owners get READER access.
496
+ # * `private` - File owner gets OWNER access.
497
+ # * `project_private`, `projectPrivate` - File owner gets OWNER
498
+ # access, and project team members get access according to their
499
+ # roles.
500
+ # * `public`, `public_read`, `publicRead` - File owner gets OWNER
501
+ # access, and allUsers get READER access.
502
+ # @param [String] cache_control The
503
+ # [Cache-Control](https://tools.ietf.org/html/rfc7234#section-5.2)
504
+ # response header to be returned when the file is downloaded.
505
+ # @param [String] content_disposition The
506
+ # [Content-Disposition](https://tools.ietf.org/html/rfc6266)
507
+ # response header to be returned when the file is downloaded.
508
+ # @param [String] content_encoding The [Content-Encoding
509
+ # ](https://tools.ietf.org/html/rfc7231#section-3.1.2.2) response
510
+ # header to be returned when the file is downloaded.
511
+ # @param [String] content_language The
512
+ # [Content-Language](http://tools.ietf.org/html/bcp47) response
513
+ # header to be returned when the file is downloaded.
514
+ # @param [String] content_type The
515
+ # [Content-Type](https://tools.ietf.org/html/rfc2616#section-14.17)
516
+ # response header to be returned when the file is downloaded.
517
+ # @param [String] crc32c The CRC32c checksum of the file data, as
518
+ # described in [RFC 4960, Appendix
519
+ # B](http://tools.ietf.org/html/rfc4960#appendix-B).
520
+ # If provided, Cloud Storage will only create the file if the value
521
+ # matches the value calculated by the service. See
522
+ # [Validation](https://cloud.google.com/storage/docs/hashes-etags)
523
+ # for more information.
524
+ # @param [String] md5 The MD5 hash of the file data. If provided, Cloud
525
+ # Storage will only create the file if the value matches the value
526
+ # calculated by the service. See
527
+ # [Validation](https://cloud.google.com/storage/docs/hashes-etags) for
528
+ # more information.
529
+ # @param [Hash] metadata A hash of custom, user-provided web-safe keys
530
+ # and arbitrary string values that will returned with requests for the
531
+ # file as "x-goog-meta-" response headers.
532
+ # @param [String] encryption_key Optional. A customer-supplied, AES-256
533
+ # encryption key that will be used to encrypt the file. Must be
534
+ # provided if `encryption_key_sha256` is provided.
535
+ # @param [String] encryption_key_sha256 Optional. The SHA256 hash of the
536
+ # customer-supplied, AES-256 encryption key that will be used to
537
+ # encrypt the file. Must be provided if `encryption_key` is provided.
538
+ #
539
+ # @return [Google::Cloud::Storage::File]
540
+ #
541
+ # @example
542
+ # require "google/cloud"
543
+ #
544
+ # gcloud = Google::Cloud.new
545
+ # storage = gcloud.storage
546
+ #
547
+ # bucket = storage.bucket "my-bucket"
548
+ #
549
+ # bucket.create_file "path/to/local.file.ext"
550
+ #
551
+ # @example Specifying a destination path:
552
+ # require "google/cloud"
553
+ #
554
+ # gcloud = Google::Cloud.new
555
+ # storage = gcloud.storage
556
+ #
557
+ # bucket = storage.bucket "my-bucket"
558
+ #
559
+ # bucket.create_file "path/to/local.file.ext",
560
+ # "destination/path/file.ext"
561
+ #
562
+ # @example Providing a customer-supplied encryption key:
563
+ # require "google/cloud"
564
+ # require "digest/sha2"
565
+ #
566
+ # gcloud = Google::Cloud.new
567
+ # storage = gcloud.storage
568
+ # bucket = storage.bucket "my-bucket"
569
+ #
570
+ # # Key generation shown for example purposes only. Write your own.
571
+ # cipher = OpenSSL::Cipher.new "aes-256-cfb"
572
+ # cipher.encrypt
573
+ # key = cipher.random_key
574
+ # key_hash = Digest::SHA256.digest key
575
+ #
576
+ # bucket.create_file "path/to/local.file.ext",
577
+ # "destination/path/file.ext",
578
+ # encryption_key: key,
579
+ # encryption_key_sha256: key_hash
580
+ #
581
+ # # Store your key and hash securely for later use.
582
+ # file = bucket.file "destination/path/file.ext",
583
+ # encryption_key: key,
584
+ # encryption_key_sha256: key_hash
585
+ #
586
+ def create_file file, path = nil, acl: nil, cache_control: nil,
587
+ content_disposition: nil, content_encoding: nil,
588
+ content_language: nil, content_type: nil,
589
+ crc32c: nil, md5: nil, metadata: nil,
590
+ encryption_key: nil, encryption_key_sha256: nil
591
+ ensure_service!
592
+ options = { acl: File::Acl.predefined_rule_for(acl), md5: md5,
593
+ cache_control: cache_control, content_type: content_type,
594
+ content_disposition: content_disposition, crc32c: crc32c,
595
+ content_encoding: content_encoding,
596
+ content_language: content_language, metadata: metadata,
597
+ key: encryption_key, key_sha256: encryption_key_sha256 }
598
+ ensure_file_exists! file
599
+ # TODO: Handle file as an IO and path is missing more gracefully
600
+ path ||= Pathname(file).to_path
601
+ gapi = service.insert_file name, file, path, options
602
+ File.from_gapi gapi, service
603
+ end
604
+ alias_method :upload_file, :create_file
605
+ alias_method :new_file, :create_file
606
+
607
+ ##
608
+ # The Bucket::Acl instance used to control access to the bucket.
609
+ #
610
+ # A bucket has owners, writers, and readers. Permissions can be granted
611
+ # to an individual user's email address, a group's email address, as
612
+ # well as many predefined lists.
613
+ #
614
+ # @see https://cloud.google.com/storage/docs/access-control Access
615
+ # Control guide
616
+ #
617
+ # @example Grant access to a user by prepending `"user-"` to an email:
618
+ # require "google/cloud"
619
+ #
620
+ # gcloud = Google::Cloud.new
621
+ # storage = gcloud.storage
622
+ #
623
+ # bucket = storage.bucket "my-todo-app"
624
+ #
625
+ # email = "heidi@example.net"
626
+ # bucket.acl.add_reader "user-#{email}"
627
+ #
628
+ # @example Grant access to a group by prepending `"group-"` to an email:
629
+ # require "google/cloud"
630
+ #
631
+ # gcloud = Google::Cloud.new
632
+ # storage = gcloud.storage
633
+ #
634
+ # bucket = storage.bucket "my-todo-app"
635
+ #
636
+ # email = "authors@example.net"
637
+ # bucket.acl.add_reader "group-#{email}"
638
+ #
639
+ # @example Or, grant access via a predefined permissions list:
640
+ # require "google/cloud"
641
+ #
642
+ # gcloud = Google::Cloud.new
643
+ # storage = gcloud.storage
644
+ #
645
+ # bucket = storage.bucket "my-todo-app"
646
+ #
647
+ # bucket.acl.public!
648
+ #
649
+ def acl
650
+ @acl ||= Bucket::Acl.new self
651
+ end
652
+
653
+ ##
654
+ # The Bucket::DefaultAcl instance used to control access to the bucket's
655
+ # files.
656
+ #
657
+ # A bucket's files have owners, writers, and readers. Permissions can be
658
+ # granted to an individual user's email address, a group's email
659
+ # address, as well as many predefined lists.
660
+ #
661
+ # @see https://cloud.google.com/storage/docs/access-control Access
662
+ # Control guide
663
+ #
664
+ # @example Grant access to a user by prepending `"user-"` to an email:
665
+ # require "google/cloud"
666
+ #
667
+ # gcloud = Google::Cloud.new
668
+ # storage = gcloud.storage
669
+ #
670
+ # bucket = storage.bucket "my-todo-app"
671
+ #
672
+ # email = "heidi@example.net"
673
+ # bucket.default_acl.add_reader "user-#{email}"
674
+ #
675
+ # @example Grant access to a group by prepending `"group-"` to an email
676
+ # require "google/cloud"
677
+ #
678
+ # gcloud = Google::Cloud.new
679
+ # storage = gcloud.storage
680
+ #
681
+ # bucket = storage.bucket "my-todo-app"
682
+ #
683
+ # email = "authors@example.net"
684
+ # bucket.default_acl.add_reader "group-#{email}"
685
+ #
686
+ # @example Or, grant access via a predefined permissions list:
687
+ # require "google/cloud"
688
+ #
689
+ # gcloud = Google::Cloud.new
690
+ # storage = gcloud.storage
691
+ #
692
+ # bucket = storage.bucket "my-todo-app"
693
+ #
694
+ # bucket.default_acl.public!
695
+ #
696
+ def default_acl
697
+ @default_acl ||= Bucket::DefaultAcl.new self
698
+ end
699
+
700
+ ##
701
+ # Reloads the bucket with current data from the Storage service.
702
+ def reload!
703
+ ensure_service!
704
+ @gapi = service.get_bucket name
705
+ end
706
+ alias_method :refresh!, :reload!
707
+
708
+ ##
709
+ # @private New Bucket from a Google API Client object.
710
+ def self.from_gapi gapi, conn
711
+ new.tap do |f|
712
+ f.gapi = gapi
713
+ f.service = conn
714
+ end
715
+ end
716
+
717
+ protected
718
+
719
+ ##
720
+ # Raise an error unless an active service is available.
721
+ def ensure_service!
722
+ fail "Must have active connection" unless service
723
+ end
724
+
725
+ def patch_gapi! *attributes
726
+ attributes.flatten!
727
+ return if attributes.empty?
728
+ ensure_service!
729
+ patch_args = Hash[attributes.map do |attr|
730
+ [attr, @gapi.send(attr)]
731
+ end]
732
+ patch_gapi = Google::Apis::StorageV1::Bucket.new patch_args
733
+ @gapi = service.patch_bucket name, patch_gapi
734
+ end
735
+
736
+ ##
737
+ # Raise an error if the file is not found.
738
+ def ensure_file_exists! file
739
+ return if ::File.file? file
740
+ fail ArgumentError, "cannot find file #{file}"
741
+ end
742
+
743
+ ##
744
+ # Yielded to a block to accumulate changes for a patch request.
745
+ class Updater < Bucket
746
+ attr_reader :updates
747
+ ##
748
+ # Create an Updater object.
749
+ def initialize gapi
750
+ @updates = []
751
+ @gapi = gapi
752
+ @cors_builder = nil
753
+ end
754
+
755
+ def cors
756
+ # Same as Bucket#cors, but not frozen
757
+ @cors_builder ||= Bucket::Cors.from_gapi @gapi.cors_configurations
758
+ yield @cors_builder if block_given?
759
+ @cors_builder
760
+ end
761
+
762
+ ##
763
+ # @private Make sure any cors changes are saved
764
+ def check_for_mutable_cors!
765
+ return if @cors_builder.nil?
766
+ return unless @cors_builder.changed?
767
+ @gapi.cors_configurations = @cors_builder.to_gapi
768
+ patch_gapi! :cors_configurations
769
+ end
770
+
771
+ protected
772
+
773
+ ##
774
+ # Queue up all the updates instead of making them.
775
+ def patch_gapi! attribute
776
+ @updates << attribute
777
+ @updates.uniq!
778
+ end
779
+ end
780
+ end
781
+ end
782
+ end
783
+ end