summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Moore <tmoore@incrementalism.net>2013-08-28 21:23:28 +1000
committerTim Moore <tmoore@incrementalism.net>2014-07-30 14:16:35 +1000
commitedccf04e0442d2b63e8e1bac569eccec486ca3d9 (patch)
treeb1095e05c72031d488acbb4e63cdf81a969cfebe
parentca2a8fa969982322732a969fff18f1ba1f4eadcf (diff)
downloadbundler-edccf04e0442d2b63e8e1bac569eccec486ca3d9.tar.gz
Extract SourceList class from Definition.
-rw-r--r--lib/bundler.rb1
-rw-r--r--lib/bundler/definition.rb53
-rw-r--r--lib/bundler/dsl.rb47
-rw-r--r--lib/bundler/source_list.rb70
-rw-r--r--spec/bundler/definition_spec.rb3
-rw-r--r--spec/bundler/source_list_spec.rb277
6 files changed, 392 insertions, 59 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb
index 9bf16521dd..1eaf194b8f 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -41,6 +41,7 @@ module Bundler
autoload :SharedHelpers, 'bundler/shared_helpers'
autoload :SpecSet, 'bundler/spec_set'
autoload :Source, 'bundler/source'
+ autoload :SourceList, 'bundler/source_list'
autoload :Specification, 'bundler/shared_helpers'
autoload :SystemRubyVersion, 'bundler/ruby_version'
autoload :UI, 'bundler/ui'
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index dd0750ac15..b1ebba6e70 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -39,7 +39,7 @@ module Bundler
#
# @param lockfile [Pathname] Path to Gemfile.lock
# @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
- # @param sources [Array(Bundler::Source::Rubygems)]
+ # @param sources [Bundler::SourceList]
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
@@ -110,14 +110,14 @@ module Bundler
def resolve_with_cache!
raise "Specs already loaded" if @specs
- @sources.each { |s| s.cached! }
+ sources.cached!
specs
end
def resolve_remotely!
raise "Specs already loaded" if @specs
@remote = true
- @sources.each { |s| s.remote! }
+ sources.remote!
specs
end
@@ -209,7 +209,7 @@ module Bundler
dependency_names = @dependencies.dup || []
dependency_names.map! {|d| d.name }
- @sources.each do |s|
+ sources.all_sources.each do |s|
if s.is_a?(Bundler::Source::Rubygems)
s.dependency_names = dependency_names.uniq
idx.add_source s.specs
@@ -226,21 +226,20 @@ module Bundler
# spec, even if (say) a git gem is not checked out.
def rubygems_index
@rubygems_index ||= Index.build do |idx|
- rubygems = @sources.find{|s| s.is_a?(Source::Rubygems) }
- idx.add_source rubygems.specs
+ idx.add_source sources.rubygems_source.specs
end
end
def has_rubygems_remotes?
- @sources.any?{|s| s.is_a?(Source::Rubygems) && s.remotes.any? }
+ sources.rubygems_source.remotes.any?
end
def has_local_dependencies?
- @sources.any? { |s| !s.is_a?(Source::Rubygems) }
+ !sources.path_sources.empty? || !sources.git_sources.empty?
end
def spec_git_paths
- @sources.select {|s| s.is_a?(Bundler::Source::Git) }.map {|s| s.path.to_s }
+ sources.git_sources.map {|s| s.path.to_s }
end
def groups
@@ -272,7 +271,7 @@ module Bundler
def to_lock
out = ""
- sorted_sources.each do |source|
+ sources.all_sources.each do |source|
# Add the source header
out << source.to_lock
# Find all specs for this source
@@ -326,9 +325,10 @@ module Bundler
deleted = []
changed = []
- if @locked_sources != @sources
- new_sources = @sources - @locked_sources
- deleted_sources = @locked_sources - @sources
+ gemfile_sources = sources.all_sources
+ if @locked_sources != gemfile_sources
+ new_sources = gemfile_sources - @locked_sources
+ deleted_sources = @locked_sources - gemfile_sources
if new_sources.any?
added.concat new_sources.map { |source| "* source: #{source}" }
@@ -450,8 +450,7 @@ module Bundler
end
def converge_paths
- @sources.any? do |source|
- next unless source.instance_of?(Source::Path)
+ sources.path_sources.any? do |source|
specs_changed?(source) do |ls|
ls.class == source.class && ls.path == source.path
end
@@ -465,7 +464,7 @@ module Bundler
locked_gem = @locked_sources.find { |s| s.kind_of?(Source::Rubygems) }
# Get the Rubygems source from the Gemfile
- actual_gem = @sources.find { |s| s.kind_of?(Source::Rubygems) }
+ actual_gem = sources.rubygems_source
# If there is a Rubygems source in both
if locked_gem && actual_gem
@@ -476,12 +475,11 @@ module Bundler
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
# source in the Gemfile.lock, use the one from the Gemfile.
- @sources.map! do |source|
- @locked_sources.find { |s| s == source } || source
- end
- changes = changes | (Set.new(@sources) != Set.new(@locked_sources))
+ sources.replace_sources!(@locked_sources)
+ gemfile_sources = sources.all_sources
+ changes = changes | (Set.new(gemfile_sources) != Set.new(@locked_sources))
- @sources.each do |source|
+ gemfile_sources.each do |source|
# If the source is unlockable and the current command allows an unlock of
# the source (for example, you are doing a `bundle update <foo>` of a git-pinned
# gem), unlock it. For git sources, this means to unlock the revision, which
@@ -499,7 +497,7 @@ module Bundler
def converge_dependencies
(@dependencies + @locked_deps).each do |dep|
if dep.source
- dep.source = @sources.find { |s| dep.source == s }
+ dep.source = sources.get(dep.source)
end
end
Set.new(@dependencies) != Set.new(@locked_deps)
@@ -533,7 +531,7 @@ module Bundler
converged = []
@locked_specs.each do |s|
- s.source = @sources.find { |src| s.source == src }
+ s.source = sources.get(s.source)
# Don't add a spec to the list if its source is expired. For example,
# if you change a Git gem to Rubygems.
@@ -562,7 +560,7 @@ module Bundler
diff = @locked_specs.to_a - resolve.to_a
# Now, we unlock any sources that do not have anymore gems pinned to it
- @sources.each do |source|
+ sources.all_sources.each do |source|
next unless source.respond_to?(:unlock!)
unless resolve.any? { |s| s.source == source }
@@ -597,13 +595,6 @@ module Bundler
deps
end
- def sorted_sources
- @sources.sort_by do |s|
- # Place GEM at the top
- [ s.is_a?(Source::Rubygems) ? 1 : 0, s.to_s ]
- end
- end
-
def requested_dependencies
groups = self.groups - Bundler.settings.without
groups.map! { |g| g.to_sym }
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index 5a985a6791..2953ef342c 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -17,7 +17,7 @@ module Bundler
def initialize
@source = nil
- @sources = []
+ @sources = SourceList.new
@git_sources = {}
@dependencies = []
@groups = []
@@ -27,10 +27,6 @@ module Bundler
add_github_sources
end
- def rubygems_source
- @rubygems_source ||= Source::Rubygems.new
- end
-
def eval_gemfile(gemfile, contents = nil)
contents ||= Bundler.read_file(gemfile.to_s)
instance_eval(contents, gemfile.to_s, 1)
@@ -115,30 +111,18 @@ module Bundler
@dependencies << dep
end
- def source(source, options = {})
+ def source(source)
case source
when :gemcutter, :rubygems, :rubyforge then
Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
"requests are insecure.\nPlease change your source to 'https://" \
"rubygems.org' if possible, or 'http://rubygems.org' if not."
- rubygems_source.add_remote "http://rubygems.org"
- return
+ @sources.add_rubygems_remote("http://rubygems.org")
when String
- rubygems_source.add_remote source
- return
+ @sources.add_rubygems_remote(source)
else
- @source = source
- if options[:prepend]
- @sources = [@source] | @sources
- else
- @sources = @sources | [@source]
- end
-
- yield if block_given?
- return @source
+ raise GemfileError, "Unknown source '#{source}'"
end
- ensure
- @source = nil
end
def git_source(name, &block)
@@ -154,11 +138,11 @@ module Bundler
@git_sources[name.to_s] = block
end
- def path(path, options = {}, source_options = {}, &blk)
- source Source::Path.new(normalize_hash(options).merge("path" => Pathname.new(path))), source_options, &blk
+ def path(path, options = {}, &blk)
+ with_source(@sources.add_path_source(normalize_hash(options).merge("path" => Pathname.new(path))), &blk)
end
- def git(uri, options = {}, source_options = {}, &blk)
+ def git(uri, options = {}, &blk)
unless block_given?
msg = "You can no longer specify a git source by itself. Instead, \n" \
"either use the :git option on a gem, or specify the gems that \n" \
@@ -170,11 +154,10 @@ module Bundler
raise DeprecatedError, msg
end
- source Source::Git.new(normalize_hash(options).merge("uri" => uri)), source_options, &blk
+ with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
end
def to_definition(lockfile, unlock)
- @sources << rubygems_source unless @sources.include?(rubygems_source)
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
end
@@ -217,6 +200,16 @@ module Bundler
git_source(:gist){ |repo_name| "https://gist.github.com/#{repo_name}.git" }
end
+ def with_source(source)
+ if block_given?
+ @source = source
+ yield
+ end
+ source
+ ensure
+ @source = nil
+ end
+
def normalize_hash(opts)
opts.keys.each do |k|
opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
@@ -272,7 +265,7 @@ module Bundler
else
options = opts.dup
end
- source = send(type, param, options, :prepend => true) {}
+ source = send(type, param, options) {}
opts["source"] = source
end
end
diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb
new file mode 100644
index 0000000000..6b794359d0
--- /dev/null
+++ b/lib/bundler/source_list.rb
@@ -0,0 +1,70 @@
+module Bundler
+ class SourceList
+ attr_reader :path_sources,
+ :git_sources,
+ :rubygems_source
+
+ def initialize
+ @path_sources = []
+ @git_sources = []
+ @rubygems_source = Source::Rubygems.new
+ end
+
+ def add_path_source(options = {})
+ add_source_to_list Source::Path.new(options), path_sources
+ end
+
+ def add_git_source(options = {})
+ add_source_to_list Source::Git.new(options), git_sources
+ end
+
+ def add_rubygems_remote(uri)
+ @rubygems_source.add_remote(uri)
+ @rubygems_source
+ end
+
+ def all_sources
+ path_sources + git_sources << rubygems_source
+ end
+
+ def get(source)
+ if source.is_a?(Source::Rubygems)
+ rubygems_source
+ else
+ source_list_for(source).find { |s| source == s }
+ end
+ end
+
+ def replace_sources!(replacement_sources)
+ [path_sources, git_sources].each do |source_list|
+ source_list.map! do |source|
+ replacement_sources.find { |s| s == source } || source
+ end
+ end
+ @rubygems_source = replacement_sources.find { |s| s == rubygems_source } || rubygems_source
+ end
+
+ def cached!
+ all_sources.each(&:cached!)
+ end
+
+ def remote!
+ all_sources.each(&:remote!)
+ end
+
+ private
+
+ def add_source_to_list(source, list)
+ list.unshift(source).uniq!
+ source
+ end
+
+ def source_list_for(source)
+ case source
+ when Source::Git then git_sources
+ when Source::Path then path_sources
+ else raise ArgumentError, "Invalid source: #{source.inspect}"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/definition_spec.rb b/spec/bundler/definition_spec.rb
index ad02861405..0f184169c9 100644
--- a/spec/bundler/definition_spec.rb
+++ b/spec/bundler/definition_spec.rb
@@ -4,11 +4,12 @@ require 'bundler/definition'
describe Bundler::Definition do
before do
allow(Bundler).to receive(:settings){ Bundler::Settings.new(".") }
+ allow(Bundler).to receive(:default_gemfile){ Pathname.new("Gemfile") }
end
describe "#lock" do
context "when it's not possible to write to the file" do
- subject{ Bundler::Definition.new(nil, [], [], []) }
+ subject{ Bundler::Definition.new(nil, [], Bundler::SourceList.new, []) }
it "raises an InstallError with explanation" do
expect(File).to receive(:open).with("Gemfile.lock", "wb").
diff --git a/spec/bundler/source_list_spec.rb b/spec/bundler/source_list_spec.rb
new file mode 100644
index 0000000000..583f0ed524
--- /dev/null
+++ b/spec/bundler/source_list_spec.rb
@@ -0,0 +1,277 @@
+require 'spec_helper'
+
+describe Bundler::SourceList do
+ before do
+ Bundler.stub(:root) { Pathname.new '/' }
+ end
+
+ subject(:source_list) { Bundler::SourceList.new }
+
+ describe "adding sources" do
+ before do
+ source_list.add_path_source('path' => '/existing/path/to/gem')
+ source_list.add_git_source('uri' => 'git://existing-git.org/path.git')
+ end
+
+ describe "#add_path_source" do
+ before do
+ @duplicate = source_list.add_path_source('path' => '/path/to/gem')
+ @new_source = source_list.add_path_source('path' => '/path/to/gem')
+ end
+
+ it "returns the new path source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Path)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq('path' => '/path/to/gem')
+ end
+
+ it "adds the source to the beginning of path_sources" do
+ expect(source_list.path_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.path_sources).not_to include equal(@duplicate)
+ end
+ end
+
+ describe "#add_git_source" do
+ before do
+ @duplicate = source_list.add_git_source('uri' => 'git://host/path.git')
+ @new_source = source_list.add_git_source('uri' => 'git://host/path.git')
+ end
+
+ it "returns the new git source" do
+ expect(@new_source).to be_instance_of(Bundler::Source::Git)
+ end
+
+ it "passes the provided options to the new source" do
+ expect(@new_source.options).to eq('uri' => 'git://host/path.git')
+ end
+
+ it "adds the source to the beginning of git_sources" do
+ expect(source_list.git_sources.first).to equal(@new_source)
+ end
+
+ it "removes existing duplicates" do
+ expect(source_list.git_sources).not_to include equal(@duplicate)
+ end
+ end
+
+ describe "#add_rubygems_remote" do
+ before do
+ @returned_source = source_list.add_rubygems_remote('https://rubygems.org/')
+ end
+
+ it "returns the aggregate rubygems source" do
+ expect(@returned_source).to be_instance_of(Bundler::Source::Rubygems)
+ end
+
+ it "adds the provided remote to the beginning of the aggregate source" do
+ source_list.add_rubygems_remote('https://othersource.org')
+ expect(@returned_source.remotes.first).to eq(URI('https://othersource.org/'))
+ end
+ end
+ end
+
+ describe "#all_sources" do
+ it "returns an empty rubygems source when no rubygems remotes were supplied" do
+ source_list.add_git_source('uri' => 'git://host/path.git')
+ source_list.add_path_source('path' => '/path/to/gem')
+
+ expect(source_list.all_sources).to include Bundler::Source::Rubygems.new
+ end
+
+ it "returns path sources before git sources before rubygems sources" do
+ source_list.add_git_source('uri' => 'git://host/path.git')
+ source_list.add_rubygems_remote('https://rubygems.org')
+ source_list.add_path_source('path' => '/path/to/gem')
+
+ expect(source_list.all_sources).to eq [
+ Bundler::Source::Path.new('path' => '/path/to/gem'),
+ Bundler::Source::Git.new('uri' => 'git://host/path.git'),
+ Bundler::Source::Rubygems.new('remotes' => ['https://rubygems.org'])
+ ]
+ end
+
+ it "returns sources of the same type in the reverse order that they were added" do
+ source_list.add_git_source('uri' => 'git://third-git.org/path.git')
+ source_list.add_rubygems_remote('https://fifth-rubygems.org')
+ source_list.add_path_source('path' => '/third/path/to/gem')
+ source_list.add_rubygems_remote('https://fourth-rubygems.org')
+ source_list.add_path_source('path' => '/second/path/to/gem')
+ source_list.add_rubygems_remote('https://third-rubygems.org')
+ source_list.add_git_source('uri' => 'git://second-git.org/path.git')
+ source_list.add_rubygems_remote('https://second-rubygems.org')
+ source_list.add_path_source('path' => '/first/path/to/gem')
+ source_list.add_rubygems_remote('https://first-rubygems.org')
+ source_list.add_git_source('uri' => 'git://first-git.org/path.git')
+
+ expect(source_list.all_sources).to eq [
+ Bundler::Source::Path.new('path' => '/first/path/to/gem'),
+ Bundler::Source::Path.new('path' => '/second/path/to/gem'),
+ Bundler::Source::Path.new('path' => '/third/path/to/gem'),
+ Bundler::Source::Git.new('uri' => 'git://first-git.org/path.git'),
+ Bundler::Source::Git.new('uri' => 'git://second-git.org/path.git'),
+ Bundler::Source::Git.new('uri' => 'git://third-git.org/path.git'),
+ Bundler::Source::Rubygems.new('remotes' => [
+ 'https://first-rubygems.org',
+ 'https://second-rubygems.org',
+ 'https://third-rubygems.org',
+ 'https://fourth-rubygems.org',
+ 'https://fifth-rubygems.org'
+ ]),
+ ]
+ end
+ end
+
+ describe "#path_sources" do
+ it "returns an empty array when no path sources have been added" do
+ source_list.add_rubygems_remote('https://rubygems.org')
+ source_list.add_git_source('uri' => 'git://host/path.git')
+ expect(source_list.path_sources).to be_empty
+ end
+
+ it "returns path sources in the reverse order that they were added" do
+ source_list.add_git_source('uri' => 'git://third-git.org/path.git')
+ source_list.add_rubygems_remote('https://fifth-rubygems.org')
+ source_list.add_path_source('path' => '/third/path/to/gem')
+ source_list.add_rubygems_remote('https://fourth-rubygems.org')
+ source_list.add_path_source('path' => '/second/path/to/gem')
+ source_list.add_rubygems_remote('https://third-rubygems.org')
+ source_list.add_git_source('uri' => 'git://second-git.org/path.git')
+ source_list.add_rubygems_remote('https://second-rubygems.org')
+ source_list.add_path_source('path' => '/first/path/to/gem')
+ source_list.add_rubygems_remote('https://first-rubygems.org')
+ source_list.add_git_source('uri' => 'git://first-git.org/path.git')
+
+ expect(source_list.path_sources).to eq [
+ Bundler::Source::Path.new('path' => '/first/path/to/gem'),
+ Bundler::Source::Path.new('path' => '/second/path/to/gem'),
+ Bundler::Source::Path.new('path' => '/third/path/to/gem'),
+ ]
+ end
+ end
+
+ describe "#git_sources" do
+ it "returns an empty array when no git sources have been added" do
+ source_list.add_rubygems_remote('https://rubygems.org')
+ source_list.add_path_source('path' => '/path/to/gem')
+
+ expect(source_list.git_sources).to be_empty
+ end
+
+ it "returns git sources in the reverse order that they were added" do
+ source_list.add_git_source('uri' => 'git://third-git.org/path.git')
+ source_list.add_rubygems_remote('https://fifth-rubygems.org')
+ source_list.add_path_source('path' => '/third/path/to/gem')
+ source_list.add_rubygems_remote('https://fourth-rubygems.org')
+ source_list.add_path_source('path' => '/second/path/to/gem')
+ source_list.add_rubygems_remote('https://third-rubygems.org')
+ source_list.add_git_source('uri' => 'git://second-git.org/path.git')
+ source_list.add_rubygems_remote('https://second-rubygems.org')
+ source_list.add_path_source('path' => '/first/path/to/gem')
+ source_list.add_rubygems_remote('https://first-rubygems.org')
+ source_list.add_git_source('uri' => 'git://first-git.org/path.git')
+
+ expect(source_list.git_sources).to eq [
+ Bundler::Source::Git.new('uri' => 'git://first-git.org/path.git'),
+ Bundler::Source::Git.new('uri' => 'git://second-git.org/path.git'),
+ Bundler::Source::Git.new('uri' => 'git://third-git.org/path.git'),
+ ]
+ end
+ end
+
+ describe "#get" do
+ context "when it includes an equal source" do
+ let(:rubygems_source) { Bundler::Source::Rubygems.new('remotes' => ['https://rubygems.org']) }
+ before { @equal_source = source_list.add_rubygems_remote('https://rubygems.org') }
+
+ it "returns the equal source" do
+ expect(source_list.get(rubygems_source)).to be @equal_source
+ end
+ end
+
+ context "when it does not include an equal source" do
+ let(:path_source) { Bundler::Source::Path.new('path' => '/path/to/gem') }
+
+ it "returns nil" do
+ expect(source_list.get(path_source)).to be_nil
+ end
+ end
+ end
+
+ describe "replace_sources!" do
+ let(:existing_locked_source) { Bundler::Source::Path.new('path' => '/existing/path') }
+ let(:removed_locked_source) { Bundler::Source::Path.new('path' => '/removed/path') }
+
+ let(:locked_sources) { [existing_locked_source, removed_locked_source] }
+
+ before do
+ @existing_source = source_list.add_path_source('path' => '/existing/path')
+ @new_source = source_list.add_path_source('path' => '/new/path')
+ source_list.replace_sources!(locked_sources)
+ end
+
+ it "maintains the order and number of sources" do
+ expect(source_list.path_sources).to eq [@new_source, @existing_source]
+ end
+
+ it "retains the same instance of the new source" do
+ expect(source_list.path_sources[0]).to be @new_source
+ end
+
+ it "replaces the instance of the existing source" do
+ expect(source_list.path_sources[1]).to be existing_locked_source
+ end
+ end
+
+ describe "#cached!" do
+ let(:rubygems_source) { source_list.add_rubygems_remote('https://rubygems.org') }
+ let(:git_source) { source_list.add_git_source('uri' => 'git://host/path.git') }
+ let(:path_source) { source_list.add_path_source('path' => '/path/to/gem') }
+
+ before do
+ rubygems_source.stub(:cached!)
+ git_source.stub(:cached!)
+ path_source.stub(:cached!)
+ source_list.cached!
+ end
+
+ it "calls #cached! on the included rubygems source" do
+ expect(rubygems_source).to have_received(:cached!)
+ end
+
+ it "calls #cached! on the included git source" do
+ expect(git_source).to have_received(:cached!)
+ end
+ it "calls #cached! on the included path source" do
+ expect(path_source).to have_received(:cached!)
+ end
+ end
+
+ describe "#remote!" do
+ let(:rubygems_source) { source_list.add_rubygems_remote('https://rubygems.org') }
+ let(:git_source) { source_list.add_git_source('uri' => 'git://host/path.git') }
+ let(:path_source) { source_list.add_path_source('path' => '/path/to/gem') }
+
+ before do
+ rubygems_source.stub(:remote!)
+ git_source.stub(:remote!)
+ path_source.stub(:remote!)
+ source_list.remote!
+ end
+
+ it "calls #remote! on the included rubygems source" do
+ expect(rubygems_source).to have_received(:remote!)
+ end
+
+ it "calls #remote! on the included git source" do
+ expect(git_source).to have_received(:remote!)
+ end
+ it "calls #remote! on the included path source" do
+ expect(path_source).to have_received(:remote!)
+ end
+ end
+end