Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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

…f sources
  • Loading branch information...
commit 0785b65f3ebfb14498acd84f4d0b9e4ee7419006 1 parent d14390b
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.