class Gauntlet

Constants

DATADIR
GEMDIR
GEMURL
VERSION

Attributes

data[RW]
data_file[RW]
dirty[RW]

Public Class Methods

new() click to toggle source
# File lib/gauntlet.rb, line 20
def initialize
  name = self.class.name.downcase.sub(/gauntlet/, '')
  self.data_file = "#{DATADIR}/#{name}-data.yml"
  self.dirty = false
  @cache = nil
end

Public Instance Methods

each_gem(filter = nil) { |basename(tgz, ".tgz")| ... } click to toggle source
# File lib/gauntlet.rb, line 183
def each_gem filter = nil
  filter ||= /^[\w-]+-\d+(\.\d+)*\.tgz$/
  in_gem_dir do
    Dir["*.tgz"].each do |tgz|
      next unless tgz =~ filter

      yield File.basename(tgz, ".tgz")
    end
  end
end
in_gem_dir() { || ... } click to toggle source
# File lib/gauntlet.rb, line 223
def in_gem_dir
  Dir.chdir GEMDIR do
    yield
  end
end
initialize_dir() click to toggle source
# File lib/gauntlet.rb, line 27
def initialize_dir
  Dir.mkdir GEMDIR unless File.directory? GEMDIR
  Dir.mkdir DATADIR unless File.directory? DATADIR
  in_gem_dir do
    File.symlink ".", "cache" unless File.exist? "cache"
  end
end
latest_gems() click to toggle source
# File lib/gauntlet.rb, line 54
def latest_gems
  list, _ = Gem::SpecFetcher.new.available_specs(:latest)
  list.map { |source, gems|
    gems.map { |tuple|
      gem_name = File.basename(tuple.spec_name, '.gemspec')
      [gem_name, source.uri.merge("/gems/#{gem_name}.gem")]
    }
  }.flatten(1)
end
load_yaml(path, default = {}) click to toggle source
# File lib/gauntlet.rb, line 211
def load_yaml path, default = {}
  YAML.load(File.read(path)) rescue default
end
run(name) click to toggle source

Override this to customize gauntlet. See #run_the_gauntlet for other ways of adding behavior.

# File lib/gauntlet.rb, line 233
def run name
  raise "subclass responsibility"
end
run_the_gauntlet(filter = nil) { |name| ... } click to toggle source

This is the main driver for gauntlet. filter allows you to pass in a regexp to only run against a subset of the gems available. You can pass a block to #run_the_gauntlet or it will call run. Both are passed the name of the gem and are executed within the gem directory.

# File lib/gauntlet.rb, line 251
def run_the_gauntlet filter = nil
  initialize_dir
  update_gem_tarballs if $u

  self.data ||= load_yaml data_file

  outdateds = self.data.keys - in_gem_dir do
    Dir["*.tgz"].map { |tgz| File.basename(tgz, ".tgz") }
  end

  outdateds.each do |outdated|
    self.data.delete outdated
  end

  %w[TERM KILL].each do |signal|
    trap signal do
      shutdown
      exit
    end
  end

  each_gem filter do |name|
    next if should_skip? name
    with_gem name do
      if block_given? then
        yield name
      else
        run name
      end
    end
  end
rescue Interrupt
  warn "user cancelled. quitting"
ensure
  shutdown
end
save_yaml(path, data) click to toggle source
# File lib/gauntlet.rb, line 215
def save_yaml path, data
  File.open("#{path}.new", 'w') do |f|
    warn "*** saving #{path}"
    YAML.dump data, f
  end
  File.rename "#{path}.new", path
end
should_skip?(name) click to toggle source

Override this to return true if the gem should be skipped.

# File lib/gauntlet.rb, line 240
def should_skip? name
  self.data[name]
end
shutdown() click to toggle source
# File lib/gauntlet.rb, line 288
def shutdown
  save_yaml data_file, data if dirty
