class Shrine::Storage::GoogleCloudStorage

Attributes

bucket[R]
host[R]
prefix[R]

Public Class Methods

new(project: nil, bucket:, prefix: nil, host: nil, default_acl: nil, object_options: {}, credentials: nil) click to toggle source

Initialize a Shrine::Storage for GCS allowing for auto-discovery of the Google::Cloud::Storage client. @param [String] project Provide if not using auto discovery @see googlecloudplatform.github.io/google-cloud-ruby/#/docs/google-cloud-storage/v1.6.0/guides/authentication#environmentvariables for information on discovery

# File lib/shrine/storage/google_cloud_storage.rb, line 13
def initialize(project: nil, bucket:, prefix: nil, host: nil, default_acl: nil, object_options: {}, credentials: nil)
  @project = project
  @bucket = bucket
  @prefix = prefix
  @host = host
  @default_acl = default_acl
  @object_options = object_options
  @storage = nil
  @credentials = credentials
end

Public Instance Methods

clear!(&block) click to toggle source

deletes all objects from the storage If block is givem, deletes only objects for which the block evaluates to true.

clear! # or clear! { |file| file.updated_at < 1.week.ago }

# File lib/shrine/storage/google_cloud_storage.rb, line 118
def clear!(&block)
  prefix = "#{@prefix}/" if @prefix
  files = get_bucket.files prefix: prefix

  loop do
    files.each { |file| delete_file(file) if block.nil? || block.call(file) }
    files = files.next or break
  end
end
delete(id) click to toggle source

deletes the file from the storage

# File lib/shrine/storage/google_cloud_storage.rb, line 106
def delete(id)
  file = get_file(id)
  delete_file(file) unless file.nil?
end
exists?(id) click to toggle source

checks if the file exists on the storage

# File lib/shrine/storage/google_cloud_storage.rb, line 99
def exists?(id)
  file = get_file(id)
  return false if file.nil?
  file.exists?
end
object_name(id) click to toggle source
# File lib/shrine/storage/google_cloud_storage.rb, line 140
def object_name(id)
  @prefix ? "#{@prefix}/#{id}" : id
end
open(id, rewindable: true, **options) click to toggle source

Opens the remote file and returns it as `Down::ChunkedIO` object. @return [Down::ChunkedIO] object @see github.com/janko-m/down#downchunkedio

# File lib/shrine/storage/google_cloud_storage.rb, line 77
def open(id, rewindable: true, **options)
  file = get_file(id)

  raise Shrine::FileNotFound, "file #{id.inspect} not found on storage" unless file

  # create enumerator which lazily yields chunks of downloaded content
  chunks = Enumerator.new do |yielder|
    # trick to get google client to stream the download
    proc_io = ProcIO.new { |data| yielder << data }
    file.download(proc_io, verify: :none, **options)
  end

  # wrap chunks in an IO-like object which downloads when needed
  Down::ChunkedIO.new(
    chunks:     chunks,
    size:       file.size,
    rewindable: rewindable,
    data:       { file: file },
  )
end
presign(id, **options) click to toggle source

returns request data (:method, :url, and :headers) for direct uploads

# File lib/shrine/storage/google_cloud_storage.rb, line 129
def presign(id, **options)
  url = storage.signed_url(@bucket, object_name(id), method: "PUT", **options)

  headers = {}
  headers["Content-Type"] = options[:content_type] if options[:content_type]
  headers["Content-MD5"]  = options[:content_md5] if options[:content_md5]
  headers.merge!(options[:headers]) if options[:headers]

  { method: :put, url: url, headers: headers }
end
upload(io, id, shrine_metadata: {}, **options) click to toggle source

If the file is an UploadFile from GCS, issues a copy command, otherwise it uploads a file. @param [IO] io - io like object @param [String] id - location

# File lib/shrine/storage/google_cloud_storage.rb, line 27
def upload(io, id, shrine_metadata: {}, **options)
  if copyable?(io)
    existing_file = get_bucket(io.storage.bucket).file(io.storage.object_name(io.id))
    file = existing_file.copy(
        @bucket, # dest_bucket_or_path - the bucket to copy the file to
        object_name(id), # dest_path - the path to copy the file to in the given bucket
        acl: options.fetch(:acl) { @default_acl }
    ) do |f|
      # Workaround a bug in File#copy where the content-type is not copied if you provide a block
      # See https://github.com/renchap/shrine-google_cloud_storage/issues/36
      # See https://github.com/googleapis/google-cloud-ruby/issues/4254
      f.content_type = existing_file.content_type

      # update the additional options
      @object_options.merge(options).each_pair do |key, value|
        f.send("#{key}=", value)
      end
    end
    file
  else
    with_file(io) do |file|
      file_options = @object_options.merge(
        content_type: shrine_metadata["mime_type"],
        acl: options.fetch(:acl) { @default_acl }
      ).merge(options)

      get_bucket.create_file(
          file,
          object_name(id), # path
          **file_options
      )
    end
  end
end
url(id, **options) click to toggle source

URL to the remote file, accepts options for customizing the URL

# File lib/shrine/storage/google_cloud_storage.rb, line 63
def url(id, **options)
  if options.key? :expires
    signed_url = storage.signed_url(@bucket, object_name(id), **options)
    signed_url.gsub!(/storage.googleapis.com\/#{@bucket}/, @host) if @host
    signed_url
  else
    host = @host || "storage.googleapis.com/#{@bucket}"
    "https://#{host}/#{object_name(id)}"
  end
end

Private Instance Methods

copyable?(io) click to toggle source
# File lib/shrine/storage/google_cloud_storage.rb, line 165
def copyable?(io)
  io.is_a?(UploadedFile) &&
    io.storage.is_a?(Storage::GoogleCloudStorage)
  # TODO: add a check for the credentials
end
delete_file(file) click to toggle source

deletes file with ignoring NotFoundError

# File lib/shrine/storage/google_cloud_storage.rb, line 180
def delete_file(file)
  begin
    file.delete
  rescue Google::Cloud::NotFoundError
    # we're fine if the file was already deleted
  end
end
get_bucket(bucket_name = @bucket) click to toggle source
# File lib/shrine/storage/google_cloud_storage.rb, line 150
def get_bucket(bucket_name = @bucket)
  storage.bucket(bucket_name, skip_lookup: true)
end
get_file(id) click to toggle source
# File lib/shrine/storage/google_cloud_storage.rb, line 146
def get_file(id)
  get_bucket.file(object_name(id))
end
storage() click to toggle source

@see googlecloudplatform.github.io/google-cloud-ruby/#/docs/google-cloud-storage/v1.6.0/guides/authentication

# File lib/shrine/storage/google_cloud_storage.rb, line 155
def storage
  return @storage if @storage

  opts = {}
  opts[:project] = @project if @project
  opts[:credentials] = @credentials if @credentials

  @storage = Google::Cloud::Storage.new(**opts)
end
with_file(io) { |tempfile| ... } click to toggle source
# File lib/shrine/storage/google_cloud_storage.rb, line 171
def with_file(io)
  if io.respond_to?(:tempfile) # ActionDispatch::Http::UploadedFile
    yield io.tempfile
  else
    Shrine.with_file(io) { |file| yield file }
  end
end