Class: Sprout::ArchiveUnpacker

Inherits:
Object
  • Object
show all
Defined in:
sprout/lib/sprout/archive_unpacker.rb

Overview

Given a source, destination and archive type (or ability to infer it), unpack the provided archive.

unpacker = Sprout::ArchiveUnpacker.new
unpacker.unpack "Foo.zip", "unpacked/"

Instance Method Summary (collapse)

Instance Method Details

- (File) copy_file(file, destination, clobber = nil)

Rather than unpacking, safely copy the file from one location to another. This method is generally used when .exe files are downloaded directly.

Returns:

  • (File)

    the file or directory that was created.



125
126
127
128
129
130
131
132
133
134
135
# File 'sprout/lib/sprout/archive_unpacker.rb', line 125

def copy_file file, destination, clobber=nil
  validate file, destination
  target = File.expand_path( File.join(destination, File.basename(file)) )
  if(File.exists?(target) && clobber != :clobber)
    raise Sprout::Errors::DestinationExistsError.new "Unable to copy #{file} to #{target} because target already exists and we were not asked to :clobber it"
  end
  FileUtils.mkdir_p destination
  FileUtils.cp_r file, destination

  destination
end

- (Boolean) is_darwin?

Return true if we're on a Darwin native system (OSX).

Returns:

  • (Boolean)


72
73
74
# File 'sprout/lib/sprout/archive_unpacker.rb', line 72

def is_darwin?
  Sprout.current_system.is_a?(Sprout::System::OSXSystem)
end

- (Boolean) is_exe?(archive, type = nil)

Return true if the downloaded archive is a .exe file or the type argument is :exe.

Returns:

  • (Boolean)


154
155
156
# File 'sprout/lib/sprout/archive_unpacker.rb', line 154

def is_exe? archive, type=nil
  type == :exe || !archive.match(/\.exe$/).nil?
end

- (Boolean) is_rb?(archive, type = nil)

Return true if the downloaded archive is a .rb file or the type argument is :rb.

Returns:

  • (Boolean)


168
169
170
# File 'sprout/lib/sprout/archive_unpacker.rb', line 168

def is_rb? archive, type=nil
  type == :rb || !archive.match(/\.rb$/).nil?
end

- (Boolean) is_swc?(archive, type = nil)

Return true if the downloaded archive is a .swc file or the type argument is :swc.

Returns:

  • (Boolean)


161
162
163
# File 'sprout/lib/sprout/archive_unpacker.rb', line 161

def is_swc? archive, type=nil
  type == :swc || !archive.match(/\.swc$/).nil?
end

- (Boolean) is_tgz?(archive, type = nil)

Return true if the provided file name looks like a tar.gz file or the type argument is :tgz.

Returns:

  • (Boolean)


147
148
149
# File 'sprout/lib/sprout/archive_unpacker.rb', line 147

def is_tgz? archive, type=nil
  type == :tgz || !archive.match(/\.tgz$/).nil? || !archive.match(/\.tar.gz$/).nil?
end

- (Boolean) is_zip?(archive, type = nil)

Returns true if the provided file name looks like a zip file or the type argument is :zip.

Returns:

  • (Boolean)


140
141
142
# File 'sprout/lib/sprout/archive_unpacker.rb', line 140

def is_zip? archive, type=nil
  type == :zip || !archive.match(/\.zip$/).nil?
end

- (String) unpack(archive, destination, type = nil, clobber = nil)

Unpack the provided archive into the provided destination.

If a type is not provided, a type will be inferred from the file name suffix.

Parameters:

  • (File) archive

    Path to the archive that will be unpacked (or copied)

  • (Path) destination

    Path to the folder where unpacked files should be placed (or copied).

  • (Symbol) type (defaults to: nil)

    The type of the archive in cases where it can't be inferred from the name. Acceptable values are: :zip, :tgz, :swc, :exe or :rb

  • (Boolean) clobber (defaults to: nil)

    If the destination already contains the expected file(s), the unpacker will not run unless clobber is true.

