stefan-kolb/nucleus

View on GitHub
lib/nucleus/core/common/files/archiver.rb

Summary

Maintainability
A
2 hrs
Test Coverage
module Nucleus
  class Archiver
    def initialize(exclude_git = true)
      @exclude_git = exclude_git
    end

    # Compress the files of the path into an archive, using the compression format,
    # which indicates which method must be used to compress the archive.
    # @param [String] path which directory's contents are going to be compressed into the archive
    # @param [String] compression_format represented by well-known file extensions, e.g. zip or tar.gz
    # @raise [StandardError] if the compression_format is not supported and the directory can't be compressed
    # @return [StringIO] compressed data of the given input path
    def compress(path, compression_format)
      compression_method = compression_format.downcase.tr('.', '_').underscore.to_sym
      unless respond_to?(compression_method, true)
        raise StandardError,
              "Unsupported compression format #{compression_format}"
      end
      send(compression_method, path)
    end

    private

    def tar(path)
      string_io = StringIO.new('')
      Gem::Package::TarWriter.new(string_io) do |tar|
        Find.find(path) do |file|
          # do not include the git files
          next if @exclude_git && file.start_with?("#{path}/.git")

          mode = File.stat(file).mode
          relative_file = file.sub(%r{^#{Regexp.escape path}\/?}, '')

          if File.directory?(file)
            tar.mkdir relative_file, mode
          else
            tar.add_file relative_file, mode do |tf|
              File.open(file, 'rb') { |f| tf.write f.read }
            end
          end
        end
      end
      string_io.rewind
      string_io
    end

    def tar_gz(path)
      tar_file = tar(path)
      begin
        gz = StringIO.new('')
        z = Zlib::GzipWriter.new(gz)
        z.write tar_file.string
      ensure
        z.close unless z.nil?
      end

      # z was closed to write the gzip footer, so
      # now we need a new StringIO
      StringIO.new gz.string
    end

    def zip(path)
      string_io = Zip::OutputStream.write_buffer do |zio|
        Find.find(path) do |file|
          # do not process directories && do not include the Git DB files
          next if File.directory?(file) || (@exclude_git && file.start_with?("#{path}/.git"))

          relative_file = file.sub(%r{^#{Regexp.escape path}\/?}, '')
          zio.put_next_entry(relative_file)
          File.open(file, 'rb') do |f|
            zio.write f.read
          end
        end
      end
      string_io.rewind
      string_io
    end

    def write_zip_entries(path, sub_path, io)
      search_path = File.join(path, sub_path)
      Find.find(path) do |file|
        zip_file_path = file.sub(%r{^#{Regexp.escape search_path}\/?}, '')
        next if @exclude_git && zip_file_path.start_with?('.git')
        if File.directory?(file)
          io.mkdir(zip_file_path)
          write_zip_entries(path, zip_file_path, io)
        else
          io.get_output_stream(zip_file_path) { |f| f.print(File.open(file, 'rb').read) }
        end
      end
    end
  end
end