sanger/sequencescape

View on GitHub
app/uploaders/polymorphic_uploader.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
92%
# frozen_string_literal: true
require 'carrierwave'

module CarrierWave
  module Storage
    # Database storage - puts the file contents in a database table
    class DirectDatabase < Abstract
      # Store: Takes a file object, passes it to a file wrapper class which handles storage in the DB
      def store!(file)
        f = CarrierWave::Storage::DirectDatabase::File.new(uploader, self, uploader.store_path)
        f.store(file.read)
        f
      end

      # Retrieve: Returns a file wrapper which accesses the database via the passed model
      def retrieve!(identifier)
        CarrierWave::Storage::DirectDatabase::File.new(uploader, self, uploader.store_path(identifier))
      end

      class File
        def initialize(uploader, base, path)
          @uploader = uploader
          @path = path
          @base = base
        end

        # Returns the current path of the file
        attr_reader :path

        delegate :size, to: :current_data

        # Reads the contents of the file
        def read
          current_data
        end

        # Remove the file
        def delete
          destroy_file
        end

        # Would returns the url
        def url
          raise NotImplementedError, 'Files are stored in the database, so are not available directly through a URL'
        end

        # Stores the file in the DbFiles model - split across many rows if size > 200KB
        def store(file)
          @uploader.model.db_files.create!(data: file)
        end

        # Error handling should help if uploader was mounted to a model with no content_type
        def content_type
          @uploader.model.content_type if @uploader.model.respond_to? :content_type
        end

        def content_type=(type)
          @uploader.model.content_type = type unless type.nil? if @uploader.model.respond_to? :content_type
        end

        private

        # Gets the current data from the database
        # Data used to be chunked into 200kb size fragments. This is no longer the
        # case, but the older files have not been updated.
        def current_data
          @uploader.model.db_files.pluck(:data).join
        end

        # Destroys the file. Called in the after_destroy callback
        def destroy_file
          @uploader.model.db_files.each(&:delete)
        end
      end
    end
  end
end

class PolymorphicUploader < CarrierWave::Uploader::Base
  def initialize(*args, &block)
    super
  end

  def exists?
    @column.blank?
  end

  storage CarrierWave::Storage::DirectDatabase
  cache_storage CarrierWave::Storage::File

  # This is where files are stored on upload. We are using callbacks to empty it after upload
  def self.cache_dir
    "#{Rails.root}/tmp/uploads"
  end

  def cache_dir
    self.class.cache_dir
  end

  before :store, :remember_cache_id
  after :store, :delete_tmp_dir

  # store! nils the cache_id after it finishes so we need to remember it for deletion
  def remember_cache_id(_new_file)
    @cache_id_was = cache_id
  end

  def delete_tmp_dir(_new_file)
    # make sure we don't delete other things accidentally by checking the name pattern
    FileUtils.rm_rf(File.join(cache_dir, @cache_id_was)) if @cache_id_was.present? && @cache_id_was =~ /\A[\d-]*\z/
  end
end