Returns:

  • (String)

    path to the unpacked files (usually same as destination).

Raises:

  • Sprout::Errors::UnknownArchiveType If the archive type cannot be inferred and a valid type is not provided.



27
28
29
30
31
32
33
34
35
36
37
38
# File 'sprout/lib/sprout/archive_unpacker.rb', line 27

def unpack archive, destination, type=nil, clobber=nil
  return unpack_zip(archive, destination, clobber) if is_zip?(archive, type)
  return unpack_tgz(archive, destination, clobber) if is_tgz?(archive, type)

  # This is definitely debatable, should we copy the file even if it's
  # not an archive that we're about to unpack?
  # If so, why would we only do this with some subset of file types?
  # Opinions welcome here...
  return copy_file(archive, destination, clobber)  if is_copyable?(archive)

  raise Sprout::Errors::UnknownArchiveType.new("Unsupported or unknown archive type encountered with: #{archive}")
end

- (File) unpack_tgz(archive, destination, clobber = nil)

Unpack tar.gz or .tgz files on any platform.

Returns:

  • (File)

    the file or directory that was created.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'sprout/lib/sprout/archive_unpacker.rb', line 97

def unpack_tgz archive, destination, clobber=nil
  validate archive, destination

  tar = Zlib::GzipReader.new(File.open(archive, 'rb'))
  if(!should_unpack_tgz?(destination, clobber))
    raise Sprout::Errors::DestinationExistsError.new "Unable to unpack #{archive} into #{destination} without explicit :clobber argument"
  end

  Archive::Tar::Minitar.unpack(tar, destination)

  # Recurse and unpack gzipped children (Adobe did this double 
  # gzip with the Linux FlashPlayer for some weird reason)
  ["#{destination}/**/*.tgz", "#{destination}/**/*.tar.gz"].each do |pattern|
    Dir.glob(pattern).each do |child|
      if(child != archive && dir != File.dirname(child))
        unpack_tgz(child, File.dirname(child))
      end
    end
  end
end

- (File) unpack_zip(archive, destination, clobber = nil)

Unpack zip archives on any platform using whatever strategy is most efficient and reliable.

Parameters:

  • (File) archive

    Path to the archive that will be unpacked.

  • (Path) destination

    Path to the folder where unpacked files should be placed.

  • (Boolean) clobber (defaults to: nil)

    If the destination already contains the expected file(s), the unpacker will not run unless clobber is true.

Returns:

  • (File)

    the file or directory that was created.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'sprout/lib/sprout/archive_unpacker.rb', line 49

def unpack_zip archive, destination, clobber=nil
  validate archive, destination

  ##
  # As it turns out, the Rubyzip library corrupts
  # binary files (like the Flash Player) on OSX and is also
  # horribly slow for large archives (like the ~120MB Flex SDK)
  # on all platforms.
  if is_darwin?
    unpack_zip_on_darwin archive, destination, clobber
  else
    Zip::ZipFile.open archive do |zipfile|
      zipfile.each do |entry|
        next if entry.name =~ /__MACOSX/ or entry.name =~ /\.DS_Store/
          unpack_zip_entry entry, destination, clobber
      end
    end
  end
end

- (File) unpack_zip_on_darwin(archive, destination, clobber)

Optimization for zip files on OSX. Uses the native 'unzip' utility which is much faster (and more reliable) than Ruby for large archives (like the Flex SDK) and binaries that Ruby corrupts (like the Flash Player).

Returns:

  • (File)

    the file or directory that was created.



83
84
85
86
87
88
89
90
91
# File 'sprout/lib/sprout/archive_unpacker.rb', line 83

def unpack_zip_on_darwin archive, destination, clobber
  # Unzipping on OS X
  FileUtils.makedirs destination
  zip_dir  = File.expand_path File.dirname(archive)
  zip_name = File.basename archive
  output   = File.expand_path destination
  # puts ">> zip_dir: #{zip_dir} zip_name: #{zip_name} output: #{output}"
  %x(cd #{zip_dir};unzip #{zip_name} -d #{output})
end