Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Starting to refactor the Bundler to get it ready for multiple kinds o…

…f sources
  • Loading branch information...
commit 0d181160810522f7307fb9d947cf7deb56cb7dbd 1 parent a4fa2e1
Carl Lerche authored
View
1  lib/bundler.rb
@@ -7,6 +7,7 @@
require "rubygems/installer"
require "bundler/gem_bundle"
+require "bundler/source"
require "bundler/finder"
require "bundler/gem_ext"
require "bundler/resolver"
View
59 lib/bundler/finder.rb
@@ -13,10 +13,9 @@ class Finder
# ==== Parameters
# *sources<String>:: URI pointing to the gem repository
def initialize(*sources)
- @results = {}
- @index = Hash.new { |h,k| h[k] = {} }
-
- sources.each { |source| fetch(source) }
+ @cache = {}
+ @index = {}
+ @sources = sources
end
# Figures out the best possible configuration of gems that satisfies
@@ -36,39 +35,6 @@ def resolve(*dependencies)
resolved && GemBundle.new(resolved)
end
- # Fetches the index from the remote source
- #
- # ==== Parameters
- # source<String>:: URI pointing to the gem repository
- #
- # ==== Raises
- # ArgumentError:: If the source is not a valid gem repository
- def fetch(source)
- Bundler.logger.info "Updating source: #{source}"
-
- deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{source}/Marshal.4.8.Z")
- inflated = Gem.inflate deflated
-
- append(Marshal.load(inflated), source)
- rescue Gem::RemoteFetcher::FetchError => e
- raise ArgumentError, "#{source} is not a valid source: #{e.message}"
- end
-
- # Adds a new gem index linked to a gem source to the over all
- # gem index that gets searched.
- #
- # ==== Parameters
- # index<Gem::SourceIndex>:: The index to append to the list
- # source<String>:: The original source
- def append(index, source)
- index.gems.values.each do |spec|
- next unless Gem::Platform.match(spec.platform)
- spec.source = source
- @index[spec.name][spec.version] ||= spec
- end
- self
- end
-
# Searches for a gem that matches the dependency
#
# ==== Parameters
@@ -78,12 +44,25 @@ def append(index, source)
# [Gem::Specification]:: A collection of gem specifications
# matching the search
def search(dependency)
- @results[dependency.hash] ||= begin
- possibilities = @index[dependency.name].values
- possibilities.select do |spec|
+ @cache[dependency.hash] ||= begin
+ find_by_name(dependency.name).select do |spec|
dependency =~ spec
end.sort_by {|s| s.version }
end
end
+
+ private
+
+ def find_by_name(name)
+ matches = @index[name] ||= begin
+ versions = {}
+ @sources.reverse_each do |source|
+ versions.merge! source.specs[name] || {}
+ end
+ versions
+ end
+ matches.values
+ end
+
end
end
View
9 lib/bundler/gem_bundle.rb
@@ -1,13 +1,8 @@
module Bundler
class GemBundle < Array
- def download(directory)
- FileUtils.mkdir_p(directory)
-
+ def download(repository)
sort_by {|s| s.full_name.downcase }.each do |spec|
- unless directory.join("cache", "#{spec.full_name}.gem").file?
- Bundler.logger.info "Downloading #{spec.full_name}.gem"
- Gem::RemoteFetcher.fetcher.download(spec, spec.source, directory)
- end
+ repository.download(spec)
end
self
View
4 lib/bundler/gem_ext.rb
@@ -12,8 +12,8 @@ class Specification
attribute :source
def source=(source)
- @source = source.is_a?(URI) ? source : URI.parse(source)
- raise ArgumentError, "The source must be an absolute URI" unless @source.absolute?
+ source = Bundler::Source.new(source) unless source.is_a?(Bundler::Source)
+ @source = source
end
end
end
View
3  lib/bundler/manifest.rb
@@ -7,7 +7,6 @@ class Manifest
attr_reader :sources, :dependencies, :path
def initialize(sources, dependencies, bindir, repository_path, rubygems, system_gems)
- sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) }
@sources = sources
@dependencies = dependencies
@bindir = bindir
@@ -58,7 +57,7 @@ def fetch(update)
raise VersionConflict, "No compatible versions could be found for:\n#{gems}"
end
- bundle.download(@repository.path)
+ bundle.download(@repository)
end
def gem_dependencies
View
2  lib/bundler/manifest_file.rb
@@ -11,7 +11,7 @@ def self.load(filename = nil)
def initialize(filename)
@filename = filename
- @sources = %w(http://gems.rubyforge.org)
+ @sources = [Source.new("http://gems.rubyforge.org")]
@dependencies = []
@rubygems = true
@system_gems = true
View
8 lib/bundler/repository.rb
@@ -22,6 +22,14 @@ def valid?
(Dir[@path.join("*")] - Dir[@path.join("{cache,doc,gems,environments,specifications}")]).empty?
end
+ def download(spec)
+ FileUtils.mkdir_p(@path)
+
+ unless @path.join("cache", "#{spec.full_name}.gem").file?
+ spec.source.download(spec, @path)
+ end
+ end
+
# Checks whether a gem is installed
def install_cached_gems(options = {})
cached_gems.each do |name, version|
View
6 lib/bundler/runtime.rb
@@ -41,8 +41,10 @@ def disable_system_gems
end
def source(source)
- @manifest_file.sources << source
- @manifest_file.sources.uniq!
+ source = Source.new(source)
+ unless @manifest_file.sources.include?(source)
+ @manifest_file.sources << source
+ end
end
def sources
View
51 lib/bundler/source.rb
@@ -0,0 +1,51 @@
+module Bundler
+ # Represents a source of rubygems. Initially, this is only gem repositories, but
+ # eventually, this will be git, svn, HTTP
+ class Source
+ attr_reader :uri
+
+ def initialize(uri)
+ @uri = uri.is_a?(URI) ? uri : URI.parse(uri)
+ raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
+ end
+
+ def specs
+ @specs ||= fetch_specs
+ end
+
+ def ==(other)
+ uri == other.uri
+ end
+
+ def to_s
+ @uri.to_s
+ end
+
+ def download(spec, destination)
+ Bundler.logger.info "Downloading #{spec.full_name}.gem"
+ Gem::RemoteFetcher.fetcher.download(spec, uri, destination)
+ end
+
+ private
+
+ def fetch_specs
+ Bundler.logger.info "Updating source: #{to_s}"
+
+ deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/Marshal.4.8.Z")
+ inflated = Gem.inflate deflated
+
+ index = Marshal.load(inflated)
+ specs = Hash.new { |h,k| h[k] = {} }
+
+ index.gems.values.each do |spec|
+ next unless Gem::Platform.match(spec.platform)
+ spec.source = self
+ specs[spec.name][spec.version] = spec
+ end
+
+ specs
+ rescue Gem::RemoteFetcher::FetchError => e
+ raise ArgumentError, "#{to_s} is not a valid source: #{e.message}"
+ end
+ end
+end
View
125 spec/bundler/dsl_spec.rb
@@ -1,125 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-
-describe "Bundling DSL" do
-
- it "allows specifying the path to bundle gems to" do
- build_manifest.gem_path.should == tmp_file("vendor", "gems")
- end
-
- it "allows specifying sources" do
- manifest = build_manifest <<-DSL
- source "http://gems.github.com"
- DSL
-
- manifest.sources.first.should == URI.parse("http://gems.rubyforge.org")
- manifest.sources.last.should == URI.parse("http://gems.github.com")
- end
-
- it "allows specifying gems" do
- manifest = build_manifest <<-DSL
- gem "rails"
- DSL
-
- manifest.dependencies.first.name.should == "rails"
- end
-
- it "allows specifying gem versions" do
- manifest = build_manifest <<-DSL
- gem "rails", ">= 2.0.0"
- DSL
-
- manifest.dependencies.first.version.should == ">= 2.0.0"
- end
-
- it "allows specifying how to require the gem" do
- manifest = build_manifest <<-DSL
- gem "actionpack", :require_as => "action_controller"
- DSL
-
- manifest.dependencies.first.require_as.should == ["action_controller"]
- end
-
- it "allows specifying 'only' restrictions on the environment" do
- manifest = build_manifest <<-DSL
- gem "ruby-debug", :only => "development"
- DSL
-
- manifest.dependencies.first.only.should == ["development"]
- end
-
- it "allows specifying 'except' restrictions on the environment" do
- manifest = build_manifest <<-DSL
- gem "newrelic_rpm", :except => "staging"
- DSL
-
- manifest.dependencies.first.except.should == ["staging"]
- end
-
- it "loads the manifest from a file" do
- manifest = build_manifest(tmp_file("manifest.rb"), <<-DSL)
- gem "rails"
- DSL
-
- manifest.dependencies.first.name.should == "rails"
- end
-
- it "allows specifying an arbitrary number of sources and gems" do
- manifest = build_manifest <<-DSL
- gem "thor"
- source "http://gems.github.com"
- gem "wycats-merb-core"
- gem "mislav-will_paginate"
- source "http://gems.example.org"
- gem "uuidtools"
- DSL
-
- manifest.sources.should == [
- URI.parse("http://gems.rubyforge.org"),
- URI.parse("http://gems.github.com"),
- URI.parse("http://gems.example.org")
- ]
-
- manifest.dependencies.map { |d| d.name }.should == %w(
- thor
- wycats-merb-core
- mislav-will_paginate
- uuidtools
- )
- end
-
- it "can bundle gems in a manifest defined through the DSL" do
- manifest = build_manifest <<-DSL
- sources.clear
-
- source "file://#{gem_repo1}"
- source "file://#{gem_repo2}"
- gem "merb-core", "= 1.0.12"
- gem "activerecord", "> 2.2"
- DSL
-
- gems = %w(
- abstract-1.0.0 activerecord-2.3.2 activesupport-2.3.2 erubis-2.6.4
- extlib-0.9.12 json_pure-1.1.7 merb-core-1.0.12 mime-types-1.16
- rack-1.0.0 rake-0.8.7 rspec-1.2.8 thor-0.9.9)
-
- manifest.install
-
- tmp_gem_path.should have_cached_gems(*gems)
- tmp_gem_path.should have_installed_gems(*gems)
-
- load_paths = {}
- gems.each { |g| load_paths[g] = %w(bin lib) }
-
- tmp_gem_path('environments', 'default.rb').should have_load_paths(tmp_gem_path, load_paths)
- end
-
- it "outputs a pretty error when an environment is named rubygems" do
- lambda do
- build_manifest <<-DSL
- sources.clear
-
- gem "extlib", :only => "rubygems"
- DSL
- end.should raise_error(Bundler::InvalidEnvironmentName)
- end
-end
View
94 spec/bundler/fetcher_spec.rb
@@ -2,8 +2,8 @@
describe "Fetcher" do
before(:each) do
- @source = URI.parse("file://#{gem_repo1}")
- @other = URI.parse("file://#{gem_repo2}")
+ @source = Bundler::Source.new("file://#{gem_repo1}")
+ @other = Bundler::Source.new("file://#{gem_repo2}")
@finder = Bundler::Finder.new(@source, @other)
end
@@ -15,10 +15,31 @@
@finder.search(build_dep("activerecord", "= 2.3.2")).first.source.should == @source
end
- it "raises if the source is invalid" do
- lambda { Bundler::Finder.new.fetch("file://not/a/gem/source") }.should raise_error(ArgumentError)
- lambda { Bundler::Finder.new.fetch("http://localhost") }.should raise_error(ArgumentError)
- lambda { Bundler::Finder.new.fetch("http://google.com/not/a/gem/location") }.should raise_error(ArgumentError)
+ it "raises if the source does not exist" do
+ m = build_manifest <<-Gemfile
+ sources.clear
+ source "file://not/a/gem/source"
+ gem "foo"
+ Gemfile
+ lambda { m.install }.should raise_error(ArgumentError)
+ end
+
+ it "raises if the source is not available" do
+ m = build_manifest <<-Gemfile
+ sources.clear
+ source "http://localhost"
+ gem "foo"
+ Gemfile
+ lambda { m.install }.should raise_error(ArgumentError)
+ end
+
+ it "raises if the source is not a gem repository" do
+ m = build_manifest <<-Gemfile
+ sources.clear
+ source "http://google.com/not/a/gem/location"
+ gem "foo"
+ Gemfile
+ lambda { m.install }.should raise_error(ArgumentError)
end
it "accepts multiple source indexes" do
@@ -34,7 +55,7 @@
# Try out windows
Gem.platforms = [Gem::Platform.new("mswin32_60")]
- finder = Bundler::Finder.new(@source)
+ finder = Bundler::Finder.new(Bundler::Source.new("file://#{gem_repo1}"))
finder.search(build_dep("do_sqlite3", "> 0")).should only_have_specs("do_sqlite3-0.9.12-x86-mswin32-60")
ensure
Gem.platforms = nil
@@ -42,60 +63,15 @@
end
it "outputs a logger message when updating an index from source" do
+ m = build_manifest <<-Gemfile
+ sources.clear
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo2}"
+ gem "very-simple"
+ Gemfile
+ m.install
@log_output.should have_log_message("Updating source: file:#{gem_repo1}")
@log_output.should have_log_message("Updating source: file:#{gem_repo2}")
end
- describe "resolving rails" do
- before(:each) do
- @bundle = @finder.resolve(build_dep('rails', '>= 0'))
-
- FileUtils.mkdir_p(File.join(tmp_dir, 'cache'))
- Dir[File.join(tmp_dir, 'cache', '*')].each { |file| FileUtils.rm_f(file) }
- end
-
- it "resolves rails" do
- @bundle.should match_gems(
- "rails" => ["2.3.2"],
- "actionpack" => ["2.3.2"],
- "actionmailer" => ["2.3.2"],
- "activerecord" => ["2.3.2"],
- "activeresource" => ["2.3.2"],
- "activesupport" => ["2.3.2"],
- "rake" => ["0.8.7"]
- )
- end
-
- it "keeps track of the source that the gem spec was found in" do
- @bundle.select { |spec| spec.name == "activeresource" && spec.source == @other }.should have(1).item
- @bundle.select { |spec| spec.name == "activerecord" && spec.source == @source }.should have(1).item
- end
-
- it "can download the bundle" do
- @bundle.download(tmp_dir)
- @bundle.should be_cached_at(tmp_dir)
- end
-
- it "does not download the gem if the gem is the same as the cached version" do
- copy("actionmailer-2.3.2")
-
- lambda {
- @bundle.download(tmp_dir)
- @bundle.should be_cached_at(tmp_dir)
- }.should_not change { File.mtime(fixture("actionmailer-2.3.2")) }
- end
-
- it "outputs a logger message when resolving dependencies" do
- @log_output.should have_log_message("Calculating dependencies...")
- end
-
- it "outputs a logger message for each gem it downloads" do
- @bundle.download(tmp_dir)
-
- %w(rails-2.3.2 actionpack-2.3.2 actionmailer-2.3.2 activerecord-2.3.2
- activeresource-2.3.2 activesupport-2.3.2 rake-0.8.7).each do |name|
- @log_output.should have_log_message("Downloading #{name}.gem")
- end
- end
- end
end
View
11 spec/bundler/finder_spec.rb
@@ -1,6 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "Finder" do
+ # TODO: Refactor all of these specs
before(:each) do
index = build_index do
add_spec "activemerchant", "1.4.1" do
@@ -13,7 +14,15 @@
end
end
- @faster = Bundler::Finder.new.append(index, "http://foo")
+ def index.specs
+ specs = Hash.new{|h,k| h[k] = {}}
+ @gems.values.each do |spec|
+ specs[spec.name][spec.version] = spec
+ end
+ specs
+ end
+
+ @faster = Bundler::Finder.new(index)
end
it "find the gem given correct search" do
View
8 spec/bundler/gem_specification_spec.rb
@@ -9,7 +9,7 @@
s.source = 'http://gems.rubyforge.org'
end
- spec.source.should == URI.parse("http://gems.rubyforge.org")
+ spec.source.should == Bundler::Source.new("http://gems.rubyforge.org")
end
it "does not consider two gem specs with different sources to be the same" do
@@ -20,13 +20,13 @@
end
spec2 = spec1.dup
- spec2.source = "http://gems.github.com"
+ spec2.source = Bundler::Source.new("http://gems.github.com")
spec1.should_not == spec2
end
- it "can set a source that is already a URI" do
- source = URI.parse("http://foo")
+ it "can set a source that is already a Source" do
+ source = Bundler::Source.new("http://foo")
spec = Gem::Specification.new
spec.source = source
spec.source.should == source
View
5 spec/bundler/manifest_spec.rb
@@ -32,11 +32,6 @@ def dep(name, version, options = {})
$".replace @saved_loaded_features
end
- it "has a list of sources and dependencies" do
- @manifest.sources.should == @sources.map { |s| URI.parse(s) }
- @manifest.dependencies.should == @deps
- end
-
it "bundles itself (running all of the steps)" do
@manifest.install
Please sign in to comment.
Something went wrong with that request. Please try again.