lib/bundler/source.rb in bundler-0.6.0 vs lib/bundler/source.rb in bundler-0.7.0

- old
+ new

@@ -1,10 +1,25 @@ module Bundler + class DirectorySourceError < StandardError; end + class GitSourceError < StandardError ; end # Represents a source of rubygems. Initially, this is only gem repositories, but # eventually, this will be git, svn, HTTP class Source attr_accessor :repository, :local + + def initialize(options) ; end + + private + + def process_source_gems(gems) + new_gems = Hash.new { |h,k| h[k] = [] } + gems.values.each do |spec| + spec.source = self + new_gems[spec.name] << spec + end + new_gems + end end class GemSource < Source attr_reader :uri @@ -36,11 +51,11 @@ Bundler.logger.info "Downloading #{spec.full_name}.gem" destination = repository.path unless destination.writable? - raise RubygemsRetardation + raise RubygemsRetardation, "destination: #{destination} is not writable" end # Download the gem Gem::RemoteFetcher.fetcher.download(spec, uri, destination) @@ -63,32 +78,38 @@ rescue Gem::RemoteFetcher::FetchError Bundler.logger.warn "Source '#{uri}' does not support prerelease gems" index = Marshal.load(main_index) end - gems = {} + gems = Hash.new { |h,k| h[k] = [] } index.each do |name, version, platform| spec = RemoteSpecification.new(name, version, platform, @uri) - gems[spec.full_name] = spec + spec.source = self + gems[spec.name] << spec if Gem::Platform.match(spec.platform) end gems rescue Gem::RemoteFetcher::FetchError => e raise ArgumentError, "#{to_s} is not a valid source: #{e.message}" end end class SystemGemSource < Source + + def self.instance + @instance ||= new({}) + end + def initialize(options) - # Nothing to do + @source = Gem::SourceIndex.from_installed_gems end def can_be_local? false end def gems - @specs ||= Gem::SourceIndex.from_installed_gems.gems + @gems ||= process_source_gems(@source.gems) end def ==(other) other.is_a?(SystemGemSource) end @@ -96,24 +117,17 @@ def to_s "system" end def download(spec) - # gemfile = Pathname.new(local.loaded_from) - # gemfile = gemfile.dirname.join('..', 'cache', "#{local.full_name}.gem").expand_path - # repository.cache(File.join(Gem.dir, "cache", "#{local.full_name}.gem")) gemfile = Pathname.new(spec.loaded_from) gemfile = gemfile.dirname.join('..', 'cache', "#{spec.full_name}.gem") repository.cache(gemfile) end private - def fetch_specs - - end - end class GemDirectorySource < Source attr_reader :location @@ -142,98 +156,112 @@ end private def fetch_specs - specs = {} + specs = Hash.new { |h,k| h[k] = [] } Dir["#{@location}/*.gem"].each do |gemfile| spec = Gem::Format.from_file_by_path(gemfile).spec - specs[spec.full_name] = spec + spec.source = self + specs[spec.name] << spec end specs end end class DirectorySource < Source - attr_reader :location + attr_reader :location, :specs, :required_specs def initialize(options) - @name = options[:name] - @version = options[:version] - @location = options[:location] - @require_paths = options[:require_paths] || %w(lib) + if options[:location] + @location = Pathname.new(options[:location]).expand_path + end + @glob = options[:glob] || "**/*.gemspec" + @specs = {} + @required_specs = [] end + def add_spec(path, name, version, require_paths = %w(lib)) + raise DirectorySourceError, "already have a gem defined for '#{path}'" if @specs[path.to_s] + @specs[path.to_s] = Gem::Specification.new do |s| + s.name = name + s.version = Gem::Version.new(version) + end + end + def can_be_local? true end def gems @gems ||= begin - specs = {} + # Locate all gemspecs from the directory + specs = locate_gemspecs + specs = merge_defined_specs(specs) - # Find any gemspec files in the directory and load those specs - Dir["#{location}/**/*.gemspec"].each do |file| - file = Pathname.new(file) - if spec = eval(File.read(file)) and validate_gemspec(file, spec) - spec.location = file.dirname.expand_path - specs[spec.full_name] = spec + required_specs.each do |required| + unless specs.any? {|k,v| v.name == required } + raise DirectorySourceError, "No gemspec for '#{required}' was found in" \ + " '#{location}'. Please explicitly specify a version." end end - # If a gemspec for the dependency was not found, add it to the list - if specs.keys.grep(/^#{Regexp.escape(@name)}/).empty? - case - when @version.nil? - raise ArgumentError, "If you use :at, you must specify the gem " \ - "and version you wish to stand in for" - when !Gem::Version.correct?(@version) - raise ArgumentError, "If you use :at, you must specify a gem and " \ - "version. You specified #{@version} for the version" - end + process_source_gems(specs) + end + end - default = Gem::Specification.new do |s| - s.name = @name - s.version = Gem::Version.new(@version) if @version - s.location = location - end - specs[default.full_name] = default + def locate_gemspecs + Dir["#{location}/#{@glob}"].inject({}) do |specs, file| + file = Pathname.new(file) + if spec = eval(File.read(file)) and validate_gemspec(file.dirname, spec) + spec.location = file.dirname.expand_path + specs[spec.full_name] = spec end - specs end end - # Too aggressive apparently. - # === - # def validate_gemspec(file, spec) - # file = Pathname.new(file) - # Dir.chdir(file.dirname) do - # spec.validate - # end - # rescue Gem::InvalidSpecificationException => e - # file = file.relative_path_from(repository.path) - # Bundler.logger.warn e.message - # Bundler.logger.warn "Gemspec #{spec.name} (#{spec.version}) found at '#{file}' is not valid" - # false - # end - def validate_gemspec(file, spec) - base = file.dirname + def merge_defined_specs(specs) + @specs.each do |path, spec| + # Set the spec location + spec.location = "#{location}/#{path}" + + if existing = specs.values.find { |s| s.name == spec.name } + if existing.version != spec.version + raise DirectorySourceError, "The version you specified for #{spec.name}" \ + " is #{spec.version}. The gemspec is #{existing.version}." + # Not sure if this is needed + # ==== + # elsif File.expand_path(existing.location) != File.expand_path(spec.location) + # raise DirectorySourceError, "The location you specified for #{spec.name}" \ + # " is '#{spec.location}'. The gemspec was found at '#{existing.location}'." + end + elsif !validate_gemspec(spec.location, spec) + raise "Your gem definition is not valid: #{spec}" + else + specs[spec.full_name] = spec + end + end + specs + end + + def validate_gemspec(path, spec) + path = Pathname.new(path) msg = "Gemspec for #{spec.name} (#{spec.version}) is invalid:" # Check the require_paths - (spec.require_paths || []).each do |path| - unless base.join(path).directory? - Bundler.logger.warn "#{msg} Missing require path: '#{path}'" + (spec.require_paths || []).each do |require_path| + unless path.join(require_path).directory? + Bundler.logger.warn "#{msg} Missing require path: '#{require_path}'" return false end end # Check the executables (spec.executables || []).each do |exec| - unless base.join(spec.bindir, exec).file? + unless path.join(spec.bindir, exec).file? Bundler.logger.warn "#{msg} Missing executable: '#{File.join(spec.bindir, exec)}'" return false end end @@ -253,10 +281,12 @@ # Nothing needed here end end class GitSource < DirectorySource + attr_reader :ref, :uri, :branch + def initialize(options) super @uri = options[:uri] @ref = options[:ref] @branch = options[:branch] @@ -279,12 +309,12 @@ Bundler.logger.info "Cloning git repository at: #{@uri}" `git clone #{@uri} #{location} --no-hardlinks` if @ref - Dir.chdir(location) { `git checkout #{@ref}` } + Dir.chdir(location) { `git checkout --quiet #{@ref}` } elsif @branch && @branch != "master" - Dir.chdir(location) { `git checkout origin/#{@branch}` } + Dir.chdir(location) { `git checkout --quiet origin/#{@branch}` } end end super end