class Bundler::Source::Rubygems
TODO: Refactor this class
def self.from_lock(options)
def self.from_lock(options) s = new(options) Array(options["remote"]).each { |r| s.add_remote(r) } s end
def add_remote(source)
def add_remote(source) @remotes << normalize_uri(source) end
def cache(spec)
def cache(spec) cached_path = cached_gem(spec) raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path return if File.dirname(cached_path) == Bundler.app_cache.to_s Bundler.ui.info " * #{File.basename(cached_path)}" FileUtils.cp(cached_path, Bundler.app_cache) end
def cached!
def cached! @allow_cached = true end
def cached_gem(spec)
def cached_gem(spec) possibilities = @caches.map { |p| "#{p}/#{spec.full_name}.gem" } possibilities.find { |p| File.exist?(p) } end
def cached_specs
def cached_specs @cached_specs ||= begin idx = installed_specs.dup path = Bundler.app_cache Dir["#{path}/*.gem"].each do |gemfile| next if gemfile =~ /bundler\-[\d\.]+?\.gem/ begin s ||= Gem::Format.from_file_by_path(gemfile).spec rescue Gem::Package::FormatError raise GemspecError, "Could not read gem at #{gemfile}. It may be corrupted." end s.source = self idx << s end end idx end
def download_gem_from_uri(spec, uri)
def download_gem_from_uri(spec, uri) spec.fetch_platform download_path = Bundler.requires_sudo? ? Bundler.tmp : Gem.dir gem_path = "#{Gem.dir}/cache/#{spec.full_name}.gem" FileUtils.mkdir_p("#{download_path}/cache") Gem::RemoteFetcher.fetcher.download(spec, uri, download_path) if Bundler.requires_sudo? sudo "mkdir -p #{Gem.dir}/cache" sudo "mv #{Bundler.tmp}/cache/#{spec.full_name}.gem #{gem_path}" end gem_path end
def eql?(o)
def eql?(o) Rubygems === o end
def fetch(spec)
def fetch(spec) spec, uri = @spec_fetch_map[spec.full_name] if spec path = download_gem_from_uri(spec, uri) s = Gem::Format.from_file_by_path(path).spec spec.__swap__(s) end end
def fetch_all_remote_specs(&blk)
def fetch_all_remote_specs(&blk) begin # Fetch all specs, minus prerelease specs Gem::SpecFetcher.new.list(true, false).each(&blk) # Then fetch the prerelease specs begin Gem::SpecFetcher.new.list(false, true).each(&blk) rescue Gem::RemoteFetcher::FetchError Bundler.ui.warn "Could not fetch prerelease specs from #{self}" end rescue Gem::RemoteFetcher::FetchError Bundler.ui.warn "Could not reach #{self}" end end
def fetch_specs
def fetch_specs Index.build do |idx| idx.use installed_specs idx.use cached_specs if @allow_cached || @allow_remote idx.use remote_specs if @allow_remote end end
def hash
def hash Rubygems.hash end
def initialize(options = {})
def initialize(options = {}) @options = options @remotes = (options["remotes"] || []).map { |r| normalize_uri(r) } @allow_remote = false @allow_cached = false # Hardcode the paths for now @caches = [ Bundler.app_cache ] + Gem.path.map { |p| File.expand_path("#{p}/cache") } @spec_fetch_map = {} end
def install(spec)
def install(spec) path = cached_gem(spec) if installed_specs[spec].any? Bundler.ui.info "Using #{spec.name} (#{spec.version}) " return end Bundler.ui.info "Installing #{spec.name} (#{spec.version}) " install_path = Bundler.requires_sudo? ? Bundler.tmp : Gem.dir options = { :install_dir => install_path, :ignore_dependencies => true, :wrappers => true, :env_shebang => true } options.merge!(:bin_dir => "#{install_path}/bin") unless spec.executables.nil? || spec.executables.empty? installer = Gem::Installer.new path, options installer.install # SUDO HAX if Bundler.requires_sudo? sudo "mkdir -p #{Gem.dir}/gems #{Gem.dir}/specifications" sudo "cp -R #{Bundler.tmp}/gems/#{spec.full_name} #{Gem.dir}/gems/" sudo "cp -R #{Bundler.tmp}/specifications/#{spec.full_name}.gemspec #{Gem.dir}/specifications/" spec.executables.each do |exe| sudo "mkdir -p #{Gem.bindir}" sudo "cp -R #{Bundler.tmp}/bin/#{exe} #{Gem.bindir}" end end spec.loaded_from = "#{Gem.dir}/specifications/#{spec.full_name}.gemspec" end
def installed_specs
def installed_specs @installed_specs ||= begin idx = Index.new have_bundler = false Gem.source_index.to_a.reverse.each do |dont_use_this_var, spec| next if spec.name == 'bundler' && spec.version.to_s != VERSION have_bundler = true if spec.name == 'bundler' spec.source = self idx << spec end # Always have bundler locally unless have_bundler # We're running bundler directly from the source # so, let's create a fake gemspec for it (it's a path) # gemspec bundler = Gem::Specification.new do |s| s.name = 'bundler' s.version = VERSION s.platform = Gem::Platform::RUBY s.source = self s.loaded_from = File.expand_path("..", __FILE__) end idx << bundler end idx end end
def merge_remotes(source)
def merge_remotes(source) @remotes = [] source.remotes.each do |r| add_remote r.to_s end end
def normalize_uri(uri)
def normalize_uri(uri) uri = uri.to_s uri = "#{uri}/" unless uri =~ %r'/$' uri = URI(uri) raise ArgumentError, "The source must be an absolute URI" unless uri.absolute? uri end
def options
def options { "remotes" => @remotes.map { |r| r.to_s } } end
def remote!
def remote! @allow_remote = true end
def remote_specs
def remote_specs @remote_specs ||= begin idx = Index.new old = Gem.sources remotes.each do |uri| Bundler.ui.info "Fetching source index for #{uri}" Gem.sources = ["#{uri}"] fetch_all_remote_specs do |n,v| v.each do |name, version, platform| next if name == 'bundler' spec = RemoteSpecification.new(name, version, platform, uri) spec.source = self @spec_fetch_map[spec.full_name] = [spec, uri] idx << spec end end end idx ensure Gem.sources = old end end
def specs
def specs @specs ||= fetch_specs end
def sudo(str)
def sudo(str) Bundler.sudo(str) end
def to_lock
def to_lock out = "GEM\n" out << remotes.map {|r| " remote: #{r}\n" }.join out << " specs:\n" end
def to_s
def to_s remote_names = self.remotes.map { |r| r.to_s }.join(', ') "rubygems repository #{remote_names}" end