lib/google/cloud/storage/file.rb in google-cloud-storage-0.25.0 vs lib/google/cloud/storage/file.rb in google-cloud-storage-1.0.0

- old
+ new

@@ -169,11 +169,11 @@ # Updates the # [Cache-Control](https://tools.ietf.org/html/rfc7234#section-5.2) # directive for the file data. def cache_control= cache_control @gapi.cache_control = cache_control - patch_gapi! :cache_control + update_gapi! :cache_control end ## # The [Content-Disposition](https://tools.ietf.org/html/rfc6266) of the # file data. @@ -184,11 +184,11 @@ ## # Updates the [Content-Disposition](https://tools.ietf.org/html/rfc6266) # of the file data. def content_disposition= content_disposition @gapi.content_disposition = content_disposition - patch_gapi! :content_disposition + update_gapi! :content_disposition end ## # The [Content-Encoding # ](https://tools.ietf.org/html/rfc7231#section-3.1.2.2) of the file @@ -201,11 +201,11 @@ # Updates the [Content-Encoding # ](https://tools.ietf.org/html/rfc7231#section-3.1.2.2) of the file # data. def content_encoding= content_encoding @gapi.content_encoding = content_encoding - patch_gapi! :content_encoding + update_gapi! :content_encoding end ## # The [Content-Language](http://tools.ietf.org/html/bcp47) of the file # data. @@ -216,11 +216,11 @@ ## # Updates the [Content-Language](http://tools.ietf.org/html/bcp47) of # the file data. def content_language= content_language @gapi.content_language = content_language - patch_gapi! :content_language + update_gapi! :content_language end ## # The [Content-Type](https://tools.ietf.org/html/rfc2616#section-14.17) # of the file data. @@ -232,11 +232,11 @@ # Updates the # [Content-Type](https://tools.ietf.org/html/rfc2616#section-14.17) of # the file data. def content_type= content_type @gapi.content_type = content_type - patch_gapi! :content_type + update_gapi! :content_type end ## # A hash of custom, user-provided web-safe keys and arbitrary string # values that will returned with requests for the file as "x-goog-meta-" @@ -251,11 +251,11 @@ # Updates the hash of custom, user-provided web-safe keys and arbitrary # string values that will returned with requests for the file as # "x-goog-meta-" response headers. def metadata= metadata @gapi.metadata = metadata - patch_gapi! :metadata + update_gapi! :metadata end ## # An [RFC 4648](https://tools.ietf.org/html/rfc4648#section-4) # Base64-encoded string of the SHA256 hash of the [customer-supplied @@ -290,13 +290,12 @@ # Class](https://cloud.google.com/storage/docs/per-object-storage-class). # The default value is the default storage class for the bucket. See # {Bucket#storage_class}. # @param [Symbol, String] storage_class Storage class of the file. def storage_class= storage_class - resp = service.update_file_storage_class \ - bucket, name, storage_class_for(storage_class) - @gapi = resp.resource + @gapi.storage_class = storage_class_for(storage_class) + update_gapi! :storage_class end ## # Updates the file with changes made in the given block in a single # PATCH request. The following attributes may be set: {#cache_control=}, @@ -327,11 +326,11 @@ # def update updater = Updater.new gapi yield updater updater.check_for_changed_metadata! - patch_gapi! updater.updates unless updater.updates.empty? + update_gapi! updater.updates unless updater.updates.empty? end ## # Download the file's contents to a local file or an IO instance. # @@ -467,10 +466,11 @@ # @param [Integer] generation Select a specific revision of the file to # copy. The default is the latest version. # @param [String] encryption_key Optional. The customer-supplied, # AES-256 encryption key used to encrypt the file, if one was provided # to {Bucket#create_file}. + # @yield [file] a block yielding a delegate object for updating # # @return [Google::Cloud::Storage::File] # # @example The file can be copied to a new path in the current bucket: # require "google/cloud/storage" @@ -502,21 +502,47 @@ # # file = bucket.file "path/to/my-file.ext" # file.copy "copy/of/previous/generation/file.ext", # generation: 123456 # + # @example The file can be modified during copying: + # require "google/cloud/storage" + # + # storage = Google::Cloud::Storage.new + # + # bucket = storage.bucket "my-bucket" + # + # file = bucket.file "path/to/my-file.ext" + # file.copy "new-destination-bucket", + # "path/to/destination/file.ext" do |f| + # f.metadata["copied_from"] = "#{file.bucket}/#{file.name}" + # end + # def copy dest_bucket_or_path, dest_path = nil, acl: nil, generation: nil, encryption_key: nil ensure_service! - options = { acl: acl, generation: generation, - key: encryption_key } + options = { acl: acl, generation: generation, key: encryption_key } dest_bucket, dest_path, options = fix_copy_args dest_bucket_or_path, dest_path, options - gapi = service.copy_file bucket, name, - dest_bucket, dest_path, options - File.from_gapi gapi, service + copy_gapi = nil + if block_given? + updater = Updater.new gapi + yield updater + updater.check_for_changed_metadata! + copy_gapi = gapi_from_attrs(updater.updates) if updater.updates.any? + end + + resp = service.copy_file bucket, name, dest_bucket, dest_path, + copy_gapi, options + until resp.done + sleep 1 + resp = service.copy_file bucket, name, dest_bucket, dest_path, + copy_gapi, + options.merge(token: resp.rewrite_token) + end + File.from_gapi resp.resource, service end ## # [Rewrites](https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite) # the file to the same {#bucket} and {#name} with a new @@ -564,14 +590,15 @@ # def rotate encryption_key: nil, new_encryption_key: nil ensure_service! options = { source_key: encryption_key, destination_key: new_encryption_key } - gapi = service.rewrite_file bucket, name, bucket, name, options + gapi = service.rewrite_file bucket, name, bucket, name, nil, options until gapi.done + sleep 1 options[:token] = gapi.rewrite_token - gapi = service.rewrite_file bucket, name, bucket, name, options + gapi = service.rewrite_file bucket, name, bucket, name, nil, options end File.from_gapi gapi.resource, service end ## @@ -803,21 +830,45 @@ # Raise an error unless an active service is available. def ensure_service! fail "Must have active connection" unless service end - def patch_gapi! *attributes + def update_gapi! *attributes attributes.flatten! return if attributes.empty? + update_gapi = gapi_from_attrs attributes + return if update_gapi.nil? + ensure_service! - patch_args = Hash[attributes.map do |attr| + + if attributes.include? :storage_class + @gapi = rewrite_gapi bucket, name, update_gapi + else + @gapi = service.patch_file bucket, name, update_gapi + end + end + + def gapi_from_attrs *attributes + attributes.flatten! + return nil if attributes.empty? + attr_params = Hash[attributes.map do |attr| [attr, @gapi.send(attr)] end] - patch_gapi = Google::Apis::StorageV1::Object.new patch_args - @gapi = service.patch_file bucket, name, patch_gapi + Google::Apis::StorageV1::Object.new attr_params end + def rewrite_gapi bucket, name, update_gapi + resp = service.rewrite_file bucket, name, bucket, name, update_gapi + until resp.done + sleep 1 + rewrite_options = { token: resp.rewrite_token } + resp = service.rewrite_file bucket, name, bucket, name, + update_gapi, rewrite_options + end + resp.resource + end + def fix_copy_args dest_bucket, dest_path, options = {} if dest_path.respond_to?(:to_hash) && options.empty? options = dest_path dest_path = nil end @@ -851,55 +902,51 @@ end ## # Yielded to a block to accumulate changes for a patch request. class Updater < File - # @private Do not allow storage_class to be set in an update call. - # It cannot be set with PATCH. - undef :storage_class= - # @private attr_reader :updates ## # @private Create an Updater object. def initialize gapi @updates = [] @gapi = gapi + @metadata ||= @gapi.metadata.to_h.dup end ## # A hash of custom, user-provided web-safe keys and arbitrary string # values that will returned with requests for the file as # "x-goog-meta-" response headers. def metadata - # do not freeze metadata - @metadata ||= @gapi.metadata.to_h.dup + @metadata end ## # Updates the hash of custom, user-provided web-safe keys and # arbitrary string values that will returned with requests for the # file as "x-goog-meta-" response headers. def metadata= metadata @metadata = metadata @gapi.metadata = @metadata - patch_gapi! :metadata + update_gapi! :metadata end ## # @private Make sure any metadata changes are saved def check_for_changed_metadata! - return if @metadata == @gapi.metadata + return if @metadata == @gapi.metadata.to_h @gapi.metadata = @metadata - patch_gapi! :metadata + update_gapi! :metadata end protected ## # Queue up all the updates instead of making them. - def patch_gapi! attribute + def update_gapi! attribute @updates << attribute @updates.uniq! end end end