gcloud 0.4.0 → 0.4.1

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.
@@ -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