Downloader

Tool module for downloading files and extracting archive files. Currently this is console-based but in the future will have SOC for any interface.

Methods
Attributes
[RW] destination
[RW] region
Public Class methods
new( destination, mirrors=nil, region=nil, &config )
# File lib/more/facets/downloader.rb, line 66
  def initialize( destination, mirrors=nil, region=nil, &config )
    @destination = destination
    @mirrors = []
    add_mirrors( mirrors ) if mirrors
    @region = (region || 'US').to_s
    config.call(self) if config
  end
Public Instance methods
add_mirror( url, region=nil )
# File lib/more/facets/downloader.rb, line 76
  def add_mirror( url, region=nil )
    @mirrors << Mirror.new( url, region )
  end
add_mirrors( mirrors )
# File lib/more/facets/downloader.rb, line 80
  def add_mirrors( mirrors )
    mirrors.each { |mirror|
      case mirror
      when String
        url, rgn = *mirror.strip.split(' ')
        add_mirror( url, rgn.unbracket )
      when Array
        add_mirror( *mirror )
      when Hash
        add_mirror( mirror[:url], mirror[:region] )
      when Mirror
        @mirrors << mirror
      else
        raise "unrecogized mirror definition #{mirror.inspect}"
      end
    }
  end
compute_checksum( local_path )
# File lib/more/facets/downloader.rb, line 240
  def compute_checksum( local_path )
    if File.exists?( local_path )
      File.open( local_path ) do |local_file|
        return Digest::MD5.new( local_file.read ).hexdigest
      end
    end
  end
download( url, filepath, checksum='', est_size=nil )

currently can only download a single compressed file does not handle downloading an uncompressed directory tree (should it? doubt it)

currently this displays progress to STDOUT; either their should be a way to activate/deactivate or preferably use ducktype singletons (more on that later, see google://_whytheluckystiff if interested) of course I prefer chain messaging but matz said no :(

# File lib/more/facets/downloader.rb, line 179
  def download( url, filepath, checksum='', est_size=nil )

    checksum = checksum.to_s.strip
    est_size = nil if est_size == 0

    download_complete = nil

    if interface
      interface.preparing_to_download( File.basename( filepath ), url, est_size )
    end

    progress_total = est_size ? est_size : 100000000  # pretend 100MB if no size
    pbar = ProgressBar.new( "Status", progress_total, STDOUT )
    pbar.bar_mark = "="
    pbar.format = "%-6s %3d%% %s %s"
    pbar.file_transfer_mode if est_size

    progress_proc = proc { |posit| pbar.set(posit) }

    STDOUT.sync = true
    begin
      local_file = File.open( filepath, 'wb' )
      remote_file = open( url, :progress_proc => progress_proc )
      local_file << remote_file.read
    rescue
      pbar.halt
      download_complete = nil
      raise
    else
      pbar.finish
      download_complete = filepath
    ensure
      remote_file.close unless remote_file.nil?
      local_file.close unless local_file.nil?
      STDOUT.sync = false
    end

    unless checksum.empty?
      raise ChecksumError if compute_checksum(filepath) != checksum
    end

    if interface
      if checksum.empty?
        interface.lacks_checksum( compute_checksum(filepath), :md5 )
      end
      unless est_size
        interface.lacks_size( File.size(filepath) )
      end
    end

    if download_complete
      if interface
        interface.downloaded( filepath )
      end
    end

    return download_complete
  end
extract( local_path )
# File lib/more/facets/downloader.rb, line 250
  def extract( local_path )
    success = false
    local_dir = File.dirname(local_path)
    local_file = File.basename(local_path)
    current_dir = Dir.getwd
    begin
      Dir.chdir(local_dir)
      case local_file
        when /.*gz$/
          system "tar -xzf #{local_file}"
        when /.*bz2$/
          system "tar -xjf #{local_file}"
        when /.zip$/
          system "unzip #{local_file}"
        else
          success = false
      end
    rescue
      success = false
    else
      success = true
    ensure
      Dir.chdir(current_dir)
    end
    if interface
      interface.extracted( local_path)
    end
    return success
  end
fetch( file, checksum=0, est_size=0, force=false )
# File lib/more/facets/downloader.rb, line 104
  def fetch( file, checksum=0, est_size=0, force=false )
    urls = mirrors.collect { |m| "#{m.url.chomp('/')}/#{file}" }
    urls = prioritize_urls( urls )
    filepath = "#{destination.chomp('/')}/#{file}"
    monitored_download( urls, filepath, checksum, est_size, force )
  end
interface()

delegate to an interface (under development)

# File lib/more/facets/downloader.rb, line 61
  def interface ; @interface; end
interface=(iface)
# File lib/more/facets/downloader.rb, line 62
  def interface=(iface)
    @interface = iface
  end
mirrors()
# File lib/more/facets/downloader.rb, line 98
  def mirrors
    @mirrors.sort{ |a,b| a.region == region ? 1 : ( b.region == region ? -1 : 0 ) }
  end
monitored_download( urls, filepath, checksum, est_size=0, force=false )

regional_urls - array of arrays of [ url, region, md5, expected_size ] local_region - region of the user‘s system to_dir - where to store downloaded file (full path) force - download even if file already exists locally

# File lib/more/facets/downloader.rb, line 125
  def monitored_download( urls, filepath, checksum, est_size=0, force=false )

    checksum = checksum.to_s.strip
    est_size = nil if est_size == 0

    success=nil

    # source file exists and passes checksum then we need not fetch
    #file_path = File.join(to_dir,File.basename(url[0]))
    if File.exists?(filepath)
      if compute_checksum(filepath) == checksum and ! force
        interface.report("File has already been fetched and passes checksum.")
        success = filepath
      else
        File.delete(filepath)
      end
    end

    unless success
      # download
      urls.each do |url|
        begin
          #file_path = File.join(to_dir,File.basename(url[0]))
          #file_checksum = url[2].to_s.strip
          #file_size = url[3].to_i
          success = self.download( url, filepath, checksum, est_size )
          break if success
        rescue
          next
        end
      end
    end

    return success
  end
prioritize_urls( urls )

In the future we may test each connection for fastest download

# File lib/more/facets/downloader.rb, line 163
  def prioritize_urls( urls )
    urls
    # put local region first
    #prioritized_urls = regional_urls.find_all { |a| a[1] == local_region }
    #prioritized_urls.concat regional_urls.find_all { |a| a[1] != local_region }
    #return prioritized_urls
  end