summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Giddins <segiddins@segiddins.me>2017-04-06 18:56:26 -0500
committerSamuel Giddins <segiddins@segiddins.me>2017-04-07 12:02:55 -0500
commit8b95a4858e15cf667c70db8c4d9c0333a28b55e8 (patch)
treef5a5186c0e6838415e8a35e96194107f936e3c32
parent32fb8327328789bdc911dccda4a59e99956c558c (diff)
downloadbundler-8b95a4858e15cf667c70db8c4d9c0333a28b55e8.tar.gz
[StubSpecification] Avoid loading the full spec when possible
-rw-r--r--lib/bundler/lazy_specification.rb1
-rw-r--r--lib/bundler/remote_specification.rb10
-rw-r--r--lib/bundler/rubygems_integration.rb3
-rw-r--r--lib/bundler/stub_specification.rb58
-rw-r--r--spec/runtime/require_spec.rb23
5 files changed, 92 insertions, 3 deletions
diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb
index 231bffcae8..46f50844e1 100644
--- a/lib/bundler/lazy_specification.rb
+++ b/lib/bundler/lazy_specification.rb
@@ -79,6 +79,7 @@ module Bundler
"To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again."
search = source.specs.search(self).last
end
+ search.dependencies = dependencies if search.is_a?(RemoteSpecification)
search
end
end
diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb
index e5f9c78b00..c689af46c5 100644
--- a/lib/bundler/remote_specification.rb
+++ b/lib/bundler/remote_specification.rb
@@ -11,6 +11,7 @@ module Bundler
include Comparable
attr_reader :name, :version, :platform
+ attr_writer :dependencies
attr_accessor :source, :remote
def initialize(name, version, platform, spec_fetcher)
@@ -18,6 +19,7 @@ module Bundler
@version = Gem::Version.create version
@platform = platform
@spec_fetcher = spec_fetcher
+ @dependencies = nil
end
# Needed before installs, since the arch matters then and quick
@@ -76,8 +78,16 @@ module Bundler
"#<#{self.class} name=#{name} version=#{version} platform=#{platform}>"
end
+ def dependencies
+ @dependencies || method_missing(:dependencies)
+ end
+
private
+ def to_ary
+ nil
+ end
+
def _remote_specification
@_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
@_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \
diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb
index 86912376c1..666d36a346 100644
--- a/lib/bundler/rubygems_integration.rb
+++ b/lib/bundler/rubygems_integration.rb
@@ -323,11 +323,12 @@ module Bundler
def replace_gem(specs, specs_by_name)
reverse_rubygems_kernel_mixin
- executables = specs.map(&:executables).flatten
+ executables = nil
kernel = (class << ::Kernel; self; end)
[kernel, ::Kernel].each do |kernel_class|
redefine_method(kernel_class, :gem) do |dep, *reqs|
+ executables ||= specs.map(&:executables).flatten
if executables.include? File.basename(caller.first.split(":").first)
break
end
diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb
index cbcadee269..9e43f06364 100644
--- a/lib/bundler/stub_specification.rb
+++ b/lib/bundler/stub_specification.rb
@@ -9,12 +9,14 @@ module Bundler
spec
end
- attr_accessor :stub
+ attr_accessor :stub, :ignored
def to_yaml
_remote_specification.to_yaml
end
+ # @!group Stub Delegates
+
if Bundler.rubygems.provides?(">= 2.3")
# This is defined directly to avoid having to load every installed spec
def missing_extensions?
@@ -22,10 +24,62 @@ module Bundler
end
end
+ def activated
+ stub.activated
+ end
+
+ def activated=(activated)
+ stub.instance_variable_set(:@activated, activated)
+ end
+
+ def default_gem
+ stub.default_gem
+ end
+
+ def full_gem_path
+ stub.full_gem_path
+ end
+
+ def full_require_paths
+ stub.full_require_paths
+ end
+
+ def loaded_from
+ stub.loaded_from
+ end
+
+ def matches_for_glob
+ stub.matches_for_glob
+ end
+
+ def raw_require_paths
+ stub.raw_require_paths
+ end
+
+ # This is what we do in bundler/rubygems_ext
+ # full_require_paths is always implemented in versions that have stubs
+ def load_paths
+ full_require_paths
+ end
+
private
def _remote_specification
- stub.to_spec
+ @_remote_specification ||= begin
+ rs = stub.to_spec
+ if rs.equal?(self) # happens when to_spec gets the spec from Gem.loaded_specs
+ rs = Gem::Specification.load(loaded_from)
+ stub.instance_variable_set(:@spec, rs)
+ end
+
+ unless rs
+ raise GemspecError, "The gemspec for #{full_name} at #{loaded_from}" \
+ " was missing or broken. Try running `gem pristine #{name} -v #{version}`" \
+ " to fix the cached spec."
+ end
+
+ rs
+ end
end
end
end
diff --git a/spec/runtime/require_spec.rb b/spec/runtime/require_spec.rb
index 2d8935d2af..716080deb0 100644
--- a/spec/runtime/require_spec.rb
+++ b/spec/runtime/require_spec.rb
@@ -360,6 +360,29 @@ RSpec.describe "Bundler.require" do
end
end
end
+
+ it "does not load rubygems gemspecs that are used", :rubygems => ">= 2.3" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ run! <<-R
+ path = File.join(Gem.dir, "specifications", "rack-1.0.0.gemspec")
+ contents = File.read(path)
+ contents = contents.lines.insert(-2, "\n raise 'broken gemspec'\n").join
+ File.open(path, "w") do |f|
+ f.write contents
+ end
+ R
+
+ run! <<-R
+ Bundler.require
+ puts "WIN"
+ R
+
+ expect(out).to eq("WIN")
+ end
end
RSpec.describe "Bundler.require with platform specific dependencies" do