Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Fix boolean bundle gem settings #3578

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/bundler/cli/gem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def ask_and_set(key, header, message)

if choice.nil?
Bundler.ui.confirm header
choice = (Bundler.ui.ask("#{message} y/(n):") =~ /y|yes/)
choice = Bundler.ui.yes? "#{message} y/(n):"
Bundler.settings.set_global("gem.#{key}", choice)
end

Expand Down
44 changes: 2 additions & 42 deletions lib/bundler/installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def run(options)
# installation is just SO MUCH FASTER. so we let people opt in.
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
if jobs > 1 && can_install_in_parallel?
require 'bundler/installer/parallel_installer'
install_in_parallel jobs, options[:standalone], force
else
install_sequentially options[:standalone], force
Expand Down Expand Up @@ -277,48 +278,7 @@ def install_sequentially(standalone, force = false)
end

def install_in_parallel(size, standalone, force = false)
name2spec = {}
remains = {}
enqueued = {}
specs.each do |spec|
name2spec[spec.name] = spec
remains[spec.name] = true
end

worker_pool = Worker.new size, lambda { |name, worker_num|
spec = name2spec[name]
message = install_gem_from_spec spec, standalone, worker_num, force
{ :name => spec.name, :post_install => message }
}

# Keys in the remains hash represent uninstalled gems specs.
# We enqueue all gem specs that do not have any dependencies.
# Later we call this lambda again to install specs that depended on
# previously installed specifications. We continue until all specs
# are installed.
enqueue_remaining_specs = lambda do
remains.keys.each do |name|
next if enqueued[name]
spec = name2spec[name]
if ready_to_install?(spec, remains)
worker_pool.enq name
enqueued[name] = true
end
end
end
enqueue_remaining_specs.call

until remains.empty?
message = worker_pool.deq
remains.delete message[:name]
if message[:post_install]
Installer.post_install_messages[message[:name]] = message[:post_install]
end
enqueue_remaining_specs.call
end
message
ensure
worker_pool && worker_pool.stop
ParallelInstaller.call(self, specs, size, standalone, force)
end

# We only want to install a gem spec if all its dependencies are met.
Expand Down
117 changes: 117 additions & 0 deletions lib/bundler/installer/parallel_installer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
require 'bundler/worker'


class ParallelInstaller

class SpecInstallation

attr_accessor :spec, :name, :post_install_message, :state
def initialize(spec)
@spec, @name = spec, spec.name
@state = :none
@post_install_message = ""
end

def installed?
state == :installed
end

def enqueued?
state == :enqueued
end

# Only true when spec in neither installed nor already enqueued
def ready_to_enqueue?
!installed? && !enqueued?
end

def has_post_install_message?
post_install_message.empty?
end

def ignorable_dependency?(dep)
dep.type == :development || dep.name == @name
end

# Checks installed dependencies against spec's dependencies to make
# sure needed dependencies have been installed.
def dependencies_installed?(remaining_specs)
installed_specs = remaining_specs.reject(&:installed?).map(&:name)
already_installed = lambda {|dep| installed_specs.include? dep.name }
dependencies.all? {|d| already_installed[d] }
end

# Represents only the non-development dependencies and the ones that
# are itself.
def dependencies
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
end

# Represents all dependencies
def all_dependencies
@spec.dependencies
end
end

def self.call(*args)
new(*args).call
end

# Returns max number of threads machine can handle with a min of 1
def self.max_threads
[Bundler.settings[:jobs].to_i-1, 1].max
end

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 call
enqueue_specs
process_specs until @specs.all?(&:installed?)
ensure
worker_pool && worker_pool.stop
end

def worker_pool
@worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num|
message = @installer.install_gem_from_spec spec_install.spec, @standalone, worker_num, @force
spec_install.post_install_message = message unless message.nil?
spec_install
}
end

# Dequeue a spec and save its post-install message anf then enqueue the
# remaining specs.
# Some specs might've had to wait til this spec was installed to be
# processed so the call to `enqueue_specs` is important after every
# dequeue.
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 collect_post_install_message(spec)
Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message
end

# Keys in the remains hash represent uninstalled gems specs.
# We enqueue all gem specs that do not have any dependencies.
# Later we call this lambda again to install specs that depended on
# previously installed specifications. We continue until all specs
# are installed.
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
end
8 changes: 8 additions & 0 deletions lib/bundler/ui/shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ def ask(msg)
@shell.ask(msg)
end

def yes?(msg)
@shell.yes?(msg)
end

def no?(msg)
@shell.no?(msg)
end

def level=(level)
raise ArgumentError unless LEVELS.include?(level.to_s)
@level = level
Expand Down
37 changes: 37 additions & 0 deletions spec/install/parallel/spec_installation_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require 'spec_helper'
require 'bundler/installer/parallel_installer'

describe ParallelInstaller::SpecInstallation do
describe "#ready_to_enqueue?" do

let!(:dep) do
a_spec = Object.new
def a_spec.name
"I like tests"
end
a_spec
end

context "when in enqueued state" do
it "is falsey" do
spec = ParallelInstaller::SpecInstallation.new(dep)
spec.state = :enqueued
expect(spec.ready_to_enqueue?).to be_falsey
end
end

context "when in installed state" do
it "returns falsey" do
spec = ParallelInstaller::SpecInstallation.new(dep)
spec.state = :installed
expect(spec.ready_to_enqueue?).to be_falsey
end
end

it "returns truthy" do
spec = ParallelInstaller::SpecInstallation.new(dep)
expect(spec.ready_to_enqueue?).to be_truthy
end
end

end