diff options
author | Samuel Giddins <segiddins@segiddins.me> | 2017-04-06 18:56:26 -0500 |
---|---|---|
committer | Samuel Giddins <segiddins@segiddins.me> | 2017-04-07 12:02:55 -0500 |
commit | 8b95a4858e15cf667c70db8c4d9c0333a28b55e8 (patch) | |
tree | f5a5186c0e6838415e8a35e96194107f936e3c32 | |
parent | 32fb8327328789bdc911dccda4a59e99956c558c (diff) | |
download | bundler-8b95a4858e15cf667c70db8c4d9c0333a28b55e8.tar.gz |
[StubSpecification] Avoid loading the full spec when possible
-rw-r--r-- | lib/bundler/lazy_specification.rb | 1 | ||||
-rw-r--r-- | lib/bundler/remote_specification.rb | 10 | ||||
-rw-r--r-- | lib/bundler/rubygems_integration.rb | 3 | ||||
-rw-r--r-- | lib/bundler/stub_specification.rb | 58 | ||||
-rw-r--r-- | spec/runtime/require_spec.rb | 23 |
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 |