diff options
author | Tim Moore <tmoore@incrementalism.net> | 2013-08-28 21:23:28 +1000 |
---|---|---|
committer | Tim Moore <tmoore@incrementalism.net> | 2014-07-30 14:16:35 +1000 |
commit | edccf04e0442d2b63e8e1bac569eccec486ca3d9 (patch) | |
tree | b1095e05c72031d488acbb4e63cdf81a969cfebe | |
parent | ca2a8fa969982322732a969fff18f1ba1f4eadcf (diff) | |
download | bundler-edccf04e0442d2b63e8e1bac569eccec486ca3d9.tar.gz |
Extract SourceList class from Definition.
-rw-r--r-- | lib/bundler.rb | 1 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 53 | ||||
-rw-r--r-- | lib/bundler/dsl.rb | 47 | ||||
-rw-r--r-- | lib/bundler/source_list.rb | 70 | ||||
-rw-r--r-- | spec/bundler/definition_spec.rb | 3 | ||||
-rw-r--r-- | spec/bundler/source_list_spec.rb | 277 |
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 |