end
source_index() click to toggle source
# File lib/gauntlet.rb, line 35
def source_index
  @index ||= in_gem_dir do
    dump = if ($u and not $F) or not File.exist? '.source_index' then
             warn "fetching and caching gem index"
             url = GEMURL + "Marshal.#{Gem.marshal_version}.Z"
             dump = Gem::RemoteFetcher.fetcher.fetch_path url
             require 'zlib' # HACK for rubygems :(
             dump = Gem.inflate dump
             open '.source_index', 'wb' do |io| io.write dump end
             dump
           else
             warn "using cached gem index"
             open '.source_index', 'rb' do |io| io.read end
           end

    Marshal.load dump
  end
end
update_gem_tarballs() click to toggle source
# File lib/gauntlet.rb, line 64
def update_gem_tarballs
  initialize_dir

  latest = self.latest_gems

  warn "updating mirror"

  in_gem_dir do
    gems = Dir["*.gem"]
    tgzs = Dir["*.tgz"]

    old = tgzs - latest.map { |(full_name, _url)| "#{full_name}.tgz" }
    unless old.empty? then
      warn "deleting #{old.size} tgzs"
      old.each do |tgz|
        File.unlink tgz
      end
    end

    conversions = Queue.new
    gem_names = gems.map { |gem| File.basename gem, '.gem' }
    tgz_names = tgzs.map { |tgz| File.basename tgz, '.tgz' }
    to_convert = gem_names - tgz_names

    seen_tgzs = Hash[tgzs.map { |name| [name, true] }]

    warn "adding #{to_convert.size} unconverted gems" unless to_convert.empty?

    conversions.push to_convert.shift until to_convert.empty? # LAME

    downloads = Queue.new
    latest = latest.sort_by { |(full_name, _url)| full_name.downcase }
    latest.reject! { |(full_name, _url)| seen_tgzs["#{full_name}.tgz"] }

    downloads.push(latest.shift) until latest.empty? # LAME

    converter = Thread.start do
      while payload = conversions.shift do
        full_name, _ = payload
        tgz_name  = "#{full_name}.tgz"
        gem_name  = "#{full_name}.gem"

        warn " converting #{gem_name} to tarball"

        unless File.directory? full_name then
          system "gem unpack cache/#{gem_name} > /dev/null 2>&1"
          system "gem spec -l cache/#{gem_name} > #{full_name}/gemspec"
        end

        system ["chmod -R u+rwX \"#{full_name}\"",
                "tar zmcf #{tgz_name} -- #{full_name}",
                "rm -rf -- #{full_name} #{gem_name}"].join(" && ")
      end
    end

    warn "fetching #{downloads.size} gems"

    workers downloads do |http, (full_name, url)|
      gem_name  = "#{full_name}.gem"

      unless gems.include? gem_name then
        limit = 3
        begin
          warn "downloading #{full_name}"
          while limit > 0 do
            http.request url do |response|
              case response
              when Net::HTTPSuccess
                File.open gem_name, "wb" do |f|
                  response.read_body do |chunk|
                    f.write chunk
                  end
                end
                limit = 0 # kinda lame.
              when Net::HTTPRedirection
                url = URI.parse(response['location'])
                limit -= 1
              else
                warn "  #{full_name} got #{response.code}. skipping."
                limit = 0
              end
            end
          end
        rescue SocketError, OpenSSL::SSL::SSLError, Net::HTTP::Persistent::Error => e
          warn "  #{full_name} raised #{e.message}. skipping."
        end
      end

      conversions.push full_name
    end

    conversions.push nil

    converter.join
  end

rescue Interrupt
  warn "user cancelled... quitting"
  exit 1
end
with_gem(name) { |name| ... } click to toggle source
# File lib/gauntlet.rb, line 194
def with_gem name
  in_gem_dir do
    process_dir = "#{$$}"
    begin
      Dir.mkdir process_dir
      Dir.chdir process_dir do
        system "tar zxmf ../#{name}.tgz 2> /dev/null"
        Dir.chdir name do
          yield name
        end
      end
    ensure
      system "rm -rf -- #{process_dir}"
    end
  end
end
workers(tasks, count = 10) { |http, task| ... } click to toggle source
# File lib/gauntlet.rb, line 165
def workers tasks, count = 10
  threads = []
  count.times do
    threads << Thread.new do
      http = Net::HTTP::Persistent.new

      until tasks.empty? do
        task = tasks.shift
        yield http, task
      end
    end
  end

  threads.each do |thread|
    thread.join
  end
end