class ParallelInstaller
def self.call(*args)
def self.call(*args) new(*args).call end
def self.max_threads
def self.max_threads [Bundler.settings[:jobs].to_i - 1, 1].max end
def call
def call enqueue_specs process_specs until @specs.all?(&:installed?) ensure worker_pool && worker_pool.stop end
def collect_post_install_message(spec)
def collect_post_install_message(spec) Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message end
def enqueue_specs
previously installed specifications. We continue until all specs
Later we call this lambda again to install specs that depended on
We enqueue all gem specs that do not have any dependencies.
Keys in the remains hash represent uninstalled gems specs.
def enqueue_specs @specs.select(&:ready_to_enqueue?).each do |spec| if spec.dependencies_installed? @specs worker_pool.enq spec spec.state = :enqueued end end end
def initialize(installer, all_specs, size, standalone, force)
def initialize(installer, all_specs, size, standalone, force) @installer = installer @size = size @standalone = standalone @force = force @specs = all_specs.map {|s| SpecInstallation.new(s) } end
def process_specs
processed so the call to `enqueue_specs` is important after every
Some specs might've had to wait til this spec was installed to be
remaining specs.
Dequeue a spec and save its post-install message and then enqueue the
def process_specs spec = worker_pool.deq spec.state = :installed collect_post_install_message spec if spec.has_post_install_message? enqueue_specs end
def worker_pool
def worker_pool @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda { |spec_install, worker_num| message = Bundler::GemInstaller.new( spec_install.spec, @installer, @standalone, worker_num, @force ).install_from_spec spec_install.post_install_message = message unless message.nil? spec_install } end