summaryrefslogtreecommitdiff
path: root/lib/rubygems
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems')
-rw-r--r--lib/rubygems/builder.rb13
-rw-r--r--lib/rubygems/command.rb10
-rw-r--r--lib/rubygems/command_manager.rb18
-rw-r--r--lib/rubygems/commands/build_command.rb24
-rw-r--r--lib/rubygems/commands/cert_command.rb2
-rw-r--r--lib/rubygems/commands/cleanup_command.rb29
-rw-r--r--lib/rubygems/commands/contents_command.rb31
-rw-r--r--lib/rubygems/commands/dependency_command.rb68
-rw-r--r--lib/rubygems/commands/fetch_command.rb11
-rw-r--r--lib/rubygems/commands/install_command.rb3
-rw-r--r--lib/rubygems/commands/lock_command.rb8
-rw-r--r--lib/rubygems/commands/outdated_command.rb8
-rw-r--r--lib/rubygems/commands/pristine_command.rb50
-rw-r--r--lib/rubygems/commands/query_command.rb15
-rw-r--r--lib/rubygems/commands/server_command.rb2
-rw-r--r--lib/rubygems/commands/setup_command.rb2
-rw-r--r--lib/rubygems/commands/specification_command.rb31
-rw-r--r--lib/rubygems/commands/stale_command.rb3
-rw-r--r--lib/rubygems/commands/unpack_command.rb60
-rw-r--r--lib/rubygems/commands/update_command.rb40
-rw-r--r--lib/rubygems/commands/which_command.rb14
-rw-r--r--lib/rubygems/config_file.rb16
-rw-r--r--lib/rubygems/custom_require.rb11
-rw-r--r--lib/rubygems/defaults.rb44
-rw-r--r--lib/rubygems/dependency.rb52
-rw-r--r--lib/rubygems/dependency_installer.rb78
-rw-r--r--lib/rubygems/dependency_list.rb29
-rw-r--r--lib/rubygems/deprecate.rb74
-rw-r--r--lib/rubygems/doc_manager.rb21
-rw-r--r--lib/rubygems/ext/builder.rb2
-rw-r--r--lib/rubygems/ext/rake_builder.rb4
-rw-r--r--lib/rubygems/gem_path_searcher.rb26
-rw-r--r--lib/rubygems/gem_runner.rb4
-rw-r--r--lib/rubygems/indexer.rb238
-rw-r--r--lib/rubygems/installer.rb209
-rw-r--r--lib/rubygems/installer_test_case.rb58
-rw-r--r--lib/rubygems/local_remote_options.rb4
-rw-r--r--lib/rubygems/mock_gem_ui.rb14
-rw-r--r--lib/rubygems/package.rb28
-rw-r--r--lib/rubygems/package/tar_input.rb12
-rw-r--r--lib/rubygems/package/tar_writer.rb2
-rw-r--r--lib/rubygems/package_task.rb2
-rw-r--r--lib/rubygems/path_support.rb78
-rw-r--r--lib/rubygems/platform.rb14
-rw-r--r--lib/rubygems/remote_fetcher.rb114
-rw-r--r--lib/rubygems/requirement.rb12
-rw-r--r--lib/rubygems/security.rb2
-rw-r--r--lib/rubygems/server.rb134
-rw-r--r--lib/rubygems/source_index.rb172
-rw-r--r--lib/rubygems/spec_fetcher.rb17
-rw-r--r--lib/rubygems/specification.rb2467
-rw-r--r--lib/rubygems/test_case.rb188
-rw-r--r--lib/rubygems/test_utilities.rb7
-rw-r--r--lib/rubygems/text.rb2
-rw-r--r--lib/rubygems/uninstaller.rb89
-rw-r--r--lib/rubygems/user_interaction.rb58
56 files changed, 2852 insertions, 1872 deletions
diff --git a/lib/rubygems/builder.rb b/lib/rubygems/builder.rb
index c07369f256..ea4e13ec4d 100644
--- a/lib/rubygems/builder.rb
+++ b/lib/rubygems/builder.rb
@@ -44,7 +44,7 @@ class Gem::Builder
@signer = sign
write_package
say success if Gem.configuration.verbose
- @spec.file_name
+ File.basename @spec.cache_file
end
def success
@@ -52,7 +52,7 @@ class Gem::Builder
Successfully built RubyGem
Name: #{@spec.name}
Version: #{@spec.version}
- File: #{@spec.file_name}
+ File: #{File.basename @spec.cache_file}
EOM
end
@@ -79,16 +79,17 @@ EOM
end
def write_package
- open @spec.file_name, 'wb' do |gem_io|
+ file_name = File.basename @spec.cache_file
+ open file_name, 'wb' do |gem_io|
Gem::Package.open gem_io, 'w', @signer do |pkg|
yaml = @spec.to_yaml
pkg.metadata = yaml
@spec.files.each do |file|
- next if File.directory? file
- next if file == @spec.file_name # Don't add gem onto itself
+ next if File.directory?(file)
+ next if file == file_name # Don't add gem onto itself
- stat = File.stat file
+ stat = File.stat(file)
mode = stat.mode & 0777
size = stat.size
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index 98503cb40f..fd6cb01811 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -410,10 +410,12 @@ class Gem::Command
end
end
- @parser.separator nil
- @parser.separator " Summary:"
- wrap(@summary, 80 - 4).split("\n").each do |line|
- @parser.separator " #{line.strip}"
+ if @summary then
+ @parser.separator nil
+ @parser.separator " Summary:"
+ wrap(@summary, 80 - 4).split("\n").each do |line|
+ @parser.separator " #{line.strip}"
+ end
end
if description then
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 60d82c865e..3665f31d0f 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -44,6 +44,13 @@ class Gem::CommandManager
end
##
+ # Reset the authoritative instance of the command manager.
+
+ def self.reset
+ @command_manager = nil
+ end
+
+ ##
# Register all the subcommands supported by the gem command.
def initialize
@@ -87,6 +94,13 @@ class Gem::CommandManager
end
##
+ # Unregister the Symbol +command+ as a gem command.
+
+ def unregister_command(command)
+ @commands.delete command
+ end
+
+ ##
# Return the registered command from the command name.
def [](command_name)
@@ -166,7 +180,7 @@ class Gem::CommandManager
retried = false
begin
- commands.const_get const_name
+ commands.const_get(const_name).new
rescue NameError
raise if retried
@@ -179,7 +193,7 @@ class Gem::CommandManager
Gem.configuration.backtrace
end
retry
- end.new
+ end
end
end
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 5aac489459..cdae367365 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -23,32 +23,34 @@ class Gem::Commands::BuildCommand < Gem::Command
def execute
gemspec = get_one_gem_name
- if File.exist?(gemspec)
- specs = load_gemspecs(gemspec)
- specs.each do |spec|
+
+ if File.exist? gemspec
+ spec = load_gemspec gemspec
+
+ if spec then
Gem::Builder.new(spec).build
+ else
+ alert_error "Error loading gemspec. Aborting."
+ terminate_interaction 1
end
else
alert_error "Gemspec file not found: #{gemspec}"
+ terminate_interaction 1
end
end
- def load_gemspecs(filename)
+ def load_gemspec filename
if yaml?(filename)
- result = []
open(filename) do |f|
begin
- while not f.eof? and spec = Gem::Specification.from_yaml(f)
- result << spec
- end
+ Gem::Specification.from_yaml(f)
rescue Gem::EndOfYAMLException
- # OK
+ nil
end
end
else
- result = [Gem::Specification.load(filename)]
+ Gem::Specification.load(filename) # can return nil
end
- result
end
def yaml?(filename)
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 17eaaa113f..d667f5c3a3 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -56,7 +56,7 @@ class Gem::Commands::CertCommand < Gem::Command
'Build private key and self-signed',
'certificate for EMAIL_ADDR.') do |value, options|
vals = Gem::Security.build_self_signed_cert(value)
- File.chmod 0600, vals[:key_path]
+ FileUtils.chmod 0600, vals[:key_path]
say "Public Cert: #{vals[:cert_path]}"
say "Private Key: #{vals[:key_path]}"
say "Don't forget to move the key file to somewhere private..."
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 0cdf50433e..2d8d8ff4fc 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -5,7 +5,6 @@
######################################################################
require 'rubygems/command'
-require 'rubygems/source_index'
require 'rubygems/dependency_list'
require 'rubygems/uninstaller'
@@ -44,28 +43,20 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it.
say "Cleaning up installed gems..."
primary_gems = {}
- Gem.source_index.each do |name, spec|
+ Gem::Specification.each do |spec|
if primary_gems[spec.name].nil? or
primary_gems[spec.name].version < spec.version then
primary_gems[spec.name] = spec
end
end
- gems_to_cleanup = []
-
- unless options[:args].empty? then
- options[:args].each do |gem_name|
- dep = Gem::Dependency.new gem_name, Gem::Requirement.default
- specs = Gem.source_index.search dep
- specs.each do |spec|
- gems_to_cleanup << spec
- end
- end
- else
- Gem.source_index.each do |name, spec|
- gems_to_cleanup << spec
- end
- end
+ gems_to_cleanup = unless options[:args].empty? then
+ options[:args].map do |gem_name|
+ Gem::Specification.find_all_by_name gem_name
+ end.flatten
+ else
+ Gem::Specification.to_a
+ end
gems_to_cleanup = gems_to_cleanup.select { |spec|
primary_gems[spec.name].version != spec.version
@@ -89,8 +80,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it.
:version => "= #{spec.version}",
}
- if Gem.user_dir == spec.installation_path then
- uninstall_options[:install_dir] = spec.installation_path
+ if Gem.user_dir == spec.base_dir then
+ uninstall_options[:install_dir] = spec.base_dir
end
uninstaller = Gem::Uninstaller.new spec.name, uninstall_options
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index 42ae913b01..8e015ae9da 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -58,24 +58,24 @@ class Gem::Commands::ContentsCommand < Gem::Command
end.flatten
path_kind = if spec_dirs.empty? then
- spec_dirs = Gem::SourceIndex.installed_spec_directories
+ spec_dirs = Gem::Specification.dirs
"default gem paths"
else
"specified path"
end
- si = Gem::SourceIndex.from_gems_in(*spec_dirs)
-
gem_names = if options[:all] then
- si.map { |_, spec| spec.name }
+ Gem::Specification.map(&:name)
else
get_all_gem_names
end
gem_names.each do |name|
- gem_spec = si.find_name(name, version).last
+ # HACK: find_by_name fails for some reason... ARGH
+ # How many places must we embed our resolve logic?
+ spec = Gem::Specification.find_all_by_name(name, version).last
- unless gem_spec then
+ unless spec then
say "Unable to find gem '#{name}' in #{path_kind}"
if Gem.configuration.verbose then
@@ -86,16 +86,19 @@ class Gem::Commands::ContentsCommand < Gem::Command
terminate_interaction 1 if gem_names.length == 1
end
- files = options[:lib_only] ? gem_spec.lib_files : gem_spec.files
+ gem_path = spec.full_gem_path
+ extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only]
+ glob = "#{gem_path}#{extra}/**/*"
+ files = Dir[glob]
+
+ gem_path = File.join gem_path, '' # add trailing / if missing
+
+ files.sort.each do |file|
+ next if File.directory? file
- files.each do |f|
- path = if options[:prefix] then
- File.join gem_spec.full_gem_path, f
- else
- f
- end
+ file = file.sub gem_path, '' unless options[:prefix]
- say path
+ say file
end
end
end
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index b6840c35d4..209fdebd50 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -49,13 +49,13 @@ class Gem::Commands::DependencyCommand < Gem::Command
end
def execute
- options[:args] << '' if options[:args].empty?
- specs = {}
-
- source_indexes = Hash.new do |h, source_uri|
- h[source_uri] = Gem::SourceIndex.new
+ if options[:reverse_dependencies] and remote? and not local? then
+ alert_error 'Only reverse dependencies for local gems are supported.'
+ terminate_interaction 1
end
+ options[:args] << '' if options[:args].empty?
+
pattern = if options[:args].length == 1 and
options[:args].first =~ /\A\/(.*)\/(i)?\z/m then
flags = $2 ? Regexp::IGNORECASE : nil
@@ -64,34 +64,30 @@ class Gem::Commands::DependencyCommand < Gem::Command
/\A#{Regexp.union(*options[:args])}/
end
- dependency = Gem::Dependency.new pattern, options[:version]
+ # TODO: deprecate for real damnit
+ dependency = Deprecate.skip_during {
+ Gem::Dependency.new pattern, options[:version]
+ }
dependency.prerelease = options[:prerelease]
- if options[:reverse_dependencies] and remote? and not local? then
- alert_error 'Only reverse dependencies for local gems are supported.'
- terminate_interaction 1
- end
+ specs = []
- if local? then
- Gem.source_index.search(dependency).each do |spec|
- source_indexes[:local].add_spec spec
- end
- end
+ specs.concat dependency.matching_specs if local?
if remote? and not options[:reverse_dependencies] then
fetcher = Gem::SpecFetcher.fetcher
- specs_and_sources = fetcher.find_matching(dependency, false, true,
+ # REFACTOR: fetcher.find_specs_matching => specs
+ specs_and_sources = fetcher.find_matching(dependency,
+ dependency.specific?, true,
dependency.prerelease?)
- specs_and_sources.each do |spec_tuple, source_uri|
- spec = fetcher.fetch_spec spec_tuple, URI.parse(source_uri)
-
- source_indexes[source_uri].add_spec spec
- end
+ specs.concat specs_and_sources.map { |spec_tuple, source_uri|
+ fetcher.fetch_spec spec_tuple, URI.parse(source_uri)
+ }
end
- if source_indexes.empty? then
+ if specs.empty? then
patterns = options[:args].join ','
say "No gems found matching #{patterns} (#{options[:version]})" if
Gem.configuration.verbose
@@ -99,24 +95,18 @@ class Gem::Commands::DependencyCommand < Gem::Command
terminate_interaction 1
end
- specs = {}
-
- source_indexes.values.each do |source_index|
- source_index.gems.each do |name, spec|
- specs[spec.full_name] = [source_index, spec]
- end
- end
+ specs = specs.uniq.sort
reverse = Hash.new { |h, k| h[k] = [] }
if options[:reverse_dependencies] then
- specs.values.each do |_, spec|
+ specs.each do |spec|
reverse[spec.full_name] = find_reverse_dependencies spec
end
end
if options[:pipe_format] then
- specs.values.sort_by { |_, spec| spec }.each do |_, spec|
+ specs.each do |spec|
unless spec.dependencies.empty?
spec.dependencies.sort_by { |dep| dep.name }.each do |dep|
say "#{dep.name} --version '#{dep.requirement}'"
@@ -126,7 +116,7 @@ class Gem::Commands::DependencyCommand < Gem::Command
else
response = ''
- specs.values.sort_by { |_, spec| spec }.each do |_, spec|
+ specs.each do |spec|
response << print_dependencies(spec)
unless reverse[spec.full_name].empty? then
response << " Used by\n"
@@ -158,7 +148,7 @@ class Gem::Commands::DependencyCommand < Gem::Command
def find_reverse_dependencies(spec)
result = []
- Gem.source_index.each do |name, sp|
+ Gem::Specification.each do |sp|
sp.dependencies.each do |dep|
dep = Gem::Dependency.new(*dep) unless Gem::Dependency === dep
@@ -172,17 +162,5 @@ class Gem::Commands::DependencyCommand < Gem::Command
result
end
- def find_gems(name, source_index)
- specs = {}
-
- spec_list = source_index.search name, options[:version]
-
- spec_list.each do |spec|
- specs[spec.full_name] = [source_index, spec]
- end
-
- specs
- end
-
end
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index c0fdc83e46..7d99276506 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -41,19 +41,22 @@ class Gem::Commands::FetchCommand < Gem::Command
version = options[:version] || Gem::Requirement.default
all = Gem::Requirement.default != version
+ platform = Gem.platforms.last
gem_names = get_all_gem_names
gem_names.each do |gem_name|
dep = Gem::Dependency.new gem_name, version
dep.prerelease = options[:prerelease]
- specs_and_sources = Gem::SpecFetcher.fetcher.fetch(dep, all, true,
- dep.prerelease?)
-
specs_and_sources, errors =
Gem::SpecFetcher.fetcher.fetch_with_errors(dep, all, true,
dep.prerelease?)
+ if platform then
+ filtered = specs_and_sources.select { |s,| s.platform == platform }
+ specs_and_sources = filtered unless filtered.empty?
+ end
+
spec, source_uri = specs_and_sources.sort_by { |s,| s.version }.last
if spec.nil? then
@@ -62,7 +65,7 @@ class Gem::Commands::FetchCommand < Gem::Command
end
path = Gem::RemoteFetcher.fetcher.download spec, source_uri
- FileUtils.mv path, spec.file_name
+ FileUtils.mv path, File.basename(spec.cache_file)
say "Downloaded #{spec.full_name}"
end
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 530f746e89..fff41cf6e0 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -120,7 +120,8 @@ to write the specification by hand. For example:
get_all_gem_names.each do |gem_name|
begin
- next if options[:conservative] && Gem.available?(gem_name, options[:version])
+ next if options[:conservative] and
+ not Gem::Dependency.new(gem_name, options[:version]).matching_specs.empty?
inst = Gem::DependencyInstaller.new options
inst.install gem_name, options[:version]
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index a17dd36d7a..9ce9da6899 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -93,7 +93,7 @@ lock it down to the exact version.
spec.runtime_dependencies.each do |dep|
next if locked[dep.name]
- candidates = Gem.source_index.search dep
+ candidates = dep.matching_specs
if candidates.empty? then
complain "Unable to satisfy '#{dep}' from currently installed gems"
@@ -105,11 +105,11 @@ lock it down to the exact version.
end
def spec_path(gem_full_name)
- gemspecs = Gem.path.map do |path|
+ gemspecs = Gem.path.map { |path|
File.join path, "specifications", "#{gem_full_name}.gemspec"
- end
+ }
- gemspecs.find { |gemspec| File.exist? gemspec }
+ gemspecs.find { |path| File.exist? path }
end
end
diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb
index 7b6e1cfe8e..8b7dda6c7d 100644
--- a/lib/rubygems/commands/outdated_command.rb
+++ b/lib/rubygems/commands/outdated_command.rb
@@ -22,10 +22,8 @@ class Gem::Commands::OutdatedCommand < Gem::Command
end
def execute
- locals = Gem::SourceIndex.from_installed_gems
-
- locals.outdated.sort.each do |name|
- local = locals.find_name(name).last
+ Gem::Specification.outdated.sort.each do |name|
+ local = Gem::Specification.find_all_by_name(name).max
dep = Gem::Dependency.new local.name, ">= #{local.version}"
remotes = Gem::SpecFetcher.fetcher.fetch dep
@@ -35,6 +33,4 @@ class Gem::Commands::OutdatedCommand < Gem::Command
say "#{local.name} (#{local.version} < #{remote.version})"
end
end
-
end
-
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 9a0b2e9f11..99722756ee 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -16,7 +16,8 @@ class Gem::Commands::PristineCommand < Gem::Command
def initialize
super 'pristine',
'Restores installed gems to pristine condition from files located in the gem cache',
- :version => Gem::Requirement.default
+ :version => Gem::Requirement.default, :extensions => true,
+ :all => false
add_option('--all',
'Restore all installed gems to pristine',
@@ -24,6 +25,11 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:all] = value
end
+ add_option('--[no-]extensions',
+ 'Restore gems with extensions') do |value, options|
+ options[:extensions] = value
+ end
+
add_version_option('restore to', 'pristine condition')
end
@@ -32,7 +38,7 @@ class Gem::Commands::PristineCommand < Gem::Command
end
def defaults_str # :nodoc:
- "--all"
+ "--all --extensions"
end
def description # :nodoc:
@@ -46,6 +52,9 @@ for the gem are regenerated.
If the cached gem cannot be found, you will need to use `gem install` to
revert the gem.
+
+If --no-extensions is provided pristine will not attempt to restore gems with
+extensions.
EOF
end
@@ -54,21 +63,17 @@ revert the gem.
end
def execute
- gem_name = nil
-
specs = if options[:all] then
- Gem::SourceIndex.from_installed_gems.map do |name, spec|
- spec
- end
+ Gem::Specification.map
else
- gem_name = get_one_gem_name
- Gem::SourceIndex.from_installed_gems.find_name(gem_name,
- options[:version])
+ get_all_gem_names.map do |gem_name|
+ Gem::Specification.find_all_by_name gem_name, options[:version]
+ end.flatten
end
- if specs.empty? then
+ if specs.to_a.empty? then
raise Gem::Exception,
- "Failed to find gem #{gem_name} #{options[:version]}"
+ "Failed to find gems #{options[:args]} #{options[:version]}"
end
install_dir = Gem.dir # TODO use installer option
@@ -76,25 +81,32 @@ revert the gem.
raise Gem::FilePermissionError.new(install_dir) unless
File.writable?(install_dir)
- say "Restoring gem(s) to pristine condition..."
+ say "Restoring gems to pristine condition..."
specs.each do |spec|
- gem = spec.cache_gem
+ unless spec.extensions.empty? or options[:extensions] then
+ say "Skipped #{spec.full_name}, it needs to compile an extension"
+ next
+ end
+
+ gem = spec.cache_file
+
+ unless File.exist? gem then
+ require 'rubygems/remote_fetcher'
- if gem.nil? then
say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
dep = Gem::Dependency.new spec.name, spec.version
Gem::RemoteFetcher.fetcher.download_to_cache dep
- gem = spec.cache_gem
end
# TODO use installer options
- installer = Gem::Installer.new gem, :wrappers => true, :force => true
+ installer = Gem::Installer.new(gem,
+ :wrappers => true,
+ :force => true,
+ :install_dir => spec.base_dir)
installer.install
say "Restored #{spec.full_name}"
end
end
-
end
-
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index 29e53065e7..d82f8aea60 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -80,10 +80,12 @@ class Gem::Commands::QueryCommand < Gem::Command
exit_code |= 1
end
- raise Gem::SystemExitException, exit_code
+ terminate_interaction exit_code
end
- dep = Gem::Dependency.new name, Gem::Requirement.default
+ req = Gem::Requirement.default
+ # TODO: deprecate for real
+ dep = Deprecate.skip_during { Gem::Dependency.new name, req }
if local? then
if prerelease and not both? then
@@ -96,7 +98,9 @@ class Gem::Commands::QueryCommand < Gem::Command
say
end
- specs = Gem.source_index.search dep
+ specs = Gem::Specification.find_all { |s|
+ s.name =~ name and req =~ s.version
+ }
spec_tuples = specs.map do |spec|
[[spec.name, spec.version, spec.original_platform, spec], :local]
@@ -129,9 +133,8 @@ class Gem::Commands::QueryCommand < Gem::Command
##
# Check if gem +name+ version +version+ is installed.
- def installed?(name, version = Gem::Requirement.default)
- dep = Gem::Dependency.new name, version
- !Gem.source_index.search(dep).empty?
+ def installed?(name, req = Gem::Requirement.default)
+ Gem::Specification.any? { |s| s.name =~ name and req =~ s.version }
end
def output_query_results(spec_tuples)
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
index c59f216262..0d18a82201 100644
--- a/lib/rubygems/commands/server_command.rb
+++ b/lib/rubygems/commands/server_command.rb
@@ -50,7 +50,7 @@ class Gem::Commands::ServerCommand < Gem::Command
options[:addresses].push(*address)
end
- add_option '-l', '--launch[=COMMAND]',
+ add_option '-l', '--launch[=COMMAND]',
'launches a browser window',
"COMMAND defaults to 'start' on Windows",
"and 'open' on all other platforms" do |launch, options|
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index cf844c6674..6aa80054a0 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -338,7 +338,7 @@ abort "#{deprecation_message}"
args << '--main' << 'README.rdoc' << '--quiet'
args << '.'
args << 'README.rdoc' << 'UPGRADING.rdoc'
- args << 'LICENSE.txt' << 'GPL.txt' << 'History.txt'
+ args << 'LICENSE.txt' << 'MIT.txt' << 'History.txt'
r = RDoc::RDoc.new
r.document args
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 987e401042..e4036aea78 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -72,18 +72,8 @@ FIELD name of gemspec field to show
field = get_one_optional_argument
- if field then
- field = field.intern
-
- if options[:format] == :ruby then
- raise Gem::CommandLineError, "--ruby and FIELD are mutually exclusive"
- end
-
- unless Gem::Specification.attribute_names.include? field then
- raise Gem::CommandLineError,
- "no field %p on Gem::Specification" % field.to_s
- end
- end
+ raise Gem::CommandLineError, "--ruby and FIELD are mutually exclusive" if
+ field and options[:format] == :ruby
if local? then
if File.exist? gem then
@@ -91,7 +81,7 @@ FIELD name of gemspec field to show
end
if specs.empty? then
- specs.push(*Gem.source_index.search(dep))
+ specs.push(*dep.matching_specs)
end
end
@@ -106,7 +96,11 @@ FIELD name of gemspec field to show
terminate_interaction 1
end
- output = lambda do |s|
+ unless options[:all] then
+ specs = [specs.sort_by { |s| s.version }.last]
+ end
+
+ specs.each do |s|
s = s.send field if field
say case options[:format]
@@ -117,14 +111,5 @@ FIELD name of gemspec field to show
say "\n"
end
-
- if options[:all] then
- specs.each(&output)
- else
- spec = specs.sort_by { |s| s.version }.last
- output[spec]
- end
end
-
end
-
diff --git a/lib/rubygems/commands/stale_command.rb b/lib/rubygems/commands/stale_command.rb
index 9a024faa6d..712e62aa22 100644
--- a/lib/rubygems/commands/stale_command.rb
+++ b/lib/rubygems/commands/stale_command.rb
@@ -17,7 +17,8 @@ class Gem::Commands::StaleCommand < Gem::Command
def execute
gem_to_atime = {}
- Gem.source_index.each do |name, spec|
+ Gem::Specification.each do |spec|
+ name = spec.full_name
Dir["#{spec.full_gem_path}/**/*.*"].each do |file|
next if File.directory?(file)
stat = File.stat(file)
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index ef0d65e073..b0fa2bfb05 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -25,6 +25,10 @@ class Gem::Commands::UnpackCommand < Gem::Command
options[:target] = value
end
+ add_option('--spec', 'unpack the gem specification') do |value, options|
+ options[:spec] = true
+ end
+
add_version_option
end
@@ -50,14 +54,30 @@ class Gem::Commands::UnpackCommand < Gem::Command
dependency = Gem::Dependency.new name, options[:version]
path = get_path dependency
- if path then
+ unless path then
+ alert_error "Gem '#{name}' not installed nor fetchable."
+ next
+ end
+
+ if @options[:spec] then
+ spec, metadata = get_metadata path
+
+ if metadata.nil? then
+ alert_error "--spec is unsupported on '#{name}' (old format gem)"
+ next
+ end
+
+ spec_file = File.basename spec.spec_file
+
+ open spec_file, 'w' do |io|
+ io.write metadata
+ end
+ else
basename = File.basename path, '.gem'
target_dir = File.expand_path basename, options[:target]
FileUtils.mkdir_p target_dir
Gem::Installer.new(path, :unpack => true).unpack target_dir
say "Unpacked gem: '#{target_dir}'"
- else
- alert_error "Gem '#{name}' not installed."
end
end
end
@@ -70,9 +90,8 @@ class Gem::Commands::UnpackCommand < Gem::Command
# TODO: see comments in get_path() about general service.
def find_in_cache(filename)
-
Gem.path.each do |path|
- this_path = Gem.cache_gem(filename, path)
+ this_path = File.join(path, "cache", filename)
return this_path if File.exist? this_path
end
@@ -99,9 +118,9 @@ class Gem::Commands::UnpackCommand < Gem::Command
def get_path dependency
return dependency.name if dependency.name =~ /\.gem$/i
- specs = Gem.source_index.search dependency
+ specs = dependency.matching_specs
- selected = specs.sort_by { |s| s.version }.last
+ selected = specs.sort_by { |s| s.version }.last # HACK: hunt last down
return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless
selected
@@ -111,12 +130,37 @@ class Gem::Commands::UnpackCommand < Gem::Command
# We expect to find (basename).gem in the 'cache' directory. Furthermore,
# the name match must be exact (ignoring case).
- path = find_in_cache selected.file_name
+ path = find_in_cache File.basename selected.cache_file
return Gem::RemoteFetcher.fetcher.download_to_cache(dependency) unless path
path
end
+ ##
+ # Extracts the Gem::Specification and raw metadata from the .gem file at
+ # +path+.
+
+ def get_metadata path
+ format = Gem::Format.from_file_by_path path
+ spec = format.spec
+
+ metadata = nil
+
+ open path, Gem.binary_mode do |io|
+ tar = Gem::Package::TarReader.new io
+ tar.each_entry do |entry|
+ case entry.full_name
+ when 'metadata' then
+ metadata = entry.read
+ when 'metadata.gz' then
+ metadata = Gem.gunzip entry.read
+ end
+ end
+ end
+
+ return spec, metadata
+ end
+
end
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index b7c65eb2a4..2d7da445cb 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -71,14 +71,14 @@ class Gem::Commands::UpdateCommand < Gem::Command
hig = {} # highest installed gems
- Gem.source_index.each do |name, spec|
+ Gem::Specification.each do |spec|
if hig[spec.name].nil? or hig[spec.name].version < spec.version then
hig[spec.name] = spec
end
end
end
- gems_to_update = which_to_update hig, options[:args]
+ gems_to_update = which_to_update hig, options[:args].uniq
updated = update_gems gems_to_update
@@ -123,8 +123,8 @@ class Gem::Commands::UpdateCommand < Gem::Command
end
def update_gems gems_to_update
- gems_to_update.uniq.sort.each do |name|
- update_gem name
+ gems_to_update.uniq.sort.each do |(name, version)|
+ update_gem name, version
end
@updated
@@ -141,6 +141,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
options[:user_install] = false
+ # TODO: rename version and other variable name conflicts
+ # TODO: get rid of all this indirection on name and other BS
+
version = options[:system]
if version == true then
version = Gem::Version.new Gem::VERSION
@@ -158,18 +161,25 @@ class Gem::Commands::UpdateCommand < Gem::Command
'rubygems-update' => rubygems_update
}
- gems_to_update = which_to_update hig, options[:args]
+ gems_to_update = which_to_update hig, options[:args], :system
+ name, up_ver = gems_to_update.first
+ current_ver = Gem::Version.new Gem::VERSION
+
+ target = if options[:system] == true then
+ up_ver
+ else
+ version
+ end
- if gems_to_update.empty? then
+ if current_ver == target then
+ # if options[:system] != true and version == current_ver then
say "Latest version currently installed. Aborting."
terminate_interaction
end
- update_gem gems_to_update.first, requirement
+ update_gem name, target
- Gem.source_index.refresh!
-
- installed_gems = Gem.source_index.find_name 'rubygems-update', requirement
+ installed_gems = Gem::Specification.find_all_by_name 'rubygems-update', requirement
version = installed_gems.last.version
args = []
@@ -193,7 +203,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
end
end
- def which_to_update(highest_installed_gems, gem_names)
+ def which_to_update highest_installed_gems, gem_names, system = false
result = []
highest_installed_gems.each do |l_name, l_spec|
@@ -213,9 +223,11 @@ class Gem::Commands::UpdateCommand < Gem::Command
version
end.last
- if highest_remote_gem and
- l_spec.version < highest_remote_gem.first[1] then
- result << l_name
+ highest_remote_gem ||= [[nil, Gem::Version.new(0), nil]] # "null" object
+ highest_remote_ver = highest_remote_gem.first[1]
+
+ if system or (l_spec.version < highest_remote_ver) then
+ result << [l_spec.name, [l_spec.version, highest_remote_ver].max]
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index aeb91f203c..2962491bde 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -5,12 +5,8 @@
######################################################################
require 'rubygems/command'
-require 'rubygems/gem_path_searcher'
class Gem::Commands::WhichCommand < Gem::Command
-
- EXT = %w[.rb .rbw .so .dll .bundle] # HACK
-
def initialize
super 'which', 'Find the location of a library file you can require',
:search_gems_first => false, :show_all => false
@@ -34,14 +30,13 @@ class Gem::Commands::WhichCommand < Gem::Command
end
def execute
- searcher = Gem::GemPathSearcher.new
-
found = false
options[:args].each do |arg|
- arg = arg.sub(/#{Regexp.union(*EXT)}$/, '')
+ arg = arg.sub(/#{Regexp.union(*Gem.suffixes)}$/, '')
dirs = $LOAD_PATH
- spec = searcher.find arg
+
+ spec = Gem::Specification.find_by_path arg
if spec then
if options[:search_gems_first] then
@@ -51,6 +46,7 @@ class Gem::Commands::WhichCommand < Gem::Command
end
end
+ # TODO: this is totally redundant and stupid
paths = find_paths arg, dirs
if paths.empty? then
@@ -68,7 +64,7 @@ class Gem::Commands::WhichCommand < Gem::Command
result = []
dirs.each do |dir|
- EXT.each do |ext|
+ Gem.suffixes.each do |ext|
full_path = File.join dir, "#{package_name}#{ext}"
if File.exist? full_path then
result << full_path
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 1d16cd90ce..d005755f48 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -207,11 +207,15 @@ class Gem::ConfigFile
# Location of RubyGems.org credentials
def credentials_path
- File.join(Gem.user_home, '.gem', 'credentials')
+ File.join Gem.user_home, '.gem', 'credentials'
end
def load_api_keys
- @api_keys = File.exists?(credentials_path) ? load_file(credentials_path) : @hash
+ @api_keys = if File.exist? credentials_path then
+ load_file(credentials_path)
+ else
+ @hash
+ end
if @api_keys.key? :rubygems_api_key then
@rubygems_api_key = @api_keys[:rubygems_api_key]
@api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless @api_keys.key? :rubygems
@@ -221,8 +225,8 @@ class Gem::ConfigFile
def rubygems_api_key=(api_key)
config = load_file(credentials_path).merge(:rubygems_api_key => api_key)
- dirname = File.dirname(credentials_path)
- Dir.mkdir(dirname) unless File.exists?(dirname)
+ dirname = File.dirname credentials_path
+ Dir.mkdir(dirname) unless File.exist? dirname
Gem.load_yaml
@@ -236,7 +240,7 @@ class Gem::ConfigFile
def load_file(filename)
Gem.load_yaml
- return {} unless filename and File.exists?(filename)
+ return {} unless filename and File.exist? filename
begin
YAML.load(File.read(filename))
rescue ArgumentError
@@ -360,6 +364,4 @@ class Gem::ConfigFile
protected
attr_reader :hash
-
end
-
diff --git a/lib/rubygems/custom_require.rb b/lib/rubygems/custom_require.rb
index eaa04b31c3..f32d1007c9 100644
--- a/lib/rubygems/custom_require.rb
+++ b/lib/rubygems/custom_require.rb
@@ -41,19 +41,20 @@ module Kernel
if Gem.unresolved_deps.empty? or Gem.loaded_path? path then
gem_original_require path
else
- spec = Gem.searcher.find_active path
+ spec = Gem::Specification.find { |s|
+ s.activated? and s.contains_requirable_file? path
+ }
unless spec then
- found_specs = Gem.searcher.find_in_unresolved path
+ found_specs = Gem::Specification.find_in_unresolved path
unless found_specs.empty? then
found_specs = [found_specs.last]
else
- found_specs = Gem.searcher.find_in_unresolved_tree path
+ found_specs = Gem::Specification.find_in_unresolved_tree path
end
found_specs.each do |found_spec|
- # FIX: this is dumb, activate a spec instead of name/version
- Gem.activate found_spec.name, found_spec.version
+ found_spec.activate
end
end
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 63ccd85e2a..714dc4fb2f 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -6,6 +6,8 @@
module Gem
+ # TODO: move this whole file back into rubygems.rb
+
@post_install_hooks ||= []
@post_uninstall_hooks ||= []
@pre_uninstall_hooks ||= []
@@ -23,16 +25,28 @@ module Gem
# specified in the environment
def self.default_dir
- if defined? RUBY_FRAMEWORK_VERSION then
- File.join File.dirname(ConfigMap[:sitedir]), 'Gems',
- ConfigMap[:ruby_version]
- elsif ConfigMap[:rubylibprefix] then
- File.join(ConfigMap[:rubylibprefix], 'gems',
- ConfigMap[:ruby_version])
- else
- File.join(ConfigMap[:libdir], ruby_engine, 'gems',
- ConfigMap[:ruby_version])
- end
+ path = if defined? RUBY_FRAMEWORK_VERSION then
+ [
+ File.dirname(ConfigMap[:sitedir]),
+ 'Gems',
+ ConfigMap[:ruby_version]
+ ]
+ elsif ConfigMap[:rubylibprefix] then
+ [
+ ConfigMap[:rubylibprefix],
+ 'gems',
+ ConfigMap[:ruby_version]
+ ]
+ else
+ [
+ ConfigMap[:libdir],
+ ruby_engine,
+ 'gems',
+ ConfigMap[:ruby_version]
+ ]
+ end
+
+ @default_dir ||= File.join(*path)
end
##
@@ -82,14 +96,18 @@ module Gem
# The default system-wide source info cache directory
def self.default_system_source_cache_dir
- File.join Gem.dir, 'source_cache'
+ File.join(Gem.dir, 'source_cache')
end
##
# The default user-specific source info cache directory
def self.default_user_source_cache_dir
- File.join Gem.user_home, '.gem', 'source_cache'
+ #
+ # NOTE Probably an argument for moving this to per-ruby supported dirs like
+ # user_dir
+ #
+ File.join(Gem.user_home, '.gem', 'source_cache')
end
##
@@ -102,6 +120,4 @@ module Gem
'ruby'
end
end
-
end
-
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index e4d1bfffe8..1261154fe9 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -38,6 +38,12 @@ class Gem::Dependency
# <tt>:runtime</tt>.
def initialize name, *requirements
+ if Regexp === name then
+ msg = ["NOTE: Dependency.new w/ a regexp is deprecated.",
+ "Dependency.new called from #{Gem.location_of_caller.join(":")}"]
+ warn msg.join("\n") unless Deprecate.skip
+ end
+
type = Symbol === requirements.last ? requirements.pop : :runtime
requirements = requirements.first if 1 == requirements.length # unpack
@@ -212,5 +218,49 @@ class Gem::Dependency
self.class.new name, self_req.as_list.concat(other_req.as_list)
end
-end
+ def matching_specs platform_only = false
+ matches = Gem::Specification.find_all { |spec|
+ self.name === spec.name and # TODO: == instead of ===
+ requirement.satisfied_by? spec.version
+ }
+
+ if platform_only
+ matches.reject! { |spec|
+ not Gem::Platform.match spec.platform
+ }
+ end
+
+ matches = matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
+ end
+
+ ##
+ # True if the dependency will not always match the latest version.
+
+ def specific?
+ @requirement.specific?
+ end
+ def to_specs
+ matches = matching_specs true
+
+ # TODO: check Gem.activated_spec[self.name] in case matches falls outside
+
+ if matches.empty? then
+ specs = Gem::Specification.all_names.join ", "
+ error = Gem::LoadError.new "Could not find #{name} (#{requirement}) amongst [#{specs}]"
+ error.name = self.name
+ error.requirement = self.requirement
+ raise error
+ end
+
+ # TODO: any other resolver validations should go here
+
+ matches
+ end
+
+ def to_spec
+ matches = self.to_specs
+
+ matches.find { |spec| spec.activated? } or matches.last
+ end
+end
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 5afb1bfa0d..29e8b21643 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -21,14 +21,14 @@ class Gem::DependencyInstaller
attr_reader :installed_gems
DEFAULT_OPTIONS = {
- :env_shebang => false,
- :domain => :both, # HACK dup
- :force => false,
- :format_executable => false, # HACK dup
+ :env_shebang => false,
+ :domain => :both, # HACK dup
+ :force => false,
+ :format_executable => false, # HACK dup
:ignore_dependencies => false,
- :prerelease => false,
- :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
- :wrappers => true,
+ :prerelease => false,
+ :security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
+ :wrappers => true,
}
##
@@ -51,25 +51,26 @@ class Gem::DependencyInstaller
def initialize(options = {})
if options[:install_dir] then
- spec_dir = options[:install_dir], 'specifications'
- @source_index = Gem::SourceIndex.from_gems_in spec_dir
- else
- @source_index = Gem.source_index
+ @gem_home = options[:install_dir]
+
+ Gem::Specification.dirs = @gem_home
+ Gem.ensure_gem_subdirectories @gem_home
+ options[:install_dir] = @gem_home # FIX: because we suck and reuse below
end
options = DEFAULT_OPTIONS.merge options
- @bin_dir = options[:bin_dir]
- @development = options[:development]
- @domain = options[:domain]
- @env_shebang = options[:env_shebang]
- @force = options[:force]
- @format_executable = options[:format_executable]
+ @bin_dir = options[:bin_dir]
+ @development = options[:development]
+ @domain = options[:domain]
+ @env_shebang = options[:env_shebang]
+ @force = options[:force]
+ @format_executable = options[:format_executable]
@ignore_dependencies = options[:ignore_dependencies]
- @prerelease = options[:prerelease]
- @security_policy = options[:security_policy]
- @user_install = options[:user_install]
- @wrappers = options[:wrappers]
+ @prerelease = options[:prerelease]
+ @security_policy = options[:security_policy]
+ @user_install = options[:user_install]
+ @wrappers = options[:wrappers]
@installed_gems = []
@@ -101,6 +102,7 @@ class Gem::DependencyInstaller
if @domain == :both or @domain == :remote then
begin
+ # REFACTOR: all = dep.requirement.needs_all?
requirements = dep.requirement.requirements.map do |req, ver|
req
end
@@ -146,8 +148,8 @@ class Gem::DependencyInstaller
add_found_dependencies to_do, dependency_list unless @ignore_dependencies
dependency_list.specs.reject! { |spec|
- ! keep_names.include? spec.full_name and
- @source_index.any? { |n,_| n == spec.full_name }
+ not keep_names.include?(spec.full_name) and
+ Gem::Specification.include?(spec)
}
unless dependency_list.ok? or @ignore_dependencies or @force then
@@ -181,7 +183,7 @@ class Gem::DependencyInstaller
to_do.push dep_spec
# already locally installed
- @source_index.any? do |_, installed_spec|
+ Gem::Specification.any? do |installed_spec|
dep.name == installed_spec.name and
dep.requirement.satisfied_by? installed_spec.version
end
@@ -216,23 +218,20 @@ class Gem::DependencyInstaller
local_gems = Dir["#{glob}*"].sort.reverse
- unless local_gems.empty? then
- local_gems.each do |gem_file|
- next unless gem_file =~ /gem$/
- begin
- spec = Gem::Format.from_file_by_path(gem_file).spec
- spec_and_source = [spec, gem_file]
- break
- rescue SystemCallError, Gem::Package::FormatError
- end
+ local_gems.each do |gem_file|
+ next unless gem_file =~ /gem$/
+ begin
+ spec = Gem::Format.from_file_by_path(gem_file).spec
+ spec_and_source = [spec, gem_file]
+ break
+ rescue SystemCallError, Gem::Package::FormatError
end
end
- if spec_and_source.nil? then
+ unless spec_and_source then
dep = Gem::Dependency.new gem_name, version
dep.prerelease = true if prerelease
spec_and_sources = find_gems_with_sources(dep).reverse
-
spec_and_source = spec_and_sources.find { |spec, source|
Gem::Platform.match spec.platform
}
@@ -273,9 +272,9 @@ class Gem::DependencyInstaller
gather_dependencies
- @gems_to_install.each do |spec|
- last = spec == @gems_to_install.last
- next if @source_index.any? { |n,_| n == spec.full_name } and not last
+ last = @gems_to_install.size - 1
+ @gems_to_install.each_with_index do |spec, index|
+ next if Gem::Specification.include?(spec) and index != last
# TODO: make this sorta_verbose so other users can benefit from it
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
@@ -298,7 +297,6 @@ class Gem::DependencyInstaller
:ignore_dependencies => @ignore_dependencies,
:install_dir => @install_dir,
:security_policy => @security_policy,
- :source_index => @source_index,
:user_install => @user_install,
:wrappers => @wrappers
@@ -309,6 +307,4 @@ class Gem::DependencyInstaller
@installed_gems
end
-
end
-
diff --git a/lib/rubygems/dependency_list.rb b/lib/rubygems/dependency_list.rb
index a3db1d6829..232f83c68c 100644
--- a/lib/rubygems/dependency_list.rb
+++ b/lib/rubygems/dependency_list.rb
@@ -11,6 +11,7 @@
#++
require 'tsort'
+require 'rubygems/deprecate'
##
# Gem::DependencyList is used for installing and uninstalling gems in the
@@ -28,16 +29,21 @@ class Gem::DependencyList
attr_accessor :development
##
- # Creates a DependencyList from a Gem::SourceIndex +source_index+
+ # Creates a DependencyList from the current specs.
- def self.from_source_index(source_index)
+ def self.from_specs
list = new
+ list.add(*Gem::Specification.map)
+ list
+ end
- source_index.each do |full_name, spec|
- list.add spec
- end
+ ##
+ # Creates a DependencyList from a Gem::SourceIndex +source_index+
- list
+ def self.from_source_index(ignored=nil)
+ warn "NOTE: DependencyList.from_source_index ignores it's arg" if ignored
+
+ from_specs
end
##
@@ -120,10 +126,9 @@ class Gem::DependencyList
def why_not_ok? quick = false
unsatisfied = Hash.new { |h,k| h[k] = [] }
- source_index = Gem.source_index
each do |spec|
spec.runtime_dependencies.each do |dep|
- inst = source_index.any? { |_, installed_spec|
+ inst = Gem::Specification.any? { |installed_spec|
dep.name == installed_spec.name and
dep.requirement.satisfied_by? installed_spec.version
}
@@ -134,6 +139,7 @@ class Gem::DependencyList
end
end
end
+
unsatisfied
end
@@ -242,6 +248,11 @@ class Gem::DependencyList
def active_count(specs, ignored)
specs.count { |spec| ignored[spec.full_name].nil? }
end
-
end
+class Gem::DependencyList
+ class << self
+ extend Deprecate
+ deprecate :from_source_index, "from_specs", 2011, 11
+ end
+end
diff --git a/lib/rubygems/deprecate.rb b/lib/rubygems/deprecate.rb
new file mode 100644
index 0000000000..948538da96
--- /dev/null
+++ b/lib/rubygems/deprecate.rb
@@ -0,0 +1,74 @@
+######################################################################
+# This file is imported from the rubygems project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis or Eric Hodel.
+######################################################################
+
+##
+# Provides a single method +deprecate+ to be used to declare when
+# something is going away.
+#
+# class Legacy
+# def self.klass_method
+# # ...
+# end
+#
+# def instance_method
+# # ...
+# end
+#
+# extend Deprecate
+# deprecate :instance_method, "X.z", 2011, 4
+#
+# class << self
+# extend Deprecate
+# deprecate :klass_method, :none, 2011, 4
+# end
+# end
+
+module Deprecate
+
+ def self.skip # :nodoc:
+ @skip ||= false
+ end
+
+ def self.skip= v # :nodoc:
+ @skip = v
+ end
+
+ ##
+ # Temporarily turn off warnings. Intended for tests only.
+
+ def skip_during
+ Deprecate.skip, original = true, Deprecate.skip
+ yield
+ ensure
+ Deprecate.skip = original
+ end
+
+ ##
+ # Simple deprecation method that deprecates +name+ by wrapping it up
+ # in a dummy method. It warns on each call to the dummy method
+ # telling the user of +repl+ (unless +repl+ is :none) and the
+ # year/month that it is planned to go away.
+
+ def deprecate name, repl, year, month
+ class_eval {
+ old = "_deprecated_#{name}"
+ alias_method old, name
+ define_method name do |*args, &block| # TODO: really works on 1.8.7?
+ klass = self.kind_of? Module
+ target = klass ? "#{self}." : "#{self.class}#"
+ msg = [ "NOTE: #{target}#{name} is deprecated",
+ repl == :none ? " with no replacement" : ", use #{repl}",
+ ". It will be removed on or after %4d-%02d-01." % [year, month],
+ "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
+ ]
+ warn "#{msg.join}." unless Deprecate.skip
+ send old, *args, &block
+ end
+ }
+ end
+
+ module_function :deprecate, :skip_during
+end
diff --git a/lib/rubygems/doc_manager.rb b/lib/rubygems/doc_manager.rb
index 9cfa31a0a0..20c5281231 100644
--- a/lib/rubygems/doc_manager.rb
+++ b/lib/rubygems/doc_manager.rb
@@ -92,7 +92,7 @@ class Gem::DocManager
def initialize(spec, rdoc_args="")
require 'fileutils'
@spec = spec
- @doc_dir = File.join(spec.installation_path, "doc", spec.full_name)
+ @doc_dir = spec.doc_dir
@rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
end
@@ -224,25 +224,24 @@ class Gem::DocManager
# Remove RDoc and RI documentation
def uninstall_doc
- raise Gem::FilePermissionError.new(@spec.installation_path) unless
- File.writable? @spec.installation_path
+ base_dir = @spec.base_dir
+ raise Gem::FilePermissionError.new base_dir unless File.writable? base_dir
- original_name = [
+ # TODO: ok... that's twice... ugh
+ old_name = [
@spec.name, @spec.version, @spec.original_platform].join '-'
- doc_dir = File.join @spec.installation_path, 'doc', @spec.full_name
+ doc_dir = @spec.doc_dir
unless File.directory? doc_dir then
- doc_dir = File.join @spec.installation_path, 'doc', original_name
+ doc_dir = File.join File.dirname(doc_dir), old_name
end
- FileUtils.rm_rf doc_dir
-
- ri_dir = File.join @spec.installation_path, 'ri', @spec.full_name
-
+ ri_dir = @spec.ri_dir
unless File.directory? ri_dir then
- ri_dir = File.join @spec.installation_path, 'ri', original_name
+ ri_dir = File.join File.dirname(ri_dir), old_name
end
+ FileUtils.rm_rf doc_dir
FileUtils.rm_rf ri_dir
end
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index 2a147d59f2..29d5af5b12 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -19,7 +19,7 @@ class Gem::Ext::Builder
def self.make(dest_path, results)
unless File.exist? 'Makefile' then
- raise Gem::InstallError, "Makefile not found:\n\n#{results.join "\n"}"
+ raise Gem::InstallError, "Makefile not found:\n\n#{results.join "\n"}"
end
mf = File.read('Makefile')
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index ba9fa8a94c..6b7086ce82 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -23,12 +23,12 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
end
# Deal with possible spaces in the path, e.g. C:/Program Files
- dest_path = '"' + dest_path + '"' if dest_path.include?(' ')
+ dest_path = '"' + dest_path.to_s + '"' if dest_path.to_s.include?(' ')
rake = ENV['rake']
rake ||= begin
- "\"#{Gem.ruby}\" -rubygems #{Gem.bin_path('rake')}"
+ "\"#{Gem.ruby}\" -rubygems #{Gem.bin_path('rake', 'rake')}"
rescue Gem::Exception
end
diff --git a/lib/rubygems/gem_path_searcher.rb b/lib/rubygems/gem_path_searcher.rb
index 79c3b0f6c9..36a86dec0a 100644
--- a/lib/rubygems/gem_path_searcher.rb
+++ b/lib/rubygems/gem_path_searcher.rb
@@ -4,6 +4,9 @@
# File a patch instead and assign it to Ryan Davis or Eric Hodel.
######################################################################
+require "rubygems"
+require "rubygems/deprecate"
+
##
# GemPathSearcher has the capability to find loadable files inside
# gems. It generates data up front to speed up searches later.
@@ -56,6 +59,15 @@ class Gem::GemPathSearcher
end
end
+ # Looks through the available gemspecs and finds the first
+ # one that contains +file+ as a requirable file.
+
+ def find_spec_for_file(file)
+ @gemspecs.find do |spec|
+ return spec if spec.contains_requirable_file?(file)
+ end
+ end
+
def find_active(glob)
# HACK violation of encapsulation
@gemspecs.find do |spec|
@@ -138,9 +150,7 @@ class Gem::GemPathSearcher
# in reverse version order. (bar-2, bar-1, foo-2)
def init_gemspecs
- specs = Gem.source_index.map { |_, spec| spec }
-
- specs.sort { |a, b|
+ Gem::Specification.sort { |a, b|
names = a.name <=> b.name
next names if names.nonzero?
b.version <=> a.version
@@ -156,5 +166,13 @@ class Gem::GemPathSearcher
spec.require_paths
end
-end
+ extend Deprecate
+ deprecate :initialize, :none, 2011, 10
+ deprecate :find, :none, 2011, 10
+ deprecate :find_active, :none, 2011, 10
+ deprecate :find_all, :none, 2011, 10
+ deprecate :find_in_unresolved, :none, 2011, 10
+ deprecate :find_in_unresolved_tree, :none, 2011, 10
+ deprecate :find_spec_for_file, :none, 2011, 10
+end
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index 1c432ba494..6b5aef3fa7 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -10,6 +10,7 @@
# See LICENSE.txt for permissions.
#++
+require "rubygems"
require 'rubygems/command_manager'
require 'rubygems/config_file'
require 'rubygems/doc_manager'
@@ -31,6 +32,7 @@ Gem.load_env_plugins rescue nil
class Gem::GemRunner
def initialize(options={})
+ # TODO: nuke these options
@command_manager_class = options[:command_manager] || Gem::CommandManager
@config_file_class = options[:config_file] || Gem::ConfigFile
@doc_manager_class = options[:doc_manager] || Gem::DocManager
@@ -80,7 +82,7 @@ class Gem::GemRunner
def do_configuration(args)
Gem.configuration = @config_file_class.new(args)
- Gem.use_paths(Gem.configuration[:gemhome], Gem.configuration[:gempath])
+ Gem.use_paths Gem.configuration[:gemhome], Gem.configuration[:gempath]
Gem::Command.extra_args = Gem.configuration[:gem]
@doc_manager_class.configured_args = Gem.configuration[:rdoc]
end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 6e481c6790..c367532c43 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -79,7 +79,7 @@ class Gem::Indexer
@rss_gems_host = options[:rss_gems_host]
@dest_directory = directory
- @directory = File.join Dir.tmpdir, "gem_generate_index_#{$$}"
+ @directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}")
marshal_name = "Marshal.#{Gem.marshal_version}"
@@ -87,24 +87,23 @@ class Gem::Indexer
@marshal_index = File.join @directory, marshal_name
@quick_dir = File.join @directory, 'quick'
-
@quick_marshal_dir = File.join @quick_dir, marshal_name
+ @quick_marshal_dir_base = File.join "quick", marshal_name # FIX: UGH
@quick_index = File.join @quick_dir, 'index'
@latest_index = File.join @quick_dir, 'latest_index'
@specs_index = File.join @directory, "specs.#{Gem.marshal_version}"
- @latest_specs_index = File.join @directory,
- "latest_specs.#{Gem.marshal_version}"
- @prerelease_specs_index = File.join(@directory,
- "prerelease_specs.#{Gem.marshal_version}")
-
- @dest_specs_index = File.join @dest_directory,
- "specs.#{Gem.marshal_version}"
- @dest_latest_specs_index = File.join @dest_directory,
- "latest_specs.#{Gem.marshal_version}"
- @dest_prerelease_specs_index = File.join @dest_directory,
- "prerelease_specs.#{Gem.marshal_version}"
+ @latest_specs_index =
+ File.join(@directory, "latest_specs.#{Gem.marshal_version}")
+ @prerelease_specs_index =
+ File.join(@directory, "prerelease_specs.#{Gem.marshal_version}")
+ @dest_specs_index =
+ File.join(@dest_directory, "specs.#{Gem.marshal_version}")
+ @dest_latest_specs_index =
+ File.join(@dest_directory, "latest_specs.#{Gem.marshal_version}")
+ @dest_prerelease_specs_index =
+ File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}")
@rss_index = File.join @directory, 'index.rss'
@@ -129,12 +128,16 @@ class Gem::Indexer
##
# Build various indicies
- def build_indicies(index)
+ def build_indicies
# Marshal gemspecs are used by both modern and legacy RubyGems
- build_marshal_gemspecs index
- build_legacy_indicies index if @build_legacy
- build_modern_indicies index if @build_modern
- build_rss index
+
+ Gem::Specification.dirs = []
+ Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list))
+
+ build_marshal_gemspecs
+ build_legacy_indicies if @build_legacy
+ build_modern_indicies if @build_modern
+ build_rss
compress_indicies
end
@@ -142,7 +145,9 @@ class Gem::Indexer
##
# Builds indicies for RubyGems older than 1.2.x
- def build_legacy_indicies(index)
+ def build_legacy_indicies
+ index = collect_specs
+
say "Generating Marshal master index"
Gem.time 'Generated Marshal master index' do
@@ -158,16 +163,17 @@ class Gem::Indexer
##
# Builds Marshal quick index gemspecs.
- def build_marshal_gemspecs(index)
- progress = ui.progress_reporter index.size,
- "Generating Marshal quick index gemspecs for #{index.size} gems",
+ def build_marshal_gemspecs
+ count = Gem::Specification.count
+ progress = ui.progress_reporter count,
+ "Generating Marshal quick index gemspecs for #{count} gems",
"Complete"
files = []
Gem.time 'Generated Marshal quick index gemspecs' do
- index.gems.each do |original_name, spec|
- spec_file_name = "#{original_name}.gemspec.rz"
+ Gem::Specification.each do |spec|
+ spec_file_name = "#{spec.original_name}.gemspec.rz"
marshal_name = File.join @quick_marshal_dir, spec_file_name
marshal_zipped = Gem.deflate Marshal.dump(spec)
@@ -175,7 +181,7 @@ class Gem::Indexer
files << marshal_name
- progress.updated original_name
+ progress.updated spec.original_name
end
progress.done
@@ -195,8 +201,8 @@ class Gem::Indexer
Gem.time "Generated #{name} index" do
open(file, 'wb') do |io|
specs = index.map do |*spec|
- # We have to splat here because latest_specs is an array,
- # while the others are hashes. See the TODO in source_index.rb
+ # We have to splat here because latest_specs is an array, while the
+ # others are hashes.
spec = spec.flatten.last
platform = spec.original_platform
@@ -219,13 +225,15 @@ class Gem::Indexer
##
# Builds indicies for RubyGems 1.2 and newer. Handles full, latest, prerelease
- def build_modern_indicies(index)
- build_modern_index(index.released_specs.sort, @specs_index, 'specs')
- build_modern_index(index.latest_specs.sort,
- @latest_specs_index,
- 'latest specs')
- build_modern_index(index.prerelease_specs.sort,
- @prerelease_specs_index,
+ def build_modern_indicies
+ prerelease, released = Gem::Specification.partition { |s|
+ s.version.prerelease?
+ }
+ latest_specs = Gem::Specification.latest_specs
+
+ build_modern_index(released.sort, @specs_index, 'specs')
+ build_modern_index(latest_specs.sort, @latest_specs_index, 'latest specs')
+ build_modern_index(prerelease.sort, @prerelease_specs_index,
'prerelease specs')
@files += [@specs_index,
@@ -240,7 +248,7 @@ class Gem::Indexer
# Builds an RSS feed for past two days gem releases according to the gem's
# date.
- def build_rss(index)
+ def build_rss
if @rss_host.nil? or @rss_gems_host.nil? then
if Gem.configuration.really_verbose then
alert_warning "no --rss-host or --rss-gems-host, RSS generation disabled"
@@ -272,31 +280,18 @@ class Gem::Indexer
today = Gem::Specification::TODAY
yesterday = today - 86400
- index = index.select do |_, spec|
- spec_date = spec.date
-
- case spec_date
- when Date
- Time.parse(spec_date.to_s) >= yesterday
- when Time
- spec_date >= yesterday
- end
- end
-
- index = index.select do |_, spec|
+ index = Gem::Specification.select do |spec|
spec_date = spec.date
+ # TODO: remove this and make YAML based specs properly normalized
+ spec_date = Time.parse(spec_date.to_s) if Date === spec_date
- case spec_date
- when Date
- Time.parse(spec_date.to_s) <= today
- when Time
- spec_date <= today
- end
+ spec_date >= yesterday && spec_date <= today
end
- index.sort_by { |_, spec| [-spec.date.to_i, spec] }.each do |_, spec|
- gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{spec.file_name}"
- size = File.stat(spec.loaded_from).size rescue next
+ index.sort_by { |spec| [-spec.date.to_i, spec] }.each do |spec|
+ file_name = File.basename spec.cache_file
+ gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{file_name}"
+ size = File.stat(spec.loaded_from).size # rescue next
description = spec.description || spec.summary || ''
authors = Array spec.authors
@@ -347,54 +342,56 @@ class Gem::Indexer
@files << @rss_index
end
+ def map_gems_to_specs gems
+ gems.map { |gemfile|
+ if File.size(gemfile) == 0 then
+ alert_warning "Skipping zero-length gem: #{gemfile}"
+ next
+ end
+
+ begin
+ spec = Gem::Format.from_file_by_path(gemfile).spec
+ spec.loaded_from = gemfile
+
+ # HACK: fuck this shit - borks all tests that use pl1
+ # if File.basename(gemfile, ".gem") != spec.original_name then
+ # exp = spec.full_name
+ # exp << " (#{spec.original_name})" if
+ # spec.original_name != spec.full_name
+ # msg = "Skipping misnamed gem: #{gemfile} should be named #{exp}"
+ # alert_warning msg
+ # next
+ # end
+
+ abbreviate spec
+ sanitize spec
+
+ spec
+ rescue SignalException => e
+ alert_error "Received signal, exiting"
+ raise
+ rescue Exception => e
+ msg = ["Unable to process #{gemfile}",
+ "#{e.message} (#{e.class})",
+ "\t#{e.backtrace.join "\n\t"}"].join("\n")
+ alert_error msg
+ end
+ }.compact
+ end
+
##
# Collect specifications from .gem files from the gem directory.
def collect_specs(gems = gem_file_list)
- index = Gem::SourceIndex.new
-
- progress = ui.progress_reporter gems.size,
- "Loading #{gems.size} gems from #{@dest_directory}",
- "Loaded all gems"
+ Deprecate.skip_during do
+ index = Gem::SourceIndex.new
- Gem.time 'loaded' do
- gems.each do |gemfile|
- if File.size(gemfile.to_s) == 0 then
- alert_warning "Skipping zero-length gem: #{gemfile}"
- next
- end
-
- begin
- spec = Gem::Format.from_file_by_path(gemfile).spec
- spec.loaded_from = gemfile
-
- unless gemfile =~ /\/#{Regexp.escape spec.original_name}.*\.gem\z/i then
- expected_name = spec.full_name
- expected_name << " (#{spec.original_name})" if
- spec.original_name != spec.full_name
- alert_warning "Skipping misnamed gem: #{gemfile} should be named #{expected_name}"
- next
- end
-
- abbreviate spec
- sanitize spec
-
- index.add_spec spec, spec.original_name
-
- progress.updated spec.original_name
-
- rescue SignalException => e
- alert_error "Received signal, exiting"
- raise
- rescue Exception => e
- alert_error "Unable to process #{gemfile}\n#{e.message} (#{e.class})\n\t#{e.backtrace.join "\n\t"}"
- end
+ map_gems_to_specs(gems).each do |spec|
+ index.add_spec spec, spec.original_name
end
- progress.done
+ index
end
-
- index
end
##
@@ -454,7 +451,7 @@ class Gem::Indexer
# List of gem file names to index.
def gem_file_list
- Dir.glob(File.join(@dest_directory, "gems", "*.gem"))
+ Dir[File.join(@dest_directory, "gems", '*.gem')]
end
##
@@ -462,8 +459,7 @@ class Gem::Indexer
def generate_index
make_temp_directories
- index = collect_specs
- build_indicies index
+ build_indicies
install_indicies
rescue SignalException
ensure
@@ -487,24 +483,22 @@ class Gem::Indexer
say "Moving index into production dir #{@dest_directory}" if verbose
- files = @files.dup
+ files = @files
files.delete @quick_marshal_dir if files.include? @quick_dir
- if files.include? @quick_marshal_dir and
- not files.include? @quick_dir then
+ if files.include? @quick_marshal_dir and not files.include? @quick_dir then
files.delete @quick_marshal_dir
- quick_marshal_dir = @quick_marshal_dir.sub @directory, ''
- dst_name = File.join @dest_directory, quick_marshal_dir
+ dst_name = File.join(@dest_directory, @quick_marshal_dir_base)
FileUtils.mkdir_p File.dirname(dst_name), :verbose => verbose
FileUtils.rm_rf dst_name, :verbose => verbose
- FileUtils.mv @quick_marshal_dir, dst_name, :verbose => verbose,
- :force => true
+ FileUtils.mv(@quick_marshal_dir, dst_name,
+ :verbose => verbose, :force => true)
end
files = files.map do |path|
- path.sub @directory, ''
+ path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK?
end
files.each do |file|
@@ -512,8 +506,8 @@ class Gem::Indexer
dst_name = File.join @dest_directory, file
FileUtils.rm_rf dst_name, :verbose => verbose
- FileUtils.mv src_name, @dest_directory, :verbose => verbose,
- :force => true
+ FileUtils.mv(src_name, @dest_directory,
+ :verbose => verbose, :force => true)
end
end
@@ -544,10 +538,10 @@ class Gem::Indexer
# be replaced by their XML entity equivalent.
def sanitize(spec)
- spec.summary = sanitize_string(spec.summary)
- spec.description = sanitize_string(spec.description)
+ spec.summary = sanitize_string(spec.summary)
+ spec.description = sanitize_string(spec.description)
spec.post_install_message = sanitize_string(spec.post_install_message)
- spec.authors = spec.authors.collect { |a| sanitize_string(a) }
+ spec.authors = spec.authors.collect { |a| sanitize_string(a) }
spec
end
@@ -593,14 +587,16 @@ class Gem::Indexer
terminate_interaction 0
end
- index = collect_specs updated_gems
+ specs = map_gems_to_specs updated_gems
+ prerelease, released = specs.partition { |s| s.version.prerelease? }
- files = build_marshal_gemspecs index
+ files = build_marshal_gemspecs
Gem.time 'Updated indexes' do
- update_specs_index index.released_gems, @dest_specs_index, @specs_index
- update_specs_index index.released_gems, @dest_latest_specs_index, @latest_specs_index
- update_specs_index(index.prerelease_gems, @dest_prerelease_specs_index,
+ update_specs_index released, @dest_specs_index, @specs_index
+ update_specs_index released, @dest_latest_specs_index, @latest_specs_index
+ update_specs_index(prerelease,
+ @dest_prerelease_specs_index,
@prerelease_specs_index)
end
@@ -618,12 +614,12 @@ class Gem::Indexer
files << "#{@prerelease_specs_index}.gz"
files = files.map do |path|
- path.sub @directory, ''
+ path.sub(/^#{Regexp.escape @directory}\/?/, '') # HACK?
end
files.each do |file|
src_name = File.join @directory, file
- dst_name = File.join @dest_directory, File.dirname(file)
+ dst_name = File.join @dest_directory, file # REFACTOR: duped above
FileUtils.mv src_name, dst_name, :verbose => verbose,
:force => true
@@ -639,7 +635,7 @@ class Gem::Indexer
def update_specs_index(index, source, dest)
specs_index = Marshal.load Gem.read_binary(source)
- index.each do |_, spec|
+ index.each do |spec|
platform = spec.original_platform
platform = Gem::Platform::RUBY if platform.nil? or platform.empty?
specs_index << [spec.name, spec.version, platform]
@@ -651,6 +647,4 @@ class Gem::Indexer
Marshal.dump specs_index, io
end
end
-
end
-
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 8c6aeb44c7..7ef96ddb0d 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -47,6 +47,8 @@ class Gem::Installer
include Gem::RequirePathsBuilder if Gem::QUICKLOADER_SUCKAGE
+ attr_reader :gem
+
##
# The directory a gem's executables will be installed into
@@ -58,11 +60,6 @@ class Gem::Installer
attr_reader :gem_home
##
- # The Gem::Specification for the gem being installed
-
- attr_reader :spec
-
- ##
# The options passed when the Gem::Installer was instantiated.
attr_reader :options
@@ -106,18 +103,36 @@ class Gem::Installer
@gem = gem
@options = options
process_options
- load_gem_file
if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir
check_that_user_bin_dir_is_in_path
end
+ end
- verify_gem_home(options[:unpack])
+ ##
+ # Lazy accessor for the spec's gem directory.
+
+ def gem_dir
+ @gem_dir ||= spec.gem_dir.dup.untaint
+ end
+
+ ##
+ # Lazy accessor for the installer's Gem::Format instance.
+
+ def format
+ begin
+ @format ||= Gem::Format.from_file_by_path gem, @security_policy
+ rescue Gem::Package::FormatError
+ raise Gem::InstallError, "invalid gem format for #{gem}"
+ end
+ end
- @spec = @format.spec
+ ##
+ # Lazy accessor for the installer's spec.
- @gem_dir = File.join(@gem_home, "gems", @spec.full_name).untaint
+ def spec
+ @spec ||= format.spec
end
##
@@ -132,6 +147,12 @@ class Gem::Installer
# specifications/<gem-version>.gemspec #=> the Gem::Specification
def install
+ current_home = Gem.dir
+ current_path = Gem.paths.path
+
+ verify_gem_home(options[:unpack])
+ Gem.use_paths gem_home, current_path # HACK: shouldn't need Gem.paths.path
+
# If we're forcing the install then disable security unless the security
# policy says that we only install signed gems.
@security_policy = nil if @force and @security_policy and
@@ -149,17 +170,17 @@ class Gem::Installer
if result == false then
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
- message = "pre-install hook#{location} failed for #{@spec.full_name}"
+ message = "pre-install hook#{location} failed for #{spec.full_name}"
raise Gem::InstallError, message
end
end
- Gem.ensure_gem_subdirectories @gem_home
+ Gem.ensure_gem_subdirectories gem_home
# Completely remove any previous gem files
- FileUtils.rm_rf(@gem_dir) if File.exist?(@gem_dir)
+ FileUtils.rm_rf(gem_dir) if File.exist? gem_dir
- FileUtils.mkdir_p @gem_dir
+ FileUtils.mkdir_p gem_dir
extract_files
build_extensions
@@ -168,11 +189,11 @@ class Gem::Installer
result = hook.call self
if result == false then
- FileUtils.rm_rf @gem_dir
+ FileUtils.rm_rf gem_dir
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
- message = "post-build hook#{location} failed for #{@spec.full_name}"
+ message = "post-build hook#{location} failed for #{spec.full_name}"
raise Gem::InstallError, message
end
end
@@ -182,24 +203,27 @@ class Gem::Installer
write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE
- cached_gem = Gem.cache_gem(File.basename(@gem), @gem_home)
- unless File.exist? cached_gem then
- FileUtils.cp @gem, Gem.cache_dir(@gem_home)
- end
+ cache_file = spec.cache_file
+ FileUtils.cp gem, cache_file unless File.exist? cache_file
- say @spec.post_install_message unless @spec.post_install_message.nil?
+ say spec.post_install_message unless spec.post_install_message.nil?
- @spec.loaded_from = File.join(@gem_home, 'specifications', @spec.spec_name)
+ spec.loaded_from = spec.spec_file
- @source_index.add_spec @spec
+ Gem::Specification.add_spec spec unless Gem::Specification.include? spec
Gem.post_install_hooks.each do |hook|
hook.call self
end
- return @spec
+ return spec
rescue Zlib::GzipFile::Error
- raise Gem::InstallError, "gzip error installing #{@gem}"
+ raise Gem::InstallError, "gzip error installing #{gem}"
+ ensure
+ # conditional since we might be here because we're erroring out early.
+ if current_path
+ Gem.use_paths current_home, current_path
+ end
end
##
@@ -220,7 +244,7 @@ class Gem::Installer
# True if the gems in the source_index satisfy +dependency+.
def installation_satisfies_dependency?(dependency)
- @source_index.find_name(dependency.name, dependency.requirement).size > 0
+ not dependency.matching_specs.empty?
end
##
@@ -228,7 +252,7 @@ class Gem::Installer
def unpack(directory)
@gem_dir = directory
- @format = Gem::Format.from_file_by_path @gem, @security_policy
+ @format = Gem::Format.from_file_by_path gem, @security_policy
extract_files
end
@@ -237,14 +261,10 @@ class Gem::Installer
# specifications directory.
def write_spec
- rubycode = @spec.to_ruby_for_cache
-
- file_name = File.join @gem_home, 'specifications', @spec.spec_name
-
- file_name.untaint
+ file_name = spec.spec_file.untaint
File.open(file_name, "w") do |file|
- file.puts rubycode
+ file.puts spec.to_ruby_for_cache
end
end
@@ -264,24 +284,28 @@ class Gem::Installer
end
def generate_bin
- return if @spec.executables.nil? or @spec.executables.empty?
+ return if spec.executables.nil? or spec.executables.empty?
# If the user has asked for the gem to be installed in a directory that is
# the system gem directory, then use the system bin directory, else create
# (or use) a new bin dir under the gem_home.
- bindir = @bin_dir ? @bin_dir : Gem.bindir(@gem_home)
+ bindir = @bin_dir || Gem.bindir(gem_home)
Dir.mkdir bindir unless File.exist? bindir
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
- @spec.executables.each do |filename|
+ spec.executables.each do |filename|
filename.untaint
- bin_path = File.expand_path "#{@spec.bindir}/#{filename}", @gem_dir
- if File.exist?(bin_path)
- mode = File.stat(bin_path).mode | 0111
- File.chmod mode, bin_path
+ bin_path = File.expand_path File.join(gem_dir, spec.bindir, filename)
+
+ unless File.exist? bin_path
+ warn "Hey?!?! Where did #{bin_path} go??"
+ next
end
+ mode = File.stat(bin_path).mode | 0111
+ FileUtils.chmod mode, bin_path
+
if @wrappers then
generate_bin_script filename, bindir
else
@@ -322,14 +346,14 @@ class Gem::Installer
return
end
- src = File.join @gem_dir, 'bin', filename
+ src = File.join gem_dir, spec.bindir, filename
dst = File.join bindir, formatted_program_filename(filename)
if File.exist? dst then
if File.symlink? dst then
link = File.readlink(dst).split File::SEPARATOR
cur_version = Gem::Version.create(link[-3].sub(/^.*-/, ''))
- return if @spec.version < cur_version
+ return if spec.version < cur_version
end
File.unlink dst
end
@@ -343,7 +367,7 @@ class Gem::Installer
def shebang(bin_file_name)
ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang
- path = File.join @gem_dir, @spec.bindir, bin_file_name
+ path = spec.bin_file bin_file_name
first_line = File.open(path, "rb") {|file| file.gets}
if /\A#!/ =~ first_line then
@@ -365,29 +389,29 @@ class Gem::Installer
end
def ensure_required_ruby_version_met
- if rrv = @spec.required_ruby_version then
+ if rrv = spec.required_ruby_version then
unless rrv.satisfied_by? Gem.ruby_version then
- raise Gem::InstallError, "#{@spec.name} requires Ruby version #{rrv}."
+ raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
end
end
end
def ensure_required_rubygems_version_met
- if rrgv = @spec.required_rubygems_version then
+ if rrgv = spec.required_rubygems_version then
unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then
raise Gem::InstallError,
- "#{@spec.name} requires RubyGems version #{rrgv}. " +
+ "#{spec.name} requires RubyGems version #{rrgv}. " +
"Try 'gem update --system' to update RubyGems itself."
end
end
end
def ensure_dependencies_met
- deps = @spec.runtime_dependencies
- deps |= @spec.development_dependencies if @development
+ deps = spec.runtime_dependencies
+ deps |= spec.development_dependencies if @development
deps.each do |dep_gem|
- ensure_dependency @spec, dep_gem
+ ensure_dependency spec, dep_gem
end
end
@@ -398,32 +422,24 @@ class Gem::Installer
:exec_format => false,
:force => false,
:install_dir => Gem.dir,
- :source_index => Gem.source_index,
}.merge options
@env_shebang = options[:env_shebang]
@force = options[:force]
- gem_home = options[:install_dir]
- @gem_home = File.expand_path(gem_home)
+ @gem_home = options[:install_dir]
@ignore_dependencies = options[:ignore_dependencies]
@format_executable = options[:format_executable]
@security_policy = options[:security_policy]
@wrappers = options[:wrappers]
@bin_dir = options[:bin_dir]
@development = options[:development]
- @source_index = options[:source_index]
- end
- def load_gem_file
- begin
- @format = Gem::Format.from_file_by_path @gem, @security_policy
- rescue Gem::Package::FormatError
- raise Gem::InstallError, "invalid gem format for #{@gem}"
- end
+ raise "NOTE: Installer option :source_index is dead" if
+ options[:source_index]
end
def check_that_user_bin_dir_is_in_path
- user_bin_dir = File.join(@gem_home, 'bin')
+ user_bin_dir = File.join gem_home, "bin"
unless ENV['PATH'].split(File::PATH_SEPARATOR).include? user_bin_dir then
unless self.class.path_warning then
alert_warning "You don't have #{user_bin_dir} in your PATH,\n\t gem executables will not run."
@@ -433,21 +449,21 @@ class Gem::Installer
end
def verify_gem_home(unpack = false)
- FileUtils.mkdir_p @gem_home
- raise Gem::FilePermissionError, @gem_home unless
- unpack or File.writable? @gem_home
+ FileUtils.mkdir_p gem_home
+ raise Gem::FilePermissionError, gem_home unless
+ unpack or File.writable?(gem_home)
end
##
# Return the text for an application file.
def app_script_text(bin_file_name)
- <<-TEXT
+ return <<-TEXT
#{shebang bin_file_name}
#
# This file was generated by RubyGems.
#
-# The application '#{@spec.name}' is installed as part of a gem, and
+# The application '#{spec.name}' is installed as part of a gem, and
# this file is here to facilitate running it.
#
@@ -460,8 +476,8 @@ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
ARGV.shift
end
-gem '#{@spec.name}', version
-load Gem.bin_path('#{@spec.name}', '#{bin_file_name}', version)
+gem '#{spec.name}', version
+load Gem.bin_path('#{spec.name}', '#{bin_file_name}', version)
TEXT
end
@@ -469,14 +485,16 @@ TEXT
# return the stub script text used to launch the true ruby script
def windows_stub_script(bindir, bin_file_name)
- <<-TEXT
+ ruby = File.basename(Gem.ruby).chomp('"')
+ return <<-TEXT
@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{File.basename(Gem.ruby).chomp('"')}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
-@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
+@"#{ruby}" "%~dpn0" %*
TEXT
+
end
##
@@ -484,12 +502,12 @@ TEXT
# configure scripts and rakefiles or mkrf_conf files.
def build_extensions
- return if @spec.extensions.empty?
+ return if spec.extensions.empty?
say "Building native extensions. This could take a while..."
- dest_path = File.join @gem_dir, @spec.require_paths.first
+ dest_path = File.join gem_dir, spec.require_paths.first
ran_rake = false # only run rake once
- @spec.extensions.each do |extension|
+ spec.extensions.each do |extension|
break if ran_rake
results = []
@@ -508,15 +526,15 @@ TEXT
extension_dir = begin
- File.join @gem_dir, File.dirname(extension)
+ File.join gem_dir, File.dirname(extension)
rescue TypeError # extension == nil
- @gem_dir
+ gem_dir
end
begin
Dir.chdir extension_dir do
- results = builder.build(extension, @gem_dir, dest_path, results)
+ results = builder.build(extension, gem_dir, dest_path, results)
say results.join("\n") if Gem.configuration.really_verbose
end
@@ -532,7 +550,7 @@ ERROR: Failed to build gem native extension.
#{results}
-Gem files will remain installed in #{@gem_dir} for inspection.
+Gem files will remain installed in #{gem_dir} for inspection.
Results logged to #{gem_make_out}
EOF
@@ -547,35 +565,27 @@ EOF
# Ensures that files can't be installed outside the gem directory.
def extract_files
- @gem_dir = File.expand_path @gem_dir
-
raise ArgumentError, "format required to extract from" if @format.nil?
- dirs = []
-
@format.file_entries.each do |entry, file_data|
path = entry['path'].untaint
- if path =~ /\A\// then # for extra sanity
- raise Gem::InstallError,
- "attempt to install file into #{entry['path'].inspect}"
+ if path.start_with? "/" then # for extra sanity
+ raise Gem::InstallError, "attempt to install file into #{entry['path']}"
end
- path = File.expand_path File.join(@gem_dir, path)
+ path = File.expand_path File.join(gem_dir, path)
- if path !~ /\A#{Regexp.escape @gem_dir}/ then
- msg = "attempt to install file into %p under %p" %
- [entry['path'], @gem_dir]
+ unless path.start_with? gem_dir then
+ msg = "attempt to install file into %p under %s" %
+ [entry['path'], gem_dir]
raise Gem::InstallError, msg
end
- FileUtils.rm_rf(path) if File.exists?(path)
+ FileUtils.rm_rf(path) if File.exist? path
- dir = File.dirname(path)
- if !dirs.include?(dir)
- dirs << dir
- FileUtils.mkdir_p dir
- end
+ dir = File.dirname path
+ FileUtils.mkdir_p dir unless File.exist? dir
File.open(path, "wb") do |out|
out.write file_data
@@ -598,5 +608,14 @@ EOF
end
end
+ ##
+ #
+ # Return the target directory where the gem is to be installed. This
+ # directory is not guaranteed to be populated.
+ #
+
+ def dir
+ gem_dir.to_s
+ end
end
diff --git a/lib/rubygems/installer_test_case.rb b/lib/rubygems/installer_test_case.rb
index 26b590786b..a548bd55f8 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/lib/rubygems/installer_test_case.rb
@@ -12,7 +12,7 @@ class Gem::Installer
##
# Available through requiring rubygems/installer_test_case
- attr_accessor :gem_dir
+ attr_writer :gem_dir
##
# Available through requiring rubygems/installer_test_case
@@ -63,29 +63,38 @@ class Gem::InstallerTestCase < Gem::TestCase
def setup
super
- @spec = quick_gem 'a'
- util_make_exec @spec
+ @installer_tmp = File.join @tempdir, 'installer'
+ FileUtils.mkdir_p @installer_tmp
- @gem = File.join @tempdir, @spec.file_name
+ Gem.use_paths @installer_tmp
+ Gem.ensure_gem_subdirectories @installer_tmp
- @installer = util_installer @spec, @gem, @gemhome
+ @spec = quick_gem 'a'
+ util_make_exec @spec
+ util_build_gem @spec
+ @gem = @spec.cache_file
@user_spec = quick_gem 'b'
util_make_exec @user_spec
+ util_build_gem @user_spec
+ @user_gem = @user_spec.cache_file
+
+ Gem.use_paths @gemhome
- @user_gem = File.join @tempdir, @user_spec.file_name
+ @installer = util_installer @spec, @gemhome
+ @user_installer = util_installer @user_spec, Gem.user_dir, :user
- @user_installer = util_installer @user_spec, @user_gem, Gem.user_dir
- @user_installer.gem_dir = File.join(Gem.user_dir, 'gems',
- @user_spec.full_name)
+ Gem.use_paths @gemhome
end
def util_gem_bindir spec = @spec
- File.join util_gem_dir(spec), "bin"
+ # TODO: deprecate
+ spec.bin_dir
end
def util_gem_dir spec = @spec
- File.join @gemhome, "gems", spec.full_name
+ # TODO: deprecate
+ spec.gem_dir
end
def util_inst_bindir
@@ -96,16 +105,13 @@ class Gem::InstallerTestCase < Gem::TestCase
spec.executables = %w[executable]
spec.files << 'bin/executable'
- bindir = util_gem_bindir spec
- FileUtils.mkdir_p bindir
- exec_path = File.join bindir, 'executable'
- open exec_path, 'w' do |io|
+ exec_path = spec.bin_file "executable"
+ write_file exec_path do |io|
io.puts shebang
end
- temp_bin = File.join(@tempdir, 'bin')
- FileUtils.mkdir_p temp_bin
- open File.join(temp_bin, 'executable'), 'w' do |io|
+ bin_path = File.join @tempdir, "bin", "executable"
+ write_file bin_path do |io|
io.puts shebang
end
end
@@ -128,23 +134,15 @@ class Gem::InstallerTestCase < Gem::TestCase
use_ui ui do
FileUtils.rm @gem
- Gem::Builder.new(@spec).build
+
+ @gem = Gem::Builder.new(@spec).build
end
end
@installer = Gem::Installer.new @gem
end
- def util_installer(spec, gem_path, gem_home)
- util_build_gem spec
- FileUtils.mv Gem.cache_gem(spec.file_name), @tempdir
- installer = Gem::Installer.new gem_path
- installer.gem_dir = util_gem_dir
- installer.gem_home = gem_home
- installer.spec = spec
-
- installer
+ def util_installer(spec, gem_home, user=false)
+ Gem::Installer.new spec.cache_file, :user_install => user
end
-
end
-
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index b752423845..77a8006f18 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -82,7 +82,7 @@ module Gem::LocalRemoteOptions
add_option(:"Local/Remote", '--clear-sources',
'Clear the gem sources') do |value, options|
- Gem.sources.clear
+ Gem.sources = nil
options[:sources_cleared] = true
end
end
@@ -123,7 +123,7 @@ module Gem::LocalRemoteOptions
# Add the --update-sources option
def add_update_sources_option
- add_option(:"Local/Remote", '-u', '--[no-]update-sources',
+ add_option(:Deprecated, '-u', '--[no-]update-sources',
'Update local source cache') do |value, options|
Gem.configuration.update_sources = value
end
diff --git a/lib/rubygems/mock_gem_ui.rb b/lib/rubygems/mock_gem_ui.rb
index 4450cc97a6..88c36627c7 100644
--- a/lib/rubygems/mock_gem_ui.rb
+++ b/lib/rubygems/mock_gem_ui.rb
@@ -12,7 +12,15 @@ require 'rubygems/user_interaction'
# retrieval during tests.
class Gem::MockGemUi < Gem::StreamUI
- class TermError < RuntimeError; end
+ class TermError < RuntimeError
+ attr_reader :exit_code
+
+ def initialize exit_code
+ super
+ @exit_code = exit_code
+ end
+ end
+ class SystemExitException < RuntimeError; end
module TTY
@@ -61,8 +69,8 @@ class Gem::MockGemUi < Gem::StreamUI
def terminate_interaction(status=0)
@terminated = true
- raise TermError unless status == 0
- raise Gem::SystemExitException, status
+ raise TermError, status if status != 0
+ raise SystemExitException
end
end
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 3a50ca118e..2e108bf550 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -12,32 +12,6 @@
require 'rubygems/specification'
-##
-# Wrapper for FileUtils meant to provide logging and additional operations if
-# needed.
-
-class Gem::FileOperations
-
- def initialize(logger = nil)
- require 'fileutils'
- @logger = logger
- end
-
- def method_missing(meth, *args, &block)
- case
- when FileUtils.respond_to?(meth)
- @logger.log "#{meth}: #{args}" if @logger
- FileUtils.send meth, *args, &block
- when Gem::FileOperations.respond_to?(meth)
- @logger.log "#{meth}: #{args}" if @logger
- Gem::FileOperations.send meth, *args, &block
- else
- super
- end
- end
-
-end
-
module Gem::Package
class Error < StandardError; end
@@ -63,6 +37,8 @@ module Gem::Package
class TarInvalidError < Error; end
+ # FIX: zenspider said: does it really take an IO?
+ # passed to a method called open?!? that seems stupid.
def self.open(io, mode = "r", signer = nil, &block)
tar_type = case mode
when 'r' then TarInput
diff --git a/lib/rubygems/package/tar_input.rb b/lib/rubygems/package/tar_input.rb
index 401df80a83..92c190c56a 100644
--- a/lib/rubygems/package/tar_input.rb
+++ b/lib/rubygems/package/tar_input.rb
@@ -55,6 +55,7 @@ class Gem::Package::TarInput
sio.rewind
end
+ # TODO use Gem.gunzip
gzis = Zlib::GzipReader.new(sio || entry)
# YAML wants an instance of IO
@metadata = load_gemspec(gzis)
@@ -115,7 +116,6 @@ class Gem::Package::TarInput
end
@tarreader.rewind
- @fileops = Gem::FileOperations.new
unless has_meta then
path = io.path if io.respond_to? :path
@@ -151,9 +151,9 @@ class Gem::Package::TarInput
dest = File.join destdir, entry.full_name
if File.directory? dest then
- @fileops.chmod entry.header.mode, dest, :verbose => false
+ FileUtils.chmod entry.header.mode, dest, :verbose => false
else
- @fileops.mkdir_p dest, :mode => entry.header.mode, :verbose => false
+ FileUtils.mkdir_p dest, :mode => entry.header.mode, :verbose => false
end
fsync_dir dest
@@ -165,9 +165,9 @@ class Gem::Package::TarInput
# it's a file
md5 = Digest::MD5.new if expected_md5sum
destdir = File.join destdir, File.dirname(entry.full_name)
- @fileops.mkdir_p destdir, :mode => 0755, :verbose => false
+ FileUtils.mkdir_p destdir, :mode => 0755, :verbose => false
destfile = File.join destdir, File.basename(entry.full_name)
- @fileops.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
+ FileUtils.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
open destfile, "wb", entry.header.mode do |os|
loop do
@@ -181,7 +181,7 @@ class Gem::Package::TarInput
os.fsync
end
- @fileops.chmod entry.header.mode, destfile, :verbose => false
+ FileUtils.chmod entry.header.mode, destfile, :verbose => false
fsync_dir File.dirname(destfile)
fsync_dir File.join(File.dirname(destfile), "..")
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index 823f20c88c..aeb11ad6ce 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -236,7 +236,7 @@ class Gem::Package::TarWriter
name = newname
if name.size > 100 or prefix.size > 155 then
- raise Gem::Package::TooLongFileName
+ raise Gem::Package::TooLongFileName
end
end
diff --git a/lib/rubygems/package_task.rb b/lib/rubygems/package_task.rb
index ae4f2019ae..5b6b381121 100644
--- a/lib/rubygems/package_task.rb
+++ b/lib/rubygems/package_task.rb
@@ -106,7 +106,7 @@ class Gem::PackageTask < Rake::PackageTask
task :package => [:gem]
- gem_file = gem_spec.file_name
+ gem_file = File.basename gem_spec.cache_file
gem_path = File.join package_dir, gem_file
gem_dir = File.join package_dir, gem_spec.full_name
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
new file mode 100644
index 0000000000..0809f8ab40
--- /dev/null
+++ b/lib/rubygems/path_support.rb
@@ -0,0 +1,78 @@
+######################################################################
+# This file is imported from the rubygems project.
+# DO NOT make modifications in this repo. They _will_ be reverted!
+# File a patch instead and assign it to Ryan Davis or Eric Hodel.
+######################################################################
+
+##
+#
+# Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings
+# to the rest of RubyGems.
+#
+class Gem::PathSupport
+ ##
+ # The default system path for managing Gems.
+ attr_reader :home
+
+ ##
+ # Array of paths to search for Gems.
+ attr_reader :path
+
+ ##
+ #
+ # Constructor. Takes a single argument which is to be treated like a
+ # hashtable, or defaults to ENV, the system environment.
+ #
+ def initialize(env=ENV)
+ @env = env
+
+ # note 'env' vs 'ENV'...
+ @home = env["GEM_HOME"] || ENV["GEM_HOME"] || Gem.default_dir
+ self.path = env["GEM_PATH"] || ENV["GEM_PATH"]
+ end
+
+ private
+
+ ##
+ # Set the Gem home directory (as reported by Gem.dir).
+
+ def home=(home)
+ @home = home.to_s
+ end
+
+ ##
+ # Set the Gem search path (as reported by Gem.path).
+
+ def path=(gpaths)
+ # FIX: it should be [home, *path], not [*path, home]
+
+ gem_path = []
+
+ # FIX: I can't tell wtf this is doing.
+ gpaths ||= (ENV['GEM_PATH'] || "").empty? ? nil : ENV["GEM_PATH"]
+
+ if gpaths
+ if gpaths.kind_of?(Array)
+ gem_path = gpaths.dup
+ else
+ gem_path = gpaths.split(File::PATH_SEPARATOR)
+ end
+
+ if File::ALT_SEPARATOR then
+ gem_path.map! do |this_path|
+ this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR
+ end
+ end
+
+ gem_path << @home
+ else
+ gem_path = Gem.default_path + [@home]
+
+ if defined?(Gem::APPLE_GEM_HOME)
+ gem_path << Gem::APPLE_GEM_HOME
+ end
+ end
+
+ @path = gem_path.uniq
+ end
+end
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index b6608c7814..cf6db7c16d 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -4,6 +4,8 @@
# File a patch instead and assign it to Ryan Davis or Eric Hodel.
######################################################################
+require "rubygems/deprecate"
+
##
# Available list of platforms for targeting Gem installations.
@@ -121,8 +123,13 @@ class Gem::Platform
# the same CPU, OS and version.
def ==(other)
- self.class === other and
- @cpu == other.cpu and @os == other.os and @version == other.version
+ self.class === other and to_a == other.to_a
+ end
+
+ alias :eql? :==
+
+ def hash # :nodoc:
+ to_a.hash
end
##
@@ -185,5 +192,8 @@ class Gem::Platform
CURRENT = 'current'
+ extend Deprecate
+
+ deprecate :empty?, :none, 2011, 11
end
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 520adb5827..4613bffbfa 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -75,6 +75,7 @@ class Gem::RemoteFetcher
when URI::HTTP then proxy
else URI.parse(proxy)
end
+ @user_agent = user_agent
end
##
@@ -85,7 +86,8 @@ class Gem::RemoteFetcher
# larger, more emcompassing effort. -erikh
def download_to_cache dependency
- found = Gem::SpecFetcher.fetcher.fetch dependency
+ found = Gem::SpecFetcher.fetcher.fetch dependency, true, true,
+ dependency.prerelease?
return if found.empty?
@@ -103,12 +105,12 @@ class Gem::RemoteFetcher
Gem.ensure_gem_subdirectories(install_dir) rescue nil
if File.writable?(install_dir)
- cache_dir = Gem.cache_dir(install_dir)
+ cache_dir = File.join install_dir, "cache"
else
- cache_dir = Gem.cache_dir(Gem.user_dir)
+ cache_dir = File.join Gem.user_dir, "cache"
end
- gem_file_name = spec.file_name
+ gem_file_name = File.basename spec.cache_file
local_gem_path = File.join cache_dir, gem_file_name
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
@@ -116,8 +118,8 @@ class Gem::RemoteFetcher
# Always escape URI's to deal with potential spaces and such
unless URI::Generic === source_uri
source_uri = URI.parse(URI.const_defined?(:DEFAULT_PARSER) ?
- URI::DEFAULT_PARSER.escape(source_uri) :
- URI.escape(source_uri))
+ URI::DEFAULT_PARSER.escape(source_uri.to_s) :
+ URI.escape(source_uri.to_s))
end
scheme = source_uri.scheme
@@ -193,18 +195,54 @@ class Gem::RemoteFetcher
end
##
+ # File Fetcher. Dispatched by +fetch_path+. Use it instead.
+
+ def fetch_file uri, *_
+ Gem.read_binary correct_for_windows_path uri.path
+ end
+
+ ##
+ # HTTP Fetcher. Dispatched by +fetch_path+. Use it instead.
+
+ def fetch_http uri, last_modified = nil, head = false, depth = 0
+ fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
+ response = request uri, fetch_type, last_modified
+
+ case response
+ when Net::HTTPOK, Net::HTTPNotModified then
+ head ? response : response.body
+ when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
+ Net::HTTPTemporaryRedirect then
+ raise FetchError.new('too many redirects', uri) if depth > 10
+
+ location = URI.parse response['Location']
+ fetch_http(location, last_modified, head, depth + 1)
+ else
+ raise FetchError.new("bad response #{response.message} #{response.code}", uri)
+ end
+ end
+
+ alias :fetch_https :fetch_http
+
+ ##
# Downloads +uri+ and returns it as a String.
def fetch_path(uri, mtime = nil, head = false)
- data = open_uri_or_path uri, mtime, head
+ uri = URI.parse uri unless URI::Generic === uri
+
+ raise ArgumentError, "bad uri: #{uri}" unless uri
+ raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" unless
+ uri.scheme
+
+ data = send "fetch_#{uri.scheme}", uri, mtime, head
data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/
data
rescue FetchError
raise
rescue Timeout::Error
- raise FetchError.new('timed out', uri)
+ raise FetchError.new('timed out', uri.to_s)
rescue IOError, SocketError, SystemCallError => e
- raise FetchError.new("#{e.class}: #{e}", uri)
+ raise FetchError.new("#{e.class}: #{e}", uri.to_s)
end
##
@@ -306,36 +344,8 @@ class Gem::RemoteFetcher
# read from the filesystem instead.
def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0)
- raise "block is dead" if block_given?
-
- uri = URI.parse uri unless URI::Generic === uri
-
- # This check is redundant unless Gem::RemoteFetcher is likely
- # to be used directly, since the scheme is checked elsewhere.
- # - Daniel Berger
- unless ['http', 'https', 'file'].include?(uri.scheme)
- raise ArgumentError, 'uri scheme is invalid'
- end
-
- if uri.scheme == 'file'
- path = correct_for_windows_path(uri.path)
- return Gem.read_binary(path)
- end
-
- fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
- response = request uri, fetch_type, last_modified
-
- case response
- when Net::HTTPOK, Net::HTTPNotModified then
- head ? response : response.body
- when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
- Net::HTTPTemporaryRedirect then
- raise FetchError.new('too many redirects', uri) if depth > 10
-
- open_uri_or_path(response['Location'], last_modified, head, depth + 1)
- else
- raise FetchError.new("bad response #{response.message} #{response.code}", uri)
- end
+ raise "NO: Use fetch_path instead"
+ # TODO: deprecate for fetch_path
end
##
@@ -350,12 +360,7 @@ class Gem::RemoteFetcher
request.basic_auth uri.user, uri.password
end
- ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
- ua << " Ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
- ua << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
- ua << ")"
-
- request.add_field 'User-Agent', ua
+ request.add_field 'User-Agent', @user_agent
request.add_field 'Connection', 'keep-alive'
request.add_field 'Keep-Alive', '30'
@@ -447,5 +452,24 @@ class Gem::RemoteFetcher
connection.start
end
+ def user_agent
+ ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
+
+ ruby_version = RUBY_VERSION
+ ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
+
+ ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
+ if RUBY_PATCHLEVEL >= 0 then
+ ua << " patchlevel #{RUBY_PATCHLEVEL}"
+ elsif defined?(RUBY_REVISION) then
+ ua << " revision #{RUBY_REVISION}"
+ end
+ ua << ")"
+
+ ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby'
+
+ ua
+ end
+
end
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 35f7d0809a..f7bda10cf7 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -141,6 +141,18 @@ class Gem::Requirement
requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv }
end
+ alias :=== :satisfied_by?
+ alias :=~ :satisfied_by?
+
+ ##
+ # True if the requirement will not always match the latest version.
+
+ def specific?
+ return true if @requirements.length > 1 # GIGO, > 1, > 2 is silly
+
+ not %w[> >=].include? @requirements.first.first # grab the operator
+ end
+
def to_s # :nodoc:
as_list.join ", "
end
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index 1ff375dc4a..2709279542 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -393,7 +393,7 @@ module Gem::Security
:munge_re => Regexp.new(/[^a-z0-9_.-]+/),
# output directory for trusted certificate checksums
- :trust_dir => File::join(Gem.user_home, '.gem', 'trust'),
+ :trust_dir => File.join(Gem.user_home, '.gem', 'trust'),
# default permissions for trust directory and certs
:perms => {
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index 76cb22f252..95ca292fa7 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -81,47 +81,47 @@ class Gem::Server
<dl>
<% values["specs"].each do |spec| %>
- <dt>
- <% if spec["first_name_entry"] then %>
- <a name="<%=spec["name"]%>"></a>
- <% end %>
-
- <b><%=spec["name"]%> <%=spec["version"]%></b>
-
- <% if spec["rdoc_installed"] then %>
- <a href="<%=spec["doc_path"]%>">[rdoc]</a>
- <% else %>
- <span title="rdoc not installed">[rdoc]</span>
- <% end %>
-
- <% if spec["homepage"] then %>
- <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
- <% else %>
- <span title="no homepage available">[www]</span>
- <% end %>
-
- <% if spec["has_deps"] then %>
- - depends on
- <%= spec["dependencies"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
- <% end %>
- </dt>
- <dd>
- <%=spec["summary"]%>
- <% if spec["executables"] then %>
- <br/>
-
- <% if spec["only_one_executable"] then %>
- Executable is
- <% else %>
- Executables are
- <%end%>
-
- <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
-
- <%end%>
- <br/>
- <br/>
- </dd>
+ <dt>
+ <% if spec["first_name_entry"] then %>
+ <a name="<%=spec["name"]%>"></a>
+ <% end %>
+
+ <b><%=spec["name"]%> <%=spec["version"]%></b>
+
+ <% if spec["rdoc_installed"] then %>
+ <a href="<%=spec["doc_path"]%>">[rdoc]</a>
+ <% else %>
+ <span title="rdoc not installed">[rdoc]</span>
+ <% end %>
+
+ <% if spec["homepage"] then %>
+ <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a>
+ <% else %>
+ <span title="no homepage available">[www]</span>
+ <% end %>
+
+ <% if spec["has_deps"] then %>
+ - depends on
+ <%= spec["dependencies"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>.
+ <% end %>
+ </dt>
+ <dd>
+ <%=spec["summary"]%>
+ <% if spec["executables"] then %>
+ <br/>
+
+ <% if spec["only_one_executable"] then %>
+ Executable is
+ <% else %>
+ Executables are
+ <%end%>
+
+ <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>.
+
+ <%end%>
+ <br/>
+ <br/>
+ </dd>
<% end %>
</dl>
@@ -460,15 +460,15 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
spec_dir
end
- @source_index = Gem::SourceIndex.from_gems_in(*@spec_dirs)
+ Gem::Specification.dirs = @gem_dirs
end
def Marshal(req, res)
- @source_index.refresh!
+ Gem::Specification.reset
add_date res
- index = Marshal.dump @source_index
+ index = Deprecate.skip_during { Marshal.dump Gem.source_index }
if req.request_method == 'HEAD' then
res['content-length'] = index.length
@@ -492,15 +492,16 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def latest_specs(req, res)
- @source_index.refresh!
+ Gem::Specification.reset
res['content-type'] = 'application/x-gzip'
add_date res
- specs = @source_index.latest_specs.sort.map do |spec|
- platform = spec.original_platform
- platform = Gem::Platform::RUBY if platform.nil?
+ latest_specs = Gem::Specification.latest_specs
+
+ specs = latest_specs.sort.map do |spec|
+ platform = spec.original_platform || Gem::Platform::RUBY
[spec.name, spec.version, platform]
end
@@ -552,21 +553,20 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def quick(req, res)
- @source_index.refresh!
+ Gem::Specification.reset
res['content-type'] = 'text/plain'
add_date res
case req.request_uri.path
when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
- dep = Gem::Dependency.new $2, $3
- specs = @source_index.search dep
- marshal_format = $1
+ marshal_format, name, version, platform = $1, $2, $3, $4
+ specs = Gem::Specification.find_all_by_name name, version
- selector = [$2, $3, $4].map { |s| s.inspect }.join ' '
+ selector = [name, version, platform].map(&:inspect).join ' '
- platform = if $4 then
- Gem::Platform.new $4.sub(/^-/, '')
+ platform = if platform then
+ Gem::Platform.new platform.sub(/^-/, '')
else
Gem::Platform::RUBY
end
@@ -589,7 +589,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def root(req, res)
- @source_index.refresh!
+ Gem::Specification.reset
add_date res
raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
@@ -598,13 +598,15 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
specs = []
total_file_count = 0
- @source_index.each do |path, spec|
+ Gem::Specification.each do |spec|
total_file_count += spec.files.size
- deps = spec.dependencies.map do |dep|
- { "name" => dep.name,
+ deps = spec.dependencies.map { |dep|
+ {
+ "name" => dep.name,
"type" => dep.type,
- "version" => dep.requirement.to_s, }
- end
+ "version" => dep.requirement.to_s,
+ }
+ }
deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
deps.last["is_last"] = true unless deps.empty?
@@ -798,13 +800,12 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
end
def specs(req, res)
- @source_index.refresh!
+ Gem::Specification.reset
add_date res
- specs = @source_index.sort.map do |_, spec|
- platform = spec.original_platform
- platform = Gem::Platform::RUBY if platform.nil?
+ specs = Gem::Specification.sort_by(&:sort_obj).map do |spec|
+ platform = spec.original_platform || Gem::Platform::RUBY
[spec.name, spec.version, platform]
end
@@ -827,12 +828,11 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
def launch
listeners = @server.listeners.map{|l| l.addr[2] }
+ # TODO: 0.0.0.0 == any, not localhost.
host = listeners.any?{|l| l == '0.0.0.0'} ? 'localhost' : listeners.first
say "Launching browser to http://#{host}:#{@port}"
system("#{@launch} http://#{host}:#{@port}")
end
-
end
-
diff --git a/lib/rubygems/source_index.rb b/lib/rubygems/source_index.rb
index a6733fac31..74ad5476d4 100644
--- a/lib/rubygems/source_index.rb
+++ b/lib/rubygems/source_index.rb
@@ -11,6 +11,7 @@
#++
require 'rubygems/specification'
+require 'rubygems/deprecate'
##
# The SourceIndex object indexes all the gems available from a
@@ -34,79 +35,86 @@ class Gem::SourceIndex
attr_accessor :spec_dirs
- class << self
- ##
- # Factory method to construct a source index instance for a given
- # path.
- #
- # deprecated::
- # If supplied, from_installed_gems will act just like
- # +from_gems_in+. This argument is deprecated and is provided
- # just for backwards compatibility, and should not generally
- # be used.
- #
- # return::
- # SourceIndex instance
-
- def from_installed_gems(*deprecated)
- if deprecated.empty?
- from_gems_in(*installed_spec_directories)
- else
- from_gems_in(*deprecated) # HACK warn
- end
+ ##
+ # Factory method to construct a source index instance for a given
+ # path.
+ #
+ # deprecated::
+ # If supplied, from_installed_gems will act just like
+ # +from_gems_in+. This argument is deprecated and is provided
+ # just for backwards compatibility, and should not generally
+ # be used.
+ #
+ # return::
+ # SourceIndex instance
+
+ def self.from_installed_gems(*deprecated)
+ if deprecated.empty?
+ from_gems_in(*installed_spec_directories)
+ else
+ warn "NOTE: from_installed_gems(arg) is deprecated. From #{caller.first}"
+ from_gems_in(*deprecated) # HACK warn
end
+ end
- ##
- # Returns a list of directories from Gem.path that contain specifications.
+ ##
+ # Returns a list of directories from Gem.path that contain specifications.
- def installed_spec_directories
- Gem.path.collect { |dir| File.join(dir, "specifications") }
- end
+ def self.installed_spec_directories
+ # TODO: move to Gem::Utils
+ Gem.path.collect { |dir| File.join(dir, "specifications") }
+ end
- ##
- # Creates a new SourceIndex from the ruby format gem specifications in
- # +spec_dirs+.
+ ##
+ # Creates a new SourceIndex from the ruby format gem specifications in
+ # +spec_dirs+.
- def from_gems_in(*spec_dirs)
- source_index = new
- source_index.spec_dirs = spec_dirs
- source_index.refresh!
- end
+ def self.from_gems_in(*spec_dirs)
+ new spec_dirs
+ end
- ##
- # Loads a ruby-format specification from +file_name+ and returns the
- # loaded spec.
+ ##
+ # Loads a ruby-format specification from +file_name+ and returns the
+ # loaded spec.
- def load_specification(file_name)
- Gem::Specification.load file_name
+ def self.load_specification(file_name)
+ Deprecate.skip_during do
+ Gem::Specification.load Gem::Path.new(file_name)
end
-
end
##
# Constructs a source index instance from the provided specifications, which
# is a Hash of gem full names and Gem::Specifications.
- #--
- # TODO merge @gems and @prerelease_gems and provide a separate method
- # #prerelease_gems
- def initialize(specifications={})
+ def initialize specs_or_dirs = []
@gems = {}
- specifications.each{ |full_name, spec| add_spec spec }
@spec_dirs = nil
+
+ case specs_or_dirs
+ when Hash then
+ specs_or_dirs.each do |full_name, spec|
+ add_spec spec
+ end
+ when Array, String then
+ self.spec_dirs = Array(specs_or_dirs)
+ refresh!
+ else
+ arg = specs_or_dirs.inspect
+ warn "NOTE: SourceIndex.new(#{arg}) is deprecated; From #{caller.first}."
+ end
end
- # TODO: remove method
def all_gems
- @gems
+ gems
end
def prerelease_gems
- @gems.reject{ |name, gem| !gem.version.prerelease? }
+ @gems.reject { |name, gem| !gem.version.prerelease? }
end
def released_gems
- @gems.reject{ |name, gem| gem.version.prerelease? }
+ @gems.reject { |name, gem| gem.version.prerelease? }
end
##
@@ -116,10 +124,12 @@ class Gem::SourceIndex
@gems.clear
spec_dirs.reverse_each do |spec_dir|
- spec_files = Dir.glob File.join(spec_dir, '*.gemspec')
+ spec_files = Dir[File.join(spec_dir, "*.gemspec")]
spec_files.each do |spec_file|
- gemspec = Gem::Specification.load spec_file
+ gemspec = Deprecate.skip_during do
+ Gem::Specification.load spec_file
+ end
add_spec gemspec if gemspec
end
end
@@ -159,8 +169,6 @@ class Gem::SourceIndex
result[name] << spec
end
- # TODO: why is this a hash while @gems is an array? Seems like
- # structural similarity would be good.
result.values.flatten
end
@@ -246,7 +254,10 @@ class Gem::SourceIndex
def find_name(gem_name, requirement = Gem::Requirement.default)
dep = Gem::Dependency.new gem_name, requirement
- search dep
+
+ Deprecate.skip_during do
+ search dep
+ end
end
##
@@ -258,9 +269,9 @@ class Gem::SourceIndex
# +gem_pattern+, and a Gem::Requirement for +platform_only+. This
# behavior is deprecated and will be removed.
- def search(gem_pattern, platform_only = false)
+ def search(gem_pattern, platform_or_requirement = false)
requirement = nil
- only_platform = false
+ only_platform = false # FIX: WTF is this?!?
# TODO - Remove support and warning for legacy arguments after 2008/11
unless Gem::Dependency === gem_pattern
@@ -269,9 +280,9 @@ class Gem::SourceIndex
case gem_pattern
when Regexp then
- requirement = platform_only || Gem::Requirement.default
+ requirement = platform_or_requirement || Gem::Requirement.default
when Gem::Dependency then
- only_platform = platform_only
+ only_platform = platform_or_requirement
requirement = gem_pattern.requirement
gem_pattern = if Regexp === gem_pattern.name then
@@ -282,7 +293,7 @@ class Gem::SourceIndex
/^#{Regexp.escape gem_pattern.name}$/
end
else
- requirement = platform_only || Gem::Requirement.default
+ requirement = platform_or_requirement || Gem::Requirement.default
gem_pattern = /#{gem_pattern}/i
end
@@ -290,7 +301,7 @@ class Gem::SourceIndex
requirement = Gem::Requirement.create requirement
end
- specs = all_gems.values.select do |spec|
+ specs = @gems.values.select do |spec|
spec.name =~ gem_pattern and
requirement.satisfied_by? spec.version
end
@@ -343,7 +354,6 @@ class Gem::SourceIndex
def dump
Marshal.dump(self)
end
-
end
# :stopdoc:
@@ -356,5 +366,45 @@ module Gem
Cache = SourceIndex
end
-# :startdoc:
+class Gem::SourceIndex
+ extend Deprecate
+
+ deprecate :all_gems, :none, 2011, 10
+
+ deprecate :==, :none, 2011, 11 # noisy
+ deprecate :add_specs, :none, 2011, 11 # noisy
+ deprecate :each, :none, 2011, 11
+ deprecate :gems, :none, 2011, 11
+ deprecate :load_gems_in, :none, 2011, 11
+ deprecate :refresh!, :none, 2011, 11
+ deprecate :spec_dirs=, "Specification.dirs=", 2011, 11 # noisy
+ deprecate :add_spec, "Specification.add_spec", 2011, 11
+ deprecate :find_name, "Specification.find_by_name", 2011, 11
+ deprecate :gem_signature, :none, 2011, 11
+ deprecate :index_signature, :none, 2011, 11
+ deprecate :initialize, :none, 2011, 11
+ deprecate :latest_specs, "Specification.latest_specs", 2011, 11
+ deprecate :length, "Specification.all.length", 2011, 11
+ deprecate :outdated, :none, 2011, 11
+ deprecate :prerelease_gems, :none, 2011, 11
+ deprecate :prerelease_specs, :none, 2011, 11
+ deprecate :released_gems, :none, 2011, 11
+ deprecate :released_specs, :none, 2011, 11
+ deprecate :remove_spec, "Specification.remove_spec", 2011, 11
+ deprecate :search, :none, 2011, 11
+ deprecate :size, "Specification.all.size", 2011, 11
+ deprecate :spec_dirs, "Specification.dirs", 2011, 11
+ deprecate :specification, "Specification.find", 2011, 11
+
+ class << self
+ extend Deprecate
+
+ deprecate :from_gems_in, :none, 2011, 10
+ deprecate :from_installed_gems, :none, 2011, 10
+ deprecate :installed_spec_directories, "Specification.dirs", 2011, 11
+ deprecate :load_specification, :none, 2011, 10
+ end
+end
+
+# :startdoc:
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 6c71ee63aa..3823dbd8f3 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -91,6 +91,7 @@ class Gem::SpecFetcher
all = false,
matching_platform = true,
prerelease = false)
+
specs_and_sources, errors = find_matching_with_errors(dependency,
all,
matching_platform,
@@ -144,7 +145,10 @@ class Gem::SpecFetcher
# matching released versions are returned. If +matching_platform+
# is false, gems for all platforms are returned.
- def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
+ def find_matching_with_errors(dependency,
+ all = false,
+ matching_platform = true,
+ prerelease = false)
found = {}
rejected_specs = {}
@@ -257,13 +261,12 @@ class Gem::SpecFetcher
loaded = false
if File.exist? local_file then
- spec_dump = @fetcher.fetch_path spec_path, File.mtime(local_file)
+ spec_dump =
+ @fetcher.fetch_path(spec_path, File.mtime(local_file)) rescue nil
- if spec_dump.nil? then
- spec_dump = Gem.read_binary local_file
- else
- loaded = true
- end
+ loaded = true if spec_dump
+
+ spec_dump ||= Gem.read_binary local_file
else
spec_dump = @fetcher.fetch_path spec_path
loaded = true
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index a5e250a5df..0d3cb4e0cc 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -13,6 +13,7 @@
require 'rubygems/version'
require 'rubygems/requirement'
require 'rubygems/platform'
+require "rubygems/deprecate"
# :stopdoc:
class Date; end # for ruby_code if date.rb wasn't required
@@ -34,11 +35,6 @@ class Date; end # for ruby_code if date.rb wasn't required
class Gem::Specification
##
- # Allows deinstallation of gems with legacy platforms.
-
- attr_accessor :original_platform # :nodoc:
-
- ##
# The the version number of a specification that does not specify one
# (i.e. RubyGems 0.7 or earlier).
@@ -48,6 +44,17 @@ class Gem::Specification
# The specification version applied to any new Specification instances
# created. This should be bumped whenever something in the spec format
# changes.
+ #
+ # Specification Version History:
+ #
+ # spec ruby
+ # ver ver yyyy-mm-dd description
+ # -1 <0.8.0 pre-spec-version-history
+ # 1 0.8.0 2004-08-01 Deprecated "test_suite_file" for "test_files"
+ # "test_file=x" is a shortcut for "test_files=[x]"
+ # 2 0.9.5 2007-10-01 Added "required_rubygems_version"
+ # Now forward-compatible with future versions
+ # 3 1.3.2 2009-01-03 Added Fixnum validation to specification_version
#--
# When updating this number, be sure to also update #to_ruby.
#
@@ -55,423 +62,423 @@ class Gem::Specification
CURRENT_SPECIFICATION_VERSION = 3
- ##
- # An informal list of changes to the specification. The highest-valued
- # key should be equal to the CURRENT_SPECIFICATION_VERSION.
-
- SPECIFICATION_VERSION_HISTORY = {
- -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
- 1 => [
- 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"',
- '"test_file=x" is a shortcut for "test_files=[x]"'
- ],
- 2 => [
- 'Added "required_rubygems_version"',
- 'Now forward-compatible with future versions',
- ],
- 3 => [
- 'Added Fixnum validation to the specification_version'
- ]
- }
-
# :stopdoc:
+
+ # version => # of fields
MARSHAL_FIELDS = { -1 => 16, 1 => 16, 2 => 16, 3 => 17 }
- now = Time.at(Time.now.to_i)
- TODAY = now - ((now.to_i + now.gmt_offset) % 86400)
+ today = Time.now.utc
+ TODAY = Time.utc(today.year, today.month, today.day)
+
# :startdoc:
##
- # Optional block used to gather newly defined instances.
+ # List of attribute names: [:name, :version, ...]
- @@gather = nil
+ @@required_attributes = [:rubygems_version,
+ :specification_version,
+ :name,
+ :version,
+ :date,
+ :summary,
+ :require_paths]
##
- # List of attribute names: [:name, :version, ...]
+ # Map of attribute names to default values.
+
+ @@default_value = {
+ :authors => [],
+ :autorequire => nil,
+ :bindir => 'bin',
+ :cert_chain => [],
+ :date => TODAY,
+ :dependencies => [],
+ :description => nil,
+ :email => nil,
+ :executables => [],
+ :extensions => [],
+ :extra_rdoc_files => [],
+ :files => [],
+ :homepage => nil,
+ :licenses => [],
+ :name => nil,
+ :platform => Gem::Platform::RUBY,
+ :post_install_message => nil,
+ :rdoc_options => [],
+ :require_paths => ['lib'],
+ :required_ruby_version => Gem::Requirement.default,
+ :required_rubygems_version => Gem::Requirement.default,
+ :requirements => [],
+ :rubyforge_project => nil,
+ :rubygems_version => Gem::VERSION,
+ :signing_key => nil,
+ :specification_version => CURRENT_SPECIFICATION_VERSION,
+ :summary => nil,
+ :test_files => [],
+ :version => nil,
+ }
+
+ @@attributes = @@default_value.keys.sort_by { |s| s.to_s }
+ @@array_attributes = @@default_value.reject { |k,v| v != [] }.keys
+ @@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k|
+ @@default_value[k].nil?
+ }
- @@required_attributes = []
+ ######################################################################
+ # :section: Required gemspec attributes
##
- # List of _all_ attributes and default values:
- #
- # [[:name, nil],
- # [:bindir, 'bin'],
- # ...]
+ # This gem's name
- @@attributes = []
+ attr_accessor :name
- @@nil_attributes = []
- @@non_nil_attributes = [:@original_platform]
+ ##
+ # This gem's version
+
+ attr_reader :version
##
- # List of array attributes
+ # Paths in the gem to add to $LOAD_PATH when this gem is activated.
+ #
+ # The default ['lib'] is typically sufficient.
- @@array_attributes = []
+ attr_accessor :require_paths
##
- # Map of attribute names to default values.
+ # The version of RubyGems used to create this gem.
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
- @@default_value = {}
+ attr_accessor :rubygems_version
##
- # Names of all specification attributes
+ # The Gem::Specification version of this gemspec.
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
- def self.attribute_names
- @@attributes.map { |name, default| name }
- end
+ attr_accessor :specification_version
##
- # Default values for specification attributes
+ # A short summary of this gem's description. Displayed in `gem list -d`.
+ #
+ # The description should be more detailed than the summary. For example,
+ # you might wish to copy the entire README into the description.
- def self.attribute_defaults
- @@attributes.dup
- end
+ attr_reader :summary
+
+ ######################################################################
+ # :section: Optional gemspec attributes
##
- # The default value for specification attribute +name+
+ # Autorequire was used by old RubyGems to automatically require a file.
+ #
+ # Deprecated: It is neither supported nor functional.
- def self.default_value(name)
- @@default_value[name]
- end
+ attr_accessor :autorequire
##
- # Required specification attributes
+ # The path in the gem for executable scripts. Usually 'bin'
- def self.required_attributes
- @@required_attributes.dup
- end
+ attr_accessor :bindir
##
- # Is +name+ a required attribute?
+ # The certificate chain used to sign this gem. See Gem::Security for
+ # details.
- def self.required_attribute?(name)
- @@required_attributes.include? name.to_sym
- end
+ attr_accessor :cert_chain
##
- # Specification attributes that are arrays (appendable and so-forth)
+ # A long description of this gem
- def self.array_attributes
- @@array_attributes.dup
- end
+ attr_reader :description
##
- # Specifies the +name+ and +default+ for a specification attribute, and
- # creates a reader and writer method like Module#attr_accessor.
+ # Sets the default executable for this gem.
#
- # The reader method returns the default if the value hasn't been set.
+ # Deprecated: You must now specify the executable name to Gem.bin_path.
- def self.attribute(name, default=nil)
- ivar_name = "@#{name}".intern
- if default.nil? then
- @@nil_attributes << ivar_name
- else
- @@non_nil_attributes << [ivar_name, default]
- end
+ attr_writer :default_executable
- @@attributes << [name, default]
- @@default_value[name] = default
- attr_accessor(name)
- end
+ ##
+ # A contact email for this gem
+ #
+ # If you are providing multiple authors and multiple emails they should be
+ # in the same order such that:
+ #
+ # Hash[*spec.authors.zip(spec.emails).flatten]
+ #
+ # Gives a hash of author name to email address.
+
+ attr_accessor :email
##
- # Same as :attribute, but ensures that values assigned to the attribute
- # are array values by applying :to_a to the value.
+ # The URL of this gem's home page
- def self.array_attribute(name)
- @@non_nil_attributes << ["@#{name}".intern, []]
+ attr_accessor :homepage
- @@array_attributes << name
- @@attributes << [name, []]
- @@default_value[name] = []
- code = %{
- def #{name}
- @#{name} ||= []
- end
- def #{name}=(value)
- @#{name} = Array(value)
- end
- }
+ ##
+ # True when this gemspec has been activated. This attribute is not persisted.
- module_eval code, __FILE__, __LINE__ - 9
- end
+ attr_accessor :loaded
+
+ alias :loaded? :loaded
##
- # Same as attribute above, but also records this attribute as mandatory.
+ # True when this gemspec has been activated. This attribute is not persisted.
- def self.required_attribute(*args)
- @@required_attributes << args.first
- attribute(*args)
- end
+ attr_accessor :activated
+
+ alias :activated? :activated
##
- # Sometimes we don't want the world to use a setter method for a
- # particular attribute.
- #
- # +read_only+ makes it private so we can still use it internally.
+ # Path this gemspec was loaded from. This attribute is not persisted.
- def self.read_only(*names)
- names.each do |name|
- private "#{name}="
- end
- end
+ attr_reader :loaded_from
- # Shortcut for creating several attributes at once (each with a default
- # value of +nil+).
+ ##
+ # Allows deinstallation of gems with legacy platforms.
- def self.attributes(*args)
- args.each do |arg|
- attribute(arg, nil)
- end
- end
+ attr_writer :original_platform # :nodoc:
##
- # Some attributes require special behaviour when they are accessed. This
- # allows for that.
+ # A message that gets displayed after the gem is installed
- def self.overwrite_accessor(name, &block)
- remove_method name
- define_method(name, &block)
- end
+ attr_accessor :post_install_message
##
- # Defines a _singular_ version of an existing _plural_ attribute (i.e. one
- # whose value is expected to be an array). This means just creating a
- # helper method that takes a single value and appends it to the array.
- # These are created for convenience, so that in a spec, one can write
- #
- # s.require_path = 'mylib'
- #
- # instead of:
- #
- # s.require_paths = ['mylib']
- #
- # That above convenience is available courtesy of:
- #
- # attribute_alias_singular :require_path, :require_paths
+ # The version of ruby required by this gem
- def self.attribute_alias_singular(singular, plural)
- define_method("#{singular}=") { |val|
- send("#{plural}=", [val])
- }
- define_method("#{singular}") {
- val = send("#{plural}")
- val.nil? ? nil : val.first
- }
- end
+ attr_reader :required_ruby_version
##
- # Dump only crucial instance variables.
- #--
- # MAINTAIN ORDER!
+ # The RubyGems version required by this gem
- def _dump(limit)
- Marshal.dump [
- @rubygems_version,
- @specification_version,
- @name,
- @version,
- (Time === @date ? @date : (require 'time'; Time.parse(@date.to_s))),
- @summary,
- @required_ruby_version,
- @required_rubygems_version,
- @original_platform,
- @dependencies,
- @rubyforge_project,
- @email,
- @authors,
- @description,
- @homepage,
- @has_rdoc,
- @new_platform,
- @licenses
- ]
- end
+ attr_reader :required_rubygems_version
##
- # Load custom marshal format, re-initializing defaults as needed
+ # The rubyforge project this gem lives under. i.e. RubyGems'
+ # rubyforge_project is "rubygems".
- def self._load(str)
- array = Marshal.load str
+ attr_accessor :rubyforge_project
- spec = Gem::Specification.new
- spec.instance_variable_set :@specification_version, array[1]
+ ##
+ # The key used to sign this gem. See Gem::Security for details.
- current_version = CURRENT_SPECIFICATION_VERSION
+ attr_accessor :signing_key
- field_count = if spec.specification_version > current_version then
- spec.instance_variable_set :@specification_version,
- current_version
- MARSHAL_FIELDS[current_version]
- else
- MARSHAL_FIELDS[spec.specification_version]
- end
+ def self._all # :nodoc:
+ unless defined?(@@all) && @@all then
+ specs = []
- if array.size < field_count then
- raise TypeError, "invalid Gem::Specification format #{array.inspect}"
+ self.dirs.reverse_each { |dir|
+ Dir[File.join(dir, "*.gemspec")].each { |path|
+ spec = Gem::Specification.load path.untaint
+ # #load returns nil if the spec is bad, so we just ignore
+ # it at this stage
+ specs << spec if spec
+ }
+ }
+
+ @@all = specs
+ _resort!
end
+ @@all
+ end
- spec.instance_variable_set :@rubygems_version, array[0]
- # spec version
- spec.instance_variable_set :@name, array[2]
- spec.instance_variable_set :@version, array[3]
- spec.instance_variable_set :@date, array[4]
- spec.instance_variable_set :@summary, array[5]
- spec.instance_variable_set :@required_ruby_version, array[6]
- spec.instance_variable_set :@required_rubygems_version, array[7]
- spec.instance_variable_set :@original_platform, array[8]
- spec.instance_variable_set :@dependencies, array[9]
- spec.instance_variable_set :@rubyforge_project, array[10]
- spec.instance_variable_set :@email, array[11]
- spec.instance_variable_set :@authors, array[12]
- spec.instance_variable_set :@description, array[13]
- spec.instance_variable_set :@homepage, array[14]
- spec.instance_variable_set :@has_rdoc, array[15]
- spec.instance_variable_set :@new_platform, array[16]
- spec.instance_variable_set :@platform, array[16].to_s
- spec.instance_variable_set :@license, array[17]
- spec.instance_variable_set :@loaded, false
+ def self._resort! # :nodoc:
+ @@all.sort! { |a, b|
+ names = a.name <=> b.name
+ next names if names.nonzero?
+ b.version <=> a.version
+ }
+ end
- spec
+ ##
+ # Adds +spec+ to the known specifications, keeping the collection
+ # properly sorted.
+
+ def self.add_spec spec
+ # TODO: find all extraneous adds
+ # puts
+ # p :add_spec => [spec.full_name, caller.reject { |s| s =~ /minitest/ }]
+
+ # TODO: flush the rest of the crap from the tests
+ # raise "no dupes #{spec.full_name} in #{all_names.inspect}" if
+ # _all.include? spec
+
+ raise "nil spec!" unless spec # TODO: remove once we're happy with tests
+
+ return if _all.include? spec
+
+ _all << spec
+ _resort!
end
##
- # List of dependencies that will automatically be activated at runtime.
+ # Adds multiple specs to the known specifications.
- def runtime_dependencies
- # TODO: fix #type to return :runtime if nil
- dependencies.select { |d| d.type == :runtime }
+ def self.add_specs *specs
+ raise "nil spec!" if specs.any?(&:nil?) # TODO: remove once we're happy
+
+ # TODO: this is much more efficient, but we need the extra checks for now
+ # _all.concat specs
+ # _resort!
+
+ specs.each do |spec| # TODO: slow
+ add_spec spec
+ end
end
##
- # List of dependencies that are used for development
+ # Returns all specifications. This method is discouraged from use.
+ # You probably want to use one of the Enumerable methods instead.
- def development_dependencies
- dependencies.select { |d| d.type == :development }
+ def self.all
+ warn "NOTE: Specification.all called from #{caller.first}" unless
+ Deprecate.skip
+ _all
end
- def test_suite_file # :nodoc:
- warn 'test_suite_file deprecated, use test_files'
- test_files.first
+ ##
+ # Sets the known specs to +specs+. Not guaranteed to work for you in
+ # the future. Use at your own risk. Caveat emptor. Doomy doom doom.
+ # Etc etc.
+ #
+ #--
+ # Makes +specs+ the known specs
+ # Listen, time is a river
+ # Winter comes, code breaks
+ #
+ # -- wilsonb
+
+ def self.all= specs
+ @@all = specs
end
- def test_suite_file=(val) # :nodoc:
- warn 'test_suite_file= deprecated, use test_files='
- @test_files = [] unless defined? @test_files
- @test_files << val
+ ##
+ # Return full names of all specs in sorted order.
+
+ def self.all_names
+ self._all.map(&:full_name)
end
##
- # true when this gemspec has been loaded from a specifications directory.
- # This attribute is not persisted.
+ # Return the list of all array-oriented instance variables.
+ #--
+ # Not sure why we need to use so much stupid reflection in here...
- attr_accessor :loaded
+ def self.array_attributes
+ @@array_attributes.dup
+ end
##
- # Path this gemspec was loaded from. This attribute is not persisted.
+ # Return the list of all instance variables.
+ #--
+ # Not sure why we need to use so much stupid reflection in here...
- attr_accessor :loaded_from
+ def self.attribute_names
+ @@attributes.dup
+ end
##
- # Returns an array with bindir attached to each executable in the
- # executables list
+ # Return the directories that Specification uses to find specs.
- def add_bindir(executables)
- return nil if executables.nil?
+ def self.dirs
+ @@dirs ||= Gem.path.collect { |dir|
+ File.join dir, "specifications"
+ }
+ end
- if @bindir then
- Array(executables).map { |e| File.join(@bindir, e) }
- else
- executables
- end
- rescue
- return nil
+ ##
+ # Set the directories that Specification uses to find specs. Setting
+ # this resets the list of known specs.
+
+ def self.dirs= dirs
+ # TODO: find extra calls to dir=
+ # warn "NOTE: dirs= called from #{caller.first} for #{dirs.inspect}"
+
+ self.reset
+
+ # ugh
+ @@dirs = Array(dirs).map { |dir| File.join dir, "specifications" }
end
+ extend Enumerable
+
##
- # Files in the Gem under one of the require_paths
+ # Enumerate every known spec. See ::dirs= and ::add_spec to set the list of
+ # specs.
- def lib_files
- @files.select do |file|
- require_paths.any? do |path|
- file.index(path) == 0
- end
+ def self.each
+ return enum_for(:each) unless block_given?
+
+ self._all.each do |x|
+ yield x
end
end
##
- # True if this gem was loaded from disk
+ # Returns every spec that matches +name+ and optional +requirements+.
- alias :loaded? :loaded
+ def self.find_all_by_name name, *requirements
+ requirements = Gem::Requirement.default if requirements.empty?
- ##
- # True if this gem has files in test_files
+ # TODO: maybe try: find_all { |s| spec === dep }
- def has_unit_tests?
- not test_files.empty?
+ Gem::Dependency.new(name, *requirements).matching_specs
end
- # :stopdoc:
- alias has_test_suite? has_unit_tests?
- # :startdoc:
-
##
- # Specification constructor. Assigns the default values to the
- # attributes and yields itself for further
- # initialization. Optionally takes +name+ and +version+.
+ # Find the best specification matching a +name+ and +requirements+. Raises
+ # if the dependency doesn't resolve to a valid specification.
- def initialize name = nil, version = nil
- @new_platform = nil
- assign_defaults
- @loaded = false
- @loaded_from = nil
+ def self.find_by_name name, *requirements
+ requirements = Gem::Requirement.default if requirements.empty?
- self.name = name if name
- self.version = version if version
+ # TODO: maybe try: find { |s| spec === dep }
- yield self if block_given?
+ Gem::Dependency.new(name, *requirements).to_spec
+ end
- @@gather.call(self) if @@gather
+ ##
+ # Return the best specification that contains the file matching +path+.
+
+ def self.find_by_path path
+ self.find { |spec|
+ spec.contains_requirable_file? path
+ }
end
##
- # Duplicates array_attributes from +other_spec+ so state isn't shared.
+ # Return currently unresolved specs that contain the file matching +path+.
- def initialize_copy(other_spec)
- other_ivars = other_spec.instance_variables
- other_ivars = other_ivars.map { |ivar| ivar.intern } if # for 1.9
- other_ivars.any? { |ivar| String === ivar }
+ def self.find_in_unresolved path
+ # TODO: do we need these?? Kill it
+ specs = Gem.unresolved_deps.values.map { |dep| dep.to_specs }.flatten
- self.class.array_attributes.each do |name|
- name = :"@#{name}"
- next unless other_ivars.include? name
- instance_variable_set name, other_spec.instance_variable_get(name).dup
- end
+ specs.find_all { |spec| spec.contains_requirable_file? path }
end
##
- # Each attribute has a default value (possibly nil). Here, we initialize
- # all attributes to their default value. This is done through the
- # accessor methods, so special behaviours will be honored. Furthermore,
- # we take a _copy_ of the default so each specification instance has its
- # own empty arrays, etc.
+ # Search through all unresolved deps and sub-dependencies and return
+ # specs that contain the file matching +path+.
- def assign_defaults
- @@nil_attributes.each do |name|
- instance_variable_set name, nil
- end
+ def self.find_in_unresolved_tree path
+ specs = Gem.unresolved_deps.values.map { |dep| dep.to_specs }.flatten
- @@non_nil_attributes.each do |name, default|
- value = case default
- when Time, Numeric, Symbol, true, false, nil then default
- else default.dup
- end
+ specs.reverse_each do |spec|
+ trails = []
+ spec.traverse do |from_spec, dep, to_spec, trail|
+ next unless to_spec.conflicts.empty?
+ trails << trail if to_spec.contains_requirable_file? path
+ end
+
+ next if trails.empty?
- instance_variable_set name, value
+ return trails.map(&:reverse).sort.first.reverse
end
- # HACK
- instance_variable_set :@new_platform, Gem::Platform::RUBY
+ []
end
##
@@ -480,7 +487,7 @@ class Gem::Specification
# routine (#initialize). This method makes up for that and deals with
# gems of different ages.
#
- # 'input' can be anything that YAML.load() accepts: String or IO.
+ # +input+ can be anything that YAML.load() accepts: String or IO.
def self.from_yaml(input)
input = normalize_yaml_input input
@@ -505,6 +512,27 @@ class Gem::Specification
end
##
+ # Return the latest specs, optionally including prerelease specs if
+ # +prerelease+ is true.
+
+ def self.latest_specs prerelease = false
+ result = Hash.new { |h,k| h[k] = {} }
+ native = {}
+
+ Gem::Specification._all.reverse_each do |spec|
+ next if spec.version.prerelease? unless prerelease
+
+ native[spec.name] = spec.version if spec.platform == Gem::Platform::RUBY
+ result[spec.name][spec.platform] = spec
+ end
+
+ result.map(&:last).map(&:values).flatten.reject { |spec|
+ minimum = native[spec.name]
+ minimum && spec.version < minimum
+ }
+ end
+
+ ##
# Loads Ruby format gemspec from +file+.
def self.load file
@@ -524,7 +552,7 @@ class Gem::Specification
spec = eval code, binding, file
if Gem::Specification === spec
- spec.loaded_from = file
+ spec.loaded_from = file.to_s
return spec
end
@@ -539,451 +567,472 @@ class Gem::Specification
end
##
+ # Specification attributes that must be non-nil
+
+ def self.non_nil_attributes
+ @@non_nil_attributes.dup
+ end
+
+ ##
# Make sure the YAML specification is properly formatted with dashes
def self.normalize_yaml_input(input)
result = input.respond_to?(:read) ? input.read : input
result = "--- " + result unless result =~ /\A--- /
- result.gsub(/ !!null \n/, " \n")
+ result.gsub!(/ !!null \n/, " \n")
+ # date: 2011-04-26 00:00:00.000000000Z
+ # date: 2011-04-26 00:00:00.000000000 Z
+ result.gsub!(/^(date: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+?)Z/, '\1 Z')
+ result
end
##
- # Sets the rubygems_version to the current RubyGems version
+ # Return a list of all outdated specifications. This method is HEAVY
+ # as it must go fetch specifications from the server.
- def mark_version
- @rubygems_version = Gem::VERSION
- end
+ def self.outdated
+ outdateds = []
- ##
- # Ignore unknown attributes while loading
+ # TODO: maybe we should switch to rubygems' version service?
+ fetcher = Gem::SpecFetcher.fetcher
- def method_missing(sym, *a, &b) # :nodoc:
- if @specification_version > CURRENT_SPECIFICATION_VERSION and
- sym.to_s =~ /=$/ then
- warn "ignoring #{sym} loading #{full_name}" if $DEBUG
- else
- super
- end
- end
+ latest_specs.each do |local|
+ dependency = Gem::Dependency.new local.name, ">= #{local.version}"
+ remotes = fetcher.find_matching dependency
+ remotes = remotes.map { |(_, version, _), _| version }
+ latest = remotes.sort.last
- ##
- # Adds a development dependency named +gem+ with +requirements+ to this
- # Gem. For example:
- #
- # spec.add_development_dependency 'jabber4r', '> 0.1', '<= 0.5'
- #
- # Development dependencies aren't installed by default and aren't
- # activated when a gem is required.
+ outdateds << local.name if latest and local.version < latest
+ end
- def add_development_dependency(gem, *requirements)
- add_dependency_with_type(gem, :development, *requirements)
+ outdateds
end
##
- # Adds a runtime dependency named +gem+ with +requirements+ to this Gem.
- # For example:
- #
- # spec.add_runtime_dependency 'jabber4r', '> 0.1', '<= 0.5'
+ # Removes +spec+ from the known specs.
- def add_runtime_dependency(gem, *requirements)
- add_dependency_with_type(gem, :runtime, *requirements)
+ def self.remove_spec spec
+ # TODO: beat on the tests
+ raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless
+ _all.include? spec
+ _all.delete spec
end
##
- # Adds a runtime dependency
+ # Is +name+ a required attribute?
- alias add_dependency add_runtime_dependency
+ def self.required_attribute?(name)
+ @@required_attributes.include? name.to_sym
+ end
##
- # Returns the full name (name-version) of this Gem. Platform information
- # is included (name-version-platform) if it is specified and not the
- # default Ruby platform.
+ # Required specification attributes
- def full_name
- if platform == Gem::Platform::RUBY or platform.nil? then
- "#{@name}-#{@version}"
- else
- "#{@name}-#{@version}-#{platform}"
- end
+ def self.required_attributes
+ @@required_attributes.dup
end
##
- # Returns the full name (name-version) of this gemspec using the original
- # platform. For use with legacy gems.
+ # Reset the list of known specs, running pre and post reset hooks
+ # registered in Gem.
- def original_name # :nodoc:
- if platform == Gem::Platform::RUBY or platform.nil? then
- "#{@name}-#{@version}"
- else
- "#{@name}-#{@version}-#{@original_platform}"
- end
+ def self.reset
+ @@dirs = nil
+ # from = caller.first(10).reject { |s| s =~ /minitest/ }
+ # warn ""
+ # warn "NOTE: Specification.reset from #{from.inspect}"
+ Gem.pre_reset_hooks.each { |hook| hook.call }
+ @@all = nil
+ Gem.post_reset_hooks.each { |hook| hook.call }
end
##
- # The full path to the gem (install path + full name).
+ # Load custom marshal format, re-initializing defaults as needed
- def full_gem_path
- path = File.join installation_path, 'gems', full_name
- return path if File.directory? path
- File.join installation_path, 'gems', original_name
- end
+ def self._load(str)
+ array = Marshal.load str
- ##
- # The default (generated) file name of the gem. See also #spec_name.
- #
- # spec.file_name # => "example-1.0.gem"
+ spec = Gem::Specification.new
+ spec.instance_variable_set :@specification_version, array[1]
- def file_name
- full_name + '.gem'
- end
+ current_version = CURRENT_SPECIFICATION_VERSION
- ##
- # The directory that this gem was installed into.
+ field_count = if spec.specification_version > current_version then
+ spec.instance_variable_set :@specification_version,
+ current_version
+ MARSHAL_FIELDS[current_version]
+ else
+ MARSHAL_FIELDS[spec.specification_version]
+ end
- def installation_path
- unless @loaded_from then
- raise Gem::Exception, "spec #{full_name} is not from an installed gem"
+ if array.size < field_count then
+ raise TypeError, "invalid Gem::Specification format #{array.inspect}"
end
- File.expand_path File.dirname(File.dirname(@loaded_from))
+ spec.instance_variable_set :@rubygems_version, array[0]
+ # spec version
+ spec.instance_variable_set :@name, array[2]
+ spec.instance_variable_set :@version, array[3]
+ spec.instance_variable_set :@date, array[4]
+ spec.instance_variable_set :@summary, array[5]
+ spec.instance_variable_set :@required_ruby_version, array[6]
+ spec.instance_variable_set :@required_rubygems_version, array[7]
+ spec.instance_variable_set :@original_platform, array[8]
+ spec.instance_variable_set :@dependencies, array[9]
+ spec.instance_variable_set :@rubyforge_project, array[10]
+ spec.instance_variable_set :@email, array[11]
+ spec.instance_variable_set :@authors, array[12]
+ spec.instance_variable_set :@description, array[13]
+ spec.instance_variable_set :@homepage, array[14]
+ spec.instance_variable_set :@has_rdoc, array[15]
+ spec.instance_variable_set :@new_platform, array[16]
+ spec.instance_variable_set :@platform, array[16].to_s
+ spec.instance_variable_set :@license, array[17]
+ spec.instance_variable_set :@loaded, false
+
+ spec
end
- ##
- # Checks if this specification meets the requirement of +dependency+.
+ def <=>(other) # :nodoc:
+ sort_obj <=> other.sort_obj
+ end
- def satisfies_requirement?(dependency)
- return @name == dependency.name &&
- dependency.requirement.satisfied_by?(@version)
+ def == other # :nodoc:
+ self.class === other &&
+ name == other.name &&
+ version == other.version &&
+ platform == other.platform
end
##
- # Returns an object you can use to sort specifications in #sort_by.
+ # Dump only crucial instance variables.
+ #--
+ # MAINTAIN ORDER!
+ # (down with the man)
- def sort_obj
- [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
+ def _dump(limit)
+ Marshal.dump [
+ @rubygems_version,
+ @specification_version,
+ @name,
+ @version,
+ date,
+ @summary,
+ @required_ruby_version,
+ @required_rubygems_version,
+ @original_platform,
+ @dependencies,
+ @rubyforge_project,
+ @email,
+ @authors,
+ @description,
+ @homepage,
+ true, # has_rdoc
+ @new_platform,
+ @licenses
+ ]
end
##
- # The default name of the gemspec. See also #file_name
- #
- # spec.spec_name # => "example-1.0.gemspec"
+ # Activate this spec, registering it as a loaded spec and adding
+ # it's lib paths to $LOAD_PATH. Returns true if the spec was
+ # activated, false if it was previously activated. Freaks out if
+ # there are conflicts upon activation.
- def spec_name
- full_name + '.gemspec'
- end
+ def activate
+ raise_if_conflicts
- def <=>(other) # :nodoc:
- sort_obj <=> other.sort_obj
- end
+ return false if Gem.loaded_specs[self.name]
- ##
- # Tests specs for equality (across all attributes).
-
- def ==(other) # :nodoc:
- self.class === other && same_attributes?(other)
- end
+ activate_dependencies
+ add_self_to_load_path
- alias eql? == # :nodoc:
+ Gem.loaded_specs[self.name] = self
+ self.activated = true
- ##
- # A macro to yield cached gem path
- #
- def cache_gem
- cache_name = File.join(Gem.dir, 'cache', file_name)
- return File.exist?(cache_name) ? cache_name : nil
+ return true
end
##
- # True if this gem has the same attributes as +other+.
+ # Activate all unambiguously resolved runtime dependencies of this
+ # spec. Add any ambigous dependencies to the unresolved list to be
+ # resolved later, as needed.
+
+ def activate_dependencies
+ self.runtime_dependencies.each do |spec_dep|
+ # TODO: check for conflicts! not just name!
+ next if Gem.loaded_specs.include? spec_dep.name
+ specs = spec_dep.to_specs
- def same_attributes?(other)
- @@attributes.each do |name, default|
- return false unless self.send(name) == other.send(name)
+ if specs.size == 1 then
+ specs.first.activate
+ else
+ name = spec_dep.name
+ Gem.unresolved_deps[name] = Gem.unresolved_deps[name].merge spec_dep
+ end
end
- true
+
+ Gem.unresolved_deps.delete self.name
end
- private :same_attributes?
+ ##
+ # Returns an array with bindir attached to each executable in the
+ # +executables+ list
- def hash # :nodoc:
- @@attributes.inject(0) { |hash_code, (name, _)|
- hash_code ^ self.send(name).hash
- }
+ def add_bindir(executables)
+ return nil if executables.nil?
+
+ if @bindir then
+ Array(executables).map { |e| File.join(@bindir, e) }
+ else
+ executables
+ end
+ rescue
+ return nil
end
- def encode_with coder # :nodoc:
- mark_version
+ ##
+ # Adds a dependency on gem +dependency+ with type +type+ that requires
+ # +requirements+. Valid types are currently <tt>:runtime</tt> and
+ # <tt>:development</tt>.
- attributes = @@attributes.map { |name,| name.to_s }.sort
- attributes = attributes - %w[name version platform]
+ def add_dependency_with_type(dependency, type, *requirements)
+ requirements = if requirements.empty? then
+ Gem::Requirement.default
+ else
+ requirements.flatten
+ end
- coder.add 'name', @name
- coder.add 'version', @version
- platform = case @original_platform
- when nil, '' then
- 'ruby'
- when String then
- @original_platform
- else
- @original_platform.to_s
- end
- coder.add 'platform', platform
+ unless dependency.respond_to?(:name) &&
+ dependency.respond_to?(:version_requirements)
- attributes.each do |name|
- coder.add name, instance_variable_get("@#{name}")
+ dependency = Gem::Dependency.new(dependency, requirements, type)
end
- end
- def to_yaml(opts = {}) # :nodoc:
- if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
- super.gsub(/ !!null \n/, " \n")
- else
- YAML.quick_emit object_id, opts do |out|
- out.map taguri, to_yaml_style do |map|
- encode_with map
- end
- end
- end
+ dependencies << dependency
end
- def init_with coder # :nodoc:
- yaml_initialize coder.tag, coder.map
- end
+ private :add_dependency_with_type
- def yaml_initialize(tag, vals) # :nodoc:
- vals.each do |ivar, val|
- instance_variable_set "@#{ivar}", val
- end
+ ##
+ # Adds a development dependency named +gem+ with +requirements+ to this
+ # Gem. For example:
+ #
+ # spec.add_development_dependency 'example', '~> 1.1', '>= 1.1.4'
+ #
+ # Development dependencies aren't installed by default and aren't
+ # activated when a gem is required.
- @original_platform = @platform # for backwards compatibility
- self.platform = Gem::Platform.new @platform
+ def add_development_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :development, *requirements)
end
##
- # Returns a Ruby code representation of this specification, such that it
- # can be eval'ed and reconstruct the same specification later. Attributes
- # that still have their default values are omitted.
-
- def to_ruby
- mark_version
- result = []
- result << "# -*- encoding: utf-8 -*-"
- result << nil
- result << "Gem::Specification.new do |s|"
+ # Adds a runtime dependency named +gem+ with +requirements+ to this Gem.
+ # For example:
+ #
+ # spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4'
- result << " s.name = #{ruby_code name}"
- result << " s.version = #{ruby_code version}"
- unless platform.nil? or platform == Gem::Platform::RUBY then
- result << " s.platform = #{ruby_code original_platform}"
- end
- result << ""
- result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
+ def add_runtime_dependency(gem, *requirements)
+ add_dependency_with_type(gem, :runtime, *requirements)
+ end
- handled = [
- :dependencies,
- :name,
- :platform,
- :required_rubygems_version,
- :specification_version,
- :version,
- ]
+ alias add_dependency add_runtime_dependency
- attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s }
+ ##
+ # Adds this spec's require paths to LOAD_PATH, in the proper location.
- attributes.each do |attr_name, default|
- next if handled.include? attr_name
- current_value = self.send(attr_name)
- if current_value != default or
- self.class.required_attribute? attr_name then
- result << " s.#{attr_name} = #{ruby_code current_value}"
- end
+ def add_self_to_load_path
+ paths = require_paths.map do |path|
+ File.join full_gem_path, path
end
- result << nil
- result << " if s.respond_to? :specification_version then"
- result << " s.specification_version = #{specification_version}"
- result << nil
-
- result << " if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then"
+ # gem directories must come after -I and ENV['RUBYLIB']
+ insert_index = Gem.load_path_insert_index
- dependencies.each do |dep|
- req = dep.requirements_list.inspect
- dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
- result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{req})"
+ if insert_index then
+ # gem directories must come after -I and ENV['RUBYLIB']
+ $LOAD_PATH.insert(insert_index, *paths)
+ else
+ # we are probably testing in core, -I and RUBYLIB don't apply
+ $LOAD_PATH.unshift(*paths)
end
+ end
- result << " else"
+ ##
+ # Singular reader for #authors
- dependencies.each do |dep|
- version_reqs_param = dep.requirements_list.inspect
- result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
- end
+ def author
+ val = authors and val.first
+ end
- result << ' end'
+ ##
+ # Singular writer for #authors
- result << " else"
- dependencies.each do |dep|
- version_reqs_param = dep.requirements_list.inspect
- result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
- end
- result << " end"
+ def author= o
+ self.authors = [o]
+ end
- result << "end"
- result << nil
+ ##
+ # The list of author names who wrote this gem.
+ #
+ # If you are providing multiple authors and multiple emails they should be
+ # in the same order such that:
+ #
+ # Hash[*spec.authors.zip(spec.emails).flatten]
+ #
+ # Gives a hash of author name to email address.
- result.join "\n"
+ def authors
+ @authors ||= []
end
- def to_ruby_for_cache
- s = dup
- # remove large blobs that aren't used at runtime:
- s.files = nil
- s.extra_rdoc_files = nil
- s.rdoc_options = nil
- s.to_ruby
+ ##
+ # Sets the list of authors, ensuring it is an array.
+
+ def authors= value
+ @authors = Array(value).flatten.grep(String)
end
##
- # Checks that the specification contains all required fields, and does a
- # very basic sanity check.
+ # Returns the full path to the base gem directory.
#
- # Raises InvalidSpecificationException if the spec does not pass the
- # checks..
+ # eg: /usr/local/lib/ruby/gems/1.8
- def validate
- require 'rubygems/user_interaction'
- extend Gem::UserInteraction
- normalize
-
- if rubygems_version != Gem::VERSION then
- raise Gem::InvalidSpecificationException,
- "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
- end
+ def base_dir
+ return Gem.dir unless loaded_from
+ @base_dir ||= File.dirname File.dirname loaded_from
+ end
- @@required_attributes.each do |symbol|
- unless self.send symbol then
- raise Gem::InvalidSpecificationException,
- "missing value for attribute #{symbol}"
- end
- end
+ ##
+ # Returns the full path to installed gem's bin directory.
+ #
+ # NOTE: do not confuse this with +bindir+, which is just 'bin', not
+ # a full path.
- unless String === name then
- raise Gem::InvalidSpecificationException,
- "invalid value for attribute name: \"#{name.inspect}\""
- end
+ def bin_dir
+ @bin_dir ||= File.join gem_dir, bindir # TODO: this is unfortunate
+ end
- if require_paths.empty? then
- raise Gem::InvalidSpecificationException,
- 'specification must have at least one require_path'
- end
+ ##
+ # Returns the full path to an executable named +name+ in this gem.
- @files.delete_if do |file| File.directory? file end
- @test_files.delete_if do |file| File.directory? file end
- @executables.delete_if do |file|
- File.directory? File.join(bindir, file)
- end
- @extra_rdoc_files.delete_if do |file| File.directory? file end
- @extensions.delete_if do |file| File.directory? file end
+ def bin_file name
+ File.join bin_dir, name
+ end
- non_files = files.select do |file|
- !File.file? file
- end
+ ##
+ # Returns the full path to the cache directory containing this
+ # spec's cached gem.
- unless non_files.empty? then
- non_files = non_files.map { |file| file.inspect }
- raise Gem::InvalidSpecificationException,
- "[#{non_files.join ", "}] are not files"
- end
+ def cache_dir
+ @cache_dir ||= File.join base_dir, "cache"
+ end
- unless specification_version.is_a?(Fixnum)
- raise Gem::InvalidSpecificationException,
- 'specification_version must be a Fixnum (did you mean version?)'
- end
+ ##
+ # Returns the full path to the cached gem for this spec.
- case platform
- when Gem::Platform, Gem::Platform::RUBY then # ok
- else
- raise Gem::InvalidSpecificationException,
- "invalid platform #{platform.inspect}, see Gem::Platform"
- end
+ def cache_file
+ @cache_file ||= File.join cache_dir, "#{full_name}.gem"
+ end
- unless Array === authors and
- authors.all? { |author| String === author } then
- raise Gem::InvalidSpecificationException,
- 'authors must be Array of Strings'
- end
+ alias :cache_gem :cache_file
- licenses.each { |license|
- if license.length > 64
- raise Gem::InvalidSpecificationException,
- "each license must be 64 characters or less"
- end
- }
+ ##
+ # Return any possible conflicts against the currently loaded specs.
- # reject FIXME and TODO
+ def conflicts
+ conflicts = {}
+ Gem.loaded_specs.values.each do |spec|
+ bad = self.runtime_dependencies.find_all { |dep|
+ spec.name == dep.name and not spec.satisfies_requirement? dep
+ }
- unless authors.grep(/FIXME|TODO/).empty? then
- raise Gem::InvalidSpecificationException,
- '"FIXME" or "TODO" is not an author'
+ conflicts[spec] = bad unless bad.empty?
end
+ conflicts
+ end
- unless Array(email).grep(/FIXME|TODO/).empty? then
- raise Gem::InvalidSpecificationException,
- '"FIXME" or "TODO" is not an email address'
- end
+ ##
+ # Return true if this spec can require +file+.
- if description =~ /FIXME|TODO/ then
- raise Gem::InvalidSpecificationException,
- '"FIXME" or "TODO" is not a description'
- end
+ def contains_requirable_file? file
+ root = full_gem_path
- if summary =~ /FIXME|TODO/ then
- raise Gem::InvalidSpecificationException,
- '"FIXME" or "TODO" is not a summary'
+ require_paths.each do |lib|
+ base = "#{root}/#{lib}/#{file}"
+ Gem.suffixes.each do |suf|
+ path = "#{base}#{suf}"
+ return true if File.file? path
+ end
end
- if homepage and not homepage.empty? and
- homepage !~ /\A[a-z][a-z\d+.-]*:/i then
- raise Gem::InvalidSpecificationException,
- "\"#{homepage}\" is not a URI"
- end
+ return false
+ end
- # Warnings
+ ##
+ # The date this gem was created. Lazily defaults to TODAY.
- %w[author description email homepage summary].each do |attribute|
- value = self.send attribute
- alert_warning "no #{attribute} specified" if value.nil? or value.empty?
- end
+ def date
+ @date ||= TODAY
+ end
- if summary and not summary.empty? and description == summary then
- alert_warning 'description and summary are identical'
- end
+ ##
+ # The date this gem was created
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
- alert_warning "deprecated autorequire specified" if autorequire
+ def date= date
+ # We want to end up with a Time object with one-day resolution.
+ # This is the cleanest, most-readable, faster-than-using-Date
+ # way to do it.
+ @date = case date
+ when String then
+ if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then
+ Time.utc($1.to_i, $2.to_i, $3.to_i)
+ else
+ raise(Gem::InvalidSpecificationException,
+ "invalid date format in specification: #{date.inspect}")
+ end
+ when Time, Date then
+ Time.utc(date.year, date.month, date.day)
+ else
+ TODAY
+ end
+ end
- executables.each do |executable|
- executable_path = File.join bindir, executable
- shebang = File.read(executable_path, 2) == '#!'
+ ##
+ # The default executable for this gem.
+ #
+ # Deprecated: The name of the gem is assumed to be the name of the
+ # executable now. See Gem.bin_path.
- alert_warning "#{executable_path} is missing #! line" unless shebang
+ def default_executable
+ if defined?(@default_executable) and @default_executable
+ result = @default_executable
+ elsif @executables and @executables.size == 1
+ result = Array(@executables).first
+ else
+ result = nil
end
+ result
+ end
- true
+ ##
+ # The default value for specification attribute +name+
+
+ def default_value name
+ @@default_value[name]
end
##
- # Normalize the list of files so that:
- # * All file lists have redundancies removed.
- # * Files referenced in the extra_rdoc_files are included in the package
- # file list.
+ # A list of Gem::Dependency objects this gem depends on.
+ #
+ # Use #add_dependency or #add_development_dependency to add dependencies to
+ # a gem.
- def normalize
- if defined?(@extra_rdoc_files) and @extra_rdoc_files then
- @extra_rdoc_files.uniq!
- @files ||= []
- @files.concat(@extra_rdoc_files)
- end
- @files.uniq! if @files
+ def dependencies
+ @dependencies ||= []
end
##
@@ -994,436 +1043,493 @@ class Gem::Specification
def dependent_gems
out = []
- Gem.source_index.each do |name,gem|
- gem.dependencies.each do |dep|
+ Gem::Specification.each do |spec|
+ spec.dependencies.each do |dep|
if self.satisfies_requirement?(dep) then
sats = []
find_all_satisfiers(dep) do |sat|
sats << sat
end
- out << [gem, dep, sats]
+ out << [spec, dep, sats]
end
end
end
out
end
- def to_s # :nodoc:
- "#<Gem::Specification name=#{@name} version=#{@version}>"
- end
-
- def pretty_print(q) # :nodoc:
- q.group 2, 'Gem::Specification.new do |s|', 'end' do
- q.breakable
-
- attributes = @@attributes.sort_by { |attr_name,| attr_name.to_s }
+ ##
+ # Returns all specs that matches this spec's runtime dependencies.
- attributes.each do |attr_name, default|
- current_value = self.send attr_name
- if current_value != default or
- self.class.required_attribute? attr_name then
+ def dependent_specs
+ runtime_dependencies.map { |dep| dep.to_specs }.flatten
+ end
- q.text "s.#{attr_name} = "
+ ##
+ # A long description of this gem
- if attr_name == :date then
- current_value = current_value.utc
+ def description= str
+ @description = str.to_s
+ end
- q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})"
- else
- q.pp current_value
- end
+ ##
+ # List of dependencies that are used for development
- q.breakable
- end
- end
- end
+ def development_dependencies
+ dependencies.select { |d| d.type == :development }
end
##
- # Adds a dependency on gem +dependency+ with type +type+ that requires
- # +requirements+. Valid types are currently <tt>:runtime</tt> and
- # <tt>:development</tt>.
+ # Returns the full path to this spec's documentation directory.
- def add_dependency_with_type(dependency, type, *requirements)
- requirements = if requirements.empty? then
- Gem::Requirement.default
- else
- requirements.flatten
- end
+ def doc_dir
+ @doc_dir ||= File.join base_dir, 'doc', full_name
+ end
- unless dependency.respond_to?(:name) &&
- dependency.respond_to?(:version_requirements)
+ def encode_with coder # :nodoc:
+ mark_version
- dependency = Gem::Dependency.new(dependency, requirements, type)
+ coder.add 'name', @name
+ coder.add 'version', @version
+ platform = case @original_platform
+ when nil, '' then
+ 'ruby'
+ when String then
+ @original_platform
+ else
+ @original_platform.to_s
+ end
+ coder.add 'platform', platform
+
+ attributes = @@attributes.map(&:to_s) - %w[name version platform]
+ attributes.each do |name|
+ coder.add name, instance_variable_get("@#{name}")
end
+ end
- dependencies << dependency
+ def eql? other # :nodoc:
+ self.class === other && same_attributes?(other)
end
- private :add_dependency_with_type
+ ##
+ # Singular accessor for #executables
+
+ def executable
+ val = executables and val.first
+ end
##
- # Finds all gems that satisfy +dep+
+ # Singular accessor for #executables
- def find_all_satisfiers(dep)
- Gem.source_index.each do |_, gem|
- yield gem if gem.satisfies_requirement? dep
- end
+ def executable=o
+ self.executables = [o]
end
- private :find_all_satisfiers
+ ##
+ # Executables included in the gem.
+
+ def executables
+ @executables ||= []
+ end
##
- # Return a string containing a Ruby code representation of the given
- # object.
+ # Sets executables to +value+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
- def ruby_code(obj)
- case obj
- when String then '%q{' + obj + '}'
- when Array then obj.inspect
- when Gem::Version then obj.to_s.inspect
- when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}'
- when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}'
- when Numeric then obj.inspect
- when true, false, nil then obj.inspect
- when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})"
- when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})"
- else raise Gem::Exception, "ruby_code case not handled: #{obj.class}"
- end
+ def executables= value
+ # TODO: warn about setting instead of pushing
+ @executables = Array(value)
end
- private :ruby_code
+ ##
+ # Extensions to build when installing the gem. See
+ # Gem::Installer#build_extensions for valid values.
- # :section: Required gemspec attributes
+ def extensions
+ @extensions ||= []
+ end
##
- # :attr_accessor: rubygems_version
- #
- # The version of RubyGems used to create this gem.
- #
- # Do not set this, it is set automatically when the gem is packaged.
+ # Sets extensions to +extensions+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
- required_attribute :rubygems_version, Gem::VERSION
+ def extensions= extensions
+ # TODO: warn about setting instead of pushing
+ @extensions = Array extensions
+ end
##
- # :attr_accessor: specification_version
- #
- # The Gem::Specification version of this gemspec.
- #
- # Do not set this, it is set automatically when the gem is packaged.
+ # Extra files to add to RDoc such as README or doc/examples.txt
- required_attribute :specification_version, CURRENT_SPECIFICATION_VERSION
+ def extra_rdoc_files
+ @extra_rdoc_files ||= []
+ end
##
- # :attr_accessor: name
- #
- # This gem's name
+ # Sets extra_rdoc_files to +files+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
- required_attribute :name
+ def extra_rdoc_files= files
+ # TODO: warn about setting instead of pushing
+ @extra_rdoc_files = Array files
+ end
##
- # :attr_accessor: version
+ # The default (generated) file name of the gem. See also #spec_name.
#
- # This gem's version
+ # spec.file_name # => "example-1.0.gem"
- required_attribute :version
+ def file_name
+ "#{full_name}.gem"
+ end
##
- # :attr_accessor: date
+ # Files included in this gem. You cannot append to this accessor, you must
+ # assign to it.
#
- # The date this gem was created
+ # Only add files you can require to this list, not directories, etc.
#
- # Do not set this, it is set automatically when the gem is packaged.
+ # Directories are automatically stripped from this list when building a gem,
+ # other non-files cause an error.
- required_attribute :date, TODAY
+ def files
+ # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
+ @files = [@files,
+ @test_files,
+ add_bindir(@executables),
+ @extra_rdoc_files,
+ @extensions,
+ ].flatten.uniq.compact
+ end
##
- # :attr_accessor: summary
- #
- # A short summary of this gem's description. Displayed in `gem list -d`.
- #
- # The description should be more detailed than the summary. For example,
- # you might wish to copy the entire README into the description.
- #
- # As of RubyGems 1.3.2 newlines are no longer stripped.
+ # Sets files to +files+, ensuring it is an array.
- required_attribute :summary
+ def files= files
+ @files = Array files
+ end
##
- # :attr_accessor: require_paths
- #
- # Paths in the gem to add to $LOAD_PATH when this gem is activated.
- #
- # The default 'lib' is typically sufficient.
+ # Finds all gems that satisfy +dep+
- required_attribute :require_paths, ['lib']
+ def find_all_satisfiers dep
+ Gem::Specification.each do |spec|
+ yield spec if spec.satisfies_requirement? dep
+ end
+ end
- # :section: Optional gemspec attributes
+ private :find_all_satisfiers
##
- # :attr_accessor: email
- #
- # A contact email for this gem
- #
- # If you are providing multiple authors and multiple emails they should be
- # in the same order such that:
- #
- # Hash[*spec.authors.zip(spec.emails).flatten]
- #
- # Gives a hash of author name to email address.
+ # Creates a duplicate spec without large blobs that aren't used at runtime.
- attribute :email
+ def for_cache
+ spec = dup
- ##
- # :attr_accessor: homepage
- #
- # The URL of this gem's home page
+ spec.files = nil
+ spec.test_files = nil
- attribute :homepage
+ spec
+ end
##
- # :attr_accessor: rubyforge_project
- #
- # The rubyforge project this gem lives under. i.e. RubyGems'
- # rubyforge_project is "rubygems".
+ # The full path to the gem (install path + full name).
- attribute :rubyforge_project
+ def full_gem_path
+ # TODO: try to get rid of this... or the awkward
+ # TODO: also, shouldn't it default to full_name if it hasn't been written?
+ return @full_gem_path if defined?(@full_gem_path) && @full_gem_path
- ##
- # :attr_accessor: description
- #
- # A long description of this gem
+ @full_gem_path = File.expand_path File.join(gems_dir, full_name)
+
+ return @full_gem_path if File.directory? @full_gem_path
- attribute :description
+ @full_gem_path = File.expand_path File.join(gems_dir, original_name)
+ end
##
- # :attr_accessor: autorequire
- #
- # Autorequire was used by old RubyGems to automatically require a file.
- # It no longer is supported.
+ # Returns the full name (name-version) of this Gem. Platform information
+ # is included (name-version-platform) if it is specified and not the
+ # default Ruby platform.
- attribute :autorequire
+ def full_name
+ if platform == Gem::Platform::RUBY or platform.nil? then
+ "#{@name}-#{@version}"
+ else
+ "#{@name}-#{@version}-#{platform}"
+ end
+ end
##
- # :attr_accessor: default_executable
- #
- # The default executable for this gem.
- #
- # This is not used.
+ # Returns the full path to this spec's gem directory.
+ # eg: /usr/local/lib/ruby/1.8/gems/mygem-1.0
- attribute :default_executable
+ def gem_dir
+ @gem_dir ||= File.expand_path File.join(gems_dir, full_name)
+ end
##
- # :attr_accessor: bindir
- #
- # The path in the gem for executable scripts
+ # Returns the full path to the gems directory containing this spec's
+ # gem directory. eg: /usr/local/lib/ruby/1.8/gems
- attribute :bindir, 'bin'
+ def gems_dir
+ # TODO: this logic seems terribly broken, but tests fail if just base_dir
+ @gems_dir ||= File.join(loaded_from && base_dir || Gem.dir, "gems")
+ end
##
- # :attr_accessor: has_rdoc
- #
# Deprecated and ignored, defaults to true.
#
# Formerly used to indicate this gem was RDoc-capable.
- attribute :has_rdoc, true
+ def has_rdoc
+ true
+ end
##
- # True if this gem supports RDoc
+ # Deprecated and ignored.
+ #
+ # Formerly used to indicate this gem was RDoc-capable.
+
+ def has_rdoc= ignored
+ @has_rdoc = true
+ end
alias :has_rdoc? :has_rdoc
##
- # :attr_accessor: required_ruby_version
- #
- # The version of ruby required by this gem
-
- attribute :required_ruby_version, Gem::Requirement.default
+ # True if this gem has files in test_files
- ##
- # :attr_accessor: required_rubygems_version
- #
- # The RubyGems version required by this gem
+ def has_unit_tests?
+ not test_files.empty?
+ end
- attribute :required_rubygems_version, Gem::Requirement.default
+ # :stopdoc:
+ alias has_test_suite? has_unit_tests?
+ # :startdoc:
- ##
- # :attr_accessor: platform
- #
- # The platform this gem runs on. See Gem::Platform for details.
- #
- # Setting this to any value other than Gem::Platform::RUBY or
- # Gem::Platform::CURRENT is probably wrong.
+ def hash # :nodoc:
+ @@attributes.inject(0) { |hash_code, (name, _)|
+ hash_code ^ self.send(name).hash
+ }
+ end
- attribute :platform, Gem::Platform::RUBY
+ def init_with coder # :nodoc:
+ yaml_initialize coder.tag, coder.map
+ end
##
- # :attr_accessor: signing_key
- #
- # The key used to sign this gem. See Gem::Security for details.
+ # Specification constructor. Assigns the default values to the attributes
+ # and yields itself for further initialization. Optionally takes +name+ and
+ # +version+.
- attribute :signing_key, nil
+ def initialize name = nil, version = nil
+ @loaded = false
+ @loaded_from = nil
+ @original_platform = nil
- ##
- # :attr_accessor: cert_chain
- #
- # The certificate chain used to sign this gem. See Gem::Security for
- # details.
+ @@nil_attributes.each do |key|
+ instance_variable_set "@#{key}", nil
+ end
- attribute :cert_chain, []
+ @@non_nil_attributes.each do |key|
+ default = default_value(key)
+ value = case default
+ when Time, Numeric, Symbol, true, false, nil then default
+ else default.dup
+ end
- ##
- # :attr_accessor: post_install_message
- #
- # A message that gets displayed after the gem is installed
+ instance_variable_set "@#{key}", value
+ end
- attribute :post_install_message, nil
+ @new_platform = Gem::Platform::RUBY
- ##
- # :attr_accessor: authors
- #
- # The list of author names who wrote this gem.
- #
- # If you are providing multiple authors and multiple emails they should be
- # in the same order such that:
- #
- # Hash[*spec.authors.zip(spec.emails).flatten]
- #
- # Gives a hash of author name to email address.
+ self.name = name if name
+ self.version = version if version
- array_attribute :authors
+ yield self if block_given?
+ end
##
- # :attr_accessor: licenses
- #
- # The license(s) for the library. Each license must be a short name, no
- # more than 64 characters.
-
- array_attribute :licenses
+ # Duplicates array_attributes from +other_spec+ so state isn't shared.
- ##
- # :attr_accessor: files
- #
- # Files included in this gem. You cannot append to this accessor, you must
- # assign to it.
- #
- # Only add files you can require to this list, not directories, etc.
- #
- # Directories are automatically stripped from this list when building a gem,
- # other non-files cause an error.
+ def initialize_copy other_spec
+ other_ivars = other_spec.instance_variables
+ other_ivars = other_ivars.map { |ivar| ivar.intern } if # for 1.9
+ String === other_ivars.first
- array_attribute :files
+ self.class.array_attributes.each do |name|
+ name = :"@#{name}"
+ next unless other_ivars.include? name
- ##
- # :attr_accessor: test_files
- #
- # Test files included in this gem. You cannot append to this accessor, you
- # must assign to it.
+ begin
+ val = other_spec.instance_variable_get(name)
+ if val then
+ instance_variable_set name, val.dup
+ else
+ warn "WARNING: #{full_name} has an invalid nil value for #{name}"
+ end
+ rescue TypeError
+ e = Gem::FormatException.new \
+ "#{full_name} has an invalid value for #{name}"
- array_attribute :test_files
+ e.file_path = loaded_from
+ raise e
+ end
+ end
+ end
##
- # :attr_accessor: rdoc_options
- #
- # An ARGV style array of options to RDoc
+ # The directory that this gem was installed into.
+ # TODO: rename - horrible. this is the base_dir for a gem path
- array_attribute :rdoc_options
+ def installation_path
+ loaded_from && base_dir
+ end
##
- # :attr_accessor: extra_rdoc_files
- #
- # Extra files to add to RDoc such as README or doc/examples.txt
+ # Returns a string usable in Dir.glob to match all requirable paths
+ # for this spec.
- array_attribute :extra_rdoc_files
+ def lib_dirs_glob
+ dirs = if self.require_paths.size > 1 then
+ "{#{self.require_paths.join(',')}}"
+ else
+ self.require_paths.first
+ end
+
+ "#{self.full_gem_path}/#{dirs}"
+ end
##
- # :attr_accessor: executables
- #
- # Executables included in the gem.
+ # Files in the Gem under one of the require_paths
- array_attribute :executables
+ def lib_files
+ @files.select do |file|
+ require_paths.any? do |path|
+ file.index(path) == 0
+ end
+ end
+ end
##
- # :attr_accessor: extensions
- #
- # Extensions to build when installing the gem. See
- # Gem::Installer#build_extensions for valid values.
+ # Singular accessor for #licenses
- array_attribute :extensions
+ def license
+ val = licenses and val.first
+ end
##
- # :attr_accessor: requirements
- #
- # An array or things required by this gem. Not used by anything
- # presently.
+ # Singular accessor for #licenses
- array_attribute :requirements
+ def license=o
+ self.licenses = [o]
+ end
##
- # :attr_reader: dependencies
- #
- # A list of Gem::Dependency objects this gem depends on.
- #
- # Use #add_dependency or #add_development_dependency to add dependencies to
- # a gem.
+ # The license(s) for the library. Each license must be a short name, no
+ # more than 64 characters.
- array_attribute :dependencies
+ def licenses
+ @licenses ||= []
+ end
- read_only :dependencies
+ ##
+ # Set licenses to +licenses+, ensuring it is an array.
- # :section: Aliased gemspec attributes
+ def licenses= licenses
+ @licenses = Array licenses
+ end
##
- # Singular accessor for #executables
+ # Set the location a Specification was loaded from. +obj+ is converted
+ # to a String.
- attribute_alias_singular :executable, :executables
+ def loaded_from= path
+ @loaded_from = path.to_s
+ end
##
- # Singular accessor for #authors
+ # Sets the rubygems_version to the current RubyGems version.
- attribute_alias_singular :author, :authors
+ def mark_version
+ @rubygems_version = Gem::VERSION
+ end
##
- # Singular accessor for #licenses
+ # Return all files in this gem that match for +glob+.
- attribute_alias_singular :license, :licenses
+ def matches_for_glob glob # TODO: rename?
+ # TODO: do we need these?? Kill it
+ glob = File.join(self.lib_dirs_glob, glob)
+
+ Dir[glob].map { |f| f.untaint } # FIX our tests are brokey, run w/ SAFE=1
+ end
##
- # Singular accessor for #require_paths
+ # Warn about unknown attributes while loading a spec.
- attribute_alias_singular :require_path, :require_paths
+ def method_missing(sym, *a, &b) # :nodoc:
+ if @specification_version > CURRENT_SPECIFICATION_VERSION and
+ sym.to_s =~ /=$/ then
+ warn "ignoring #{sym} loading #{full_name}" if $DEBUG
+ else
+ super
+ end
+ end
##
- # Singular accessor for #test_files
+ # Normalize the list of files so that:
+ # * All file lists have redundancies removed.
+ # * Files referenced in the extra_rdoc_files are included in the package
+ # file list.
+
+ def normalize
+ if defined?(@extra_rdoc_files) and @extra_rdoc_files then
+ @extra_rdoc_files.uniq!
+ @files ||= []
+ @files.concat(@extra_rdoc_files)
+ end
- attribute_alias_singular :test_file, :test_files
+ @files = @files.uniq if @files
+ @extensions = @extensions.uniq if @extensions
+ @test_files = @test_files.uniq if @test_files
+ @executables = @executables.uniq if @executables
+ @extra_rdoc_files = @extra_rdoc_files.uniq if @extra_rdoc_files
+ end
##
- # has_rdoc is now ignored
+ # Returns the full name (name-version) of this gemspec using the original
+ # platform. For use with legacy gems.
- overwrite_accessor :has_rdoc do
- true
+ def original_name # :nodoc:
+ if platform == Gem::Platform::RUBY or platform.nil? then
+ "#{@name}-#{@version}"
+ else
+ "#{@name}-#{@version}-#{@original_platform}"
+ end
end
##
- # has_rdoc is now ignored
+ # Cruft. Use +platform+.
- overwrite_accessor :has_rdoc= do |value|
- @has_rdoc = true
+ def original_platform # :nodoc:
+ @original_platform ||= platform
end
- overwrite_accessor :version= do |version|
- @version = Gem::Version.create(version)
- self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
- return @version
- end
+ ##
+ # The platform this gem runs on. See Gem::Platform for details.
- overwrite_accessor :platform do
- @new_platform
+ def platform
+ @new_platform ||= Gem::Platform::RUBY
end
- overwrite_accessor :platform= do |platform|
+ ##
+ # The platform this gem runs on. See Gem::Platform for details.
+ #
+ # Setting this to any value other than Gem::Platform::RUBY or
+ # Gem::Platform::CURRENT is probably wrong.
+
+ def platform= platform
if @original_platform.nil? or
@original_platform == Gem::Platform::RUBY then
@original_platform = platform
@@ -1455,68 +1561,237 @@ class Gem::Specification
@new_platform
end
- overwrite_accessor :required_ruby_version= do |value|
- @required_ruby_version = Gem::Requirement.create(value)
- end
+ def pretty_print(q) # :nodoc:
+ q.group 2, 'Gem::Specification.new do |s|', 'end' do
+ q.breakable
+
+ # REFACTOR: each_attr - use in to_yaml as well
+ @@attributes.each do |attr_name|
+ current_value = self.send attr_name
+ if current_value != default_value(attr_name) or
+ self.class.required_attribute? attr_name then
+
+ q.text "s.#{attr_name} = "
- overwrite_accessor :required_rubygems_version= do |value|
- @required_rubygems_version = Gem::Requirement.create(value)
+ if attr_name == :date then
+ current_value = current_value.utc
+
+ q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})"
+ else
+ q.pp current_value
+ end
+
+ q.breakable
+ end
+ end
+ end
end
- overwrite_accessor :date= do |date|
- # We want to end up with a Time object with one-day resolution.
- # This is the cleanest, most-readable, faster-than-using-Date
- # way to do it.
- case date
- when String then
- @date = if /\A(\d{4})-(\d{2})-(\d{2})\Z/ =~ date then
- Time.local($1.to_i, $2.to_i, $3.to_i)
- else
- require 'time'
- Time.parse date
- end
- when Time then
- @date = Time.local(date.year, date.month, date.day)
- when Date then
- @date = Time.local(date.year, date.month, date.day)
- else
- @date = TODAY
+ ##
+ # Check the spec for possible conflicts and freak out if there are any.
+
+ def raise_if_conflicts
+ other = Gem.loaded_specs[self.name]
+
+ if other and self.version != other.version then
+ # This gem is already loaded. If the currently loaded gem is not in the
+ # list of candidate gems, then we have a version conflict.
+
+ msg = "can't activate #{full_name}, already activated #{other.full_name}"
+
+ e = Gem::LoadError.new msg
+ e.name = self.name
+ # TODO: e.requirement = dep.requirement
+
+ raise e
+ end
+
+ conf = self.conflicts
+
+ unless conf.empty? then
+ y = conf.map { |act,con|
+ "#{act.full_name} conflicts with #{con.join(", ")}"
+ }.join ", "
+
+ # TODO: improve message by saying who activated `con`
+
+ raise Gem::LoadError, "Unable to activate #{self.full_name}, because #{y}"
end
end
- overwrite_accessor :date do
- self.date = nil if @date.nil? # HACK Sets the default value for date
- @date
+ ##
+ # An ARGV style array of options to RDoc
+
+ def rdoc_options
+ @rdoc_options ||= []
end
- overwrite_accessor :summary= do |str|
- @summary = if str then
- str.strip.
- gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').
- gsub(/\n[ \t]*/, " ")
- end
+ ##
+ # Sets rdoc_options to +value+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def rdoc_options= options
+ # TODO: warn about setting instead of pushing
+ @rdoc_options = Array options
end
- overwrite_accessor :description= do |str|
- @description = str.to_s
+ ##
+ # Singular accessor for #require_paths
+
+ def require_path
+ val = require_paths and val.first
end
- overwrite_accessor :default_executable do
- begin
- if defined?(@default_executable) and @default_executable
- result = @default_executable
- elsif @executables and @executables.size == 1
- result = Array(@executables).first
- else
- result = nil
- end
- result
- rescue
- nil
+ ##
+ # Singular accessor for #require_paths
+
+ def require_path= path
+ self.require_paths = [path]
+ end
+
+ ##
+ # The version of ruby required by this gem
+
+ def required_ruby_version= req
+ @required_ruby_version = Gem::Requirement.create req
+ end
+
+ ##
+ # The RubyGems version required by this gem
+
+ def required_rubygems_version= req
+ @required_rubygems_version = Gem::Requirement.create req
+ end
+
+ ##
+ # An array or things required by this gem. Not used by anything
+ # presently.
+
+ def requirements
+ @requirements ||= []
+ end
+
+ ##
+ # Set requirements to +req+, ensuring it is an array. Don't
+ # use this, push onto the array instead.
+
+ def requirements= req
+ # TODO: warn about setting instead of pushing
+ @requirements = Array req
+ end
+
+ ##
+ # Returns the full path to this spec's ri directory.
+
+ def ri_dir
+ @ri_dir ||= File.join base_dir, 'ri', full_name
+ end
+
+ ##
+ # Return a string containing a Ruby code representation of the given
+ # object.
+
+ def ruby_code(obj)
+ case obj
+ when String then '%q{' + obj + '}'
+ when Array then '[' + obj.map { |x| ruby_code x }.join(", ") + ']'
+ when Gem::Version then obj.to_s.inspect
+ when Date then '%q{' + obj.strftime('%Y-%m-%d') + '}'
+ when Time then '%q{' + obj.strftime('%Y-%m-%d') + '}'
+ when Numeric then obj.inspect
+ when true, false, nil then obj.inspect
+ when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})"
+ when Gem::Requirement then "Gem::Requirement.new(#{obj.to_s.inspect})"
+ else raise Gem::Exception, "ruby_code case not handled: #{obj.class}"
end
end
- overwrite_accessor :test_files do
+ private :ruby_code
+
+ ##
+ # List of dependencies that will automatically be activated at runtime.
+
+ def runtime_dependencies
+ dependencies.select { |d| d.type == :runtime }
+ end
+
+ ##
+ # True if this gem has the same attributes as +other+.
+
+ def same_attributes? spec
+ @@attributes.all? { |name, default| self.send(name) == spec.send(name) }
+ end
+
+ private :same_attributes?
+
+ ##
+ # Checks if this specification meets the requirement of +dependency+.
+
+ def satisfies_requirement? dependency
+ return @name == dependency.name &&
+ dependency.requirement.satisfied_by?(@version)
+ end
+
+ ##
+ # Returns an object you can use to sort specifications in #sort_by.
+
+ def sort_obj
+ # TODO: this is horrible. Deprecate it.
+ [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
+ end
+
+ ##
+ # Returns the full path to the directory containing this spec's
+ # gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
+
+ def spec_dir
+ @spec_dir ||= File.join base_dir, "specifications"
+ end
+
+ ##
+ # Returns the full path to this spec's gemspec file.
+ # eg: /usr/local/lib/ruby/gems/1.8/specifications/mygem-1.0.gemspec
+
+ def spec_file
+ @spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
+ end
+
+ ##
+ # The default name of the gemspec. See also #file_name
+ #
+ # spec.spec_name # => "example-1.0.gemspec"
+
+ def spec_name
+ "#{full_name}.gemspec"
+ end
+
+ ##
+ # A short summary of this gem's description.
+
+ def summary= str
+ @summary = str.to_s.strip.
+ gsub(/(\w-)\n[ \t]*(\w)/, '\1\2').gsub(/\n[ \t]*/, " ") # so. weird.
+ end
+
+ ##
+ # Singular accessor for #test_files
+
+ def test_file
+ val = test_files and val.first
+ end
+
+ ##
+ # Singular accessor for #test_files
+
+ def test_file= file
+ self.test_files = [file]
+ end
+
+ ##
+ # Test files included in this gem. You cannot append to this accessor, you
+ # must assign to it.
+
+ def test_files
# Handle the possibility that we have @test_suite_file but not
# @test_files. This will happen when an old gem is loaded via
# YAML.
@@ -1531,41 +1806,323 @@ class Gem::Specification
end
end
- overwrite_accessor :files do
- # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
- @files = [@files,
- @test_files,
- add_bindir(@executables),
- @extra_rdoc_files,
- @extensions,
- ].flatten.uniq.compact
+ ##
+ # Set test_files to +files+, ensuring it is an array.
+
+ def test_files= files
+ @test_files = Array files
end
- def conflicts
- conflicts = {}
- Gem.loaded_specs.values.each do |spec|
- bad = self.runtime_dependencies.find_all { |dep|
- spec.name == dep.name and not spec.satisfies_requirement? dep
- }
+ def test_suite_file # :nodoc:
+ # TODO: deprecate
+ test_files.first
+ end
- conflicts[spec] = bad unless bad.empty?
+ def test_suite_file= file # :nodoc:
+ # TODO: deprecate
+ @test_files = [] unless defined? @test_files
+ @test_files << file
+ end
+
+ ##
+ # Returns a Ruby code representation of this specification, such that it can
+ # be eval'ed and reconstruct the same specification later. Attributes that
+ # still have their default values are omitted.
+
+ def to_ruby
+ mark_version
+ result = []
+ result << "# -*- encoding: utf-8 -*-"
+ result << nil
+ result << "Gem::Specification.new do |s|"
+
+ result << " s.name = #{ruby_code name}"
+ result << " s.version = #{ruby_code version}"
+ unless platform.nil? or platform == Gem::Platform::RUBY then
+ result << " s.platform = #{ruby_code original_platform}"
end
- conflicts
+ result << ""
+ result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version="
+
+ handled = [
+ :dependencies,
+ :name,
+ :platform,
+ :required_rubygems_version,
+ :specification_version,
+ :version,
+ :has_rdoc,
+ :default_executable,
+ ]
+
+ @@attributes.each do |attr_name|
+ next if handled.include? attr_name
+ current_value = self.send(attr_name)
+ if current_value != default_value(attr_name) or
+ self.class.required_attribute? attr_name then
+ result << " s.#{attr_name} = #{ruby_code current_value}"
+ end
+ end
+
+ result << nil
+ result << " if s.respond_to? :specification_version then"
+ result << " s.specification_version = #{specification_version}"
+ result << nil
+
+ result << " if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then"
+
+ dependencies.each do |dep|
+ req = dep.requirements_list.inspect
+ dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK
+ result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>, #{req})"
+ end
+
+ result << " else"
+
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
+
+ result << ' end'
+
+ result << " else"
+ dependencies.each do |dep|
+ version_reqs_param = dep.requirements_list.inspect
+ result << " s.add_dependency(%q<#{dep.name}>, #{version_reqs_param})"
+ end
+ result << " end"
+
+ result << "end"
+ result << nil
+
+ result.join "\n"
end
- def traverse trail = [], &b
+ ##
+ # Returns a Ruby lighter-weight code representation of this specification,
+ # used for indexing only.
+ #
+ # See #to_ruby.
+
+ def to_ruby_for_cache
+ for_cache.to_ruby
+ end
+
+ def to_s # :nodoc:
+ "#<Gem::Specification name=#{@name} version=#{@version}>"
+ end
+
+ def to_yaml(opts = {}) # :nodoc:
+ if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
+ super.gsub(/ !!null \n/, " \n")
+ else
+ YAML.quick_emit object_id, opts do |out|
+ out.map taguri, to_yaml_style do |map|
+ encode_with map
+ end
+ end
+ end
+ end
+
+ ##
+ # Recursively walk dependencies of this spec, executing the +block+ for each
+ # hop.
+
+ def traverse trail = [], &block
trail = trail + [self]
runtime_dependencies.each do |dep|
- dep_specs = Gem.source_index.search dep, true
- dep_specs.each do |dep_spec|
- b[self, dep, dep_spec, trail + [dep_spec]]
- dep_spec.traverse(trail, &b) unless
+ dep.to_specs.each do |dep_spec|
+ block[self, dep, dep_spec, trail + [dep_spec]]
+ dep_spec.traverse(trail, &block) unless
trail.map(&:name).include? dep_spec.name
end
end
end
- def dependent_specs
- runtime_dependencies.map { |dep| Gem.source_index.search dep, true }.flatten
+ ##
+ # Checks that the specification contains all required fields, and does a
+ # very basic sanity check.
+ #
+ # Raises InvalidSpecificationException if the spec does not pass the
+ # checks..
+
+ def validate packaging = true
+ require 'rubygems/user_interaction'
+ extend Gem::UserInteraction
+ normalize
+
+ nil_attributes = self.class.non_nil_attributes.find_all do |name|
+ instance_variable_get("@#{name}").nil?
+ end
+
+ unless nil_attributes.empty? then
+ raise Gem::InvalidSpecificationException,
+ "#{nil_attributes.join ', '} must not be nil"
+ end
+
+ if packaging and rubygems_version != Gem::VERSION then
+ raise Gem::InvalidSpecificationException,
+ "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
+ end
+
+ @@required_attributes.each do |symbol|
+ unless self.send symbol then
+ raise Gem::InvalidSpecificationException,
+ "missing value for attribute #{symbol}"
+ end
+ end
+
+ unless String === name then
+ raise Gem::InvalidSpecificationException,
+ "invalid value for attribute name: \"#{name.inspect}\""
+ end
+
+ if require_paths.empty? then
+ raise Gem::InvalidSpecificationException,
+ 'specification must have at least one require_path'
+ end
+
+ @files.delete_if { |x| File.directory?(x) }
+ @test_files.delete_if { |x| File.directory?(x) }
+ @executables.delete_if { |x| File.directory?(File.join(@bindir, x)) }
+ @extra_rdoc_files.delete_if { |x| File.directory?(x) }
+ @extensions.delete_if { |x| File.directory?(x) }
+
+ non_files = files.reject { |x| File.file?(x) }
+
+ unless not packaging or non_files.empty? then
+ raise Gem::InvalidSpecificationException,
+ "[\"#{non_files.join "\", \""}\"] are not files"
+ end
+
+ unless specification_version.is_a?(Fixnum)
+ raise Gem::InvalidSpecificationException,
+ 'specification_version must be a Fixnum (did you mean version?)'
+ end
+
+ case platform
+ when Gem::Platform, Gem::Platform::RUBY then # ok
+ else
+ raise Gem::InvalidSpecificationException,
+ "invalid platform #{platform.inspect}, see Gem::Platform"
+ end
+
+ self.class.array_attributes.each do |field|
+ val = self.send field
+ klass = case field
+ when :dependencies
+ Gem::Dependency
+ else
+ String
+ end
+
+ unless Array === val and val.all? { |x| x.kind_of?(klass) } then
+ raise(Gem::InvalidSpecificationException,
+ "#{field} must be an Array of #{klass}")
+ end
+ end
+
+ [:authors].each do |field|
+ val = self.send field
+ raise Gem::InvalidSpecificationException, "#{field} may not be empty" if
+ val.empty?
+ end
+
+ licenses.each { |license|
+ if license.length > 64
+ raise Gem::InvalidSpecificationException,
+ "each license must be 64 characters or less"
+ end
+ }
+
+ # reject lazy developers:
+
+ lazy = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
+
+ unless authors.grep(/FI XME|TO DO/x).empty? then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not an author"
+ end
+
+ unless Array(email).grep(/FI XME|TO DO/x).empty? then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not an email"
+ end
+
+ if description =~ /FI XME|TO DO/x then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not a description"
+ end
+
+ if summary =~ /FI XME|TO DO/x then
+ raise Gem::InvalidSpecificationException, "#{lazy} is not a summary"
+ end
+
+ if homepage and not homepage.empty? and
+ homepage !~ /\A[a-z][a-z\d+.-]*:/i then
+ raise Gem::InvalidSpecificationException,
+ "\"#{homepage}\" is not a URI"
+ end
+
+ # Warnings
+
+ %w[author description email homepage summary].each do |attribute|
+ value = self.send attribute
+ alert_warning "no #{attribute} specified" if value.nil? or value.empty?
+ end
+
+ if description == summary then
+ alert_warning 'description and summary are identical'
+ end
+
+ # TODO: raise at some given date
+ alert_warning "deprecated autorequire specified" if autorequire
+
+ executables.each do |executable|
+ executable_path = File.join(bindir, executable)
+ shebang = File.read(executable_path, 2) == '#!'
+
+ alert_warning "#{executable_path} is missing #! line" unless shebang
+ end
+
+ true
end
+
+ ##
+ # Set the version to +version+, potentially also setting
+ # required_rubygems_version if +version+ indicates it is a
+ # prerelease.
+
+ def version= version
+ @version = Gem::Version.create(version)
+ self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
+ return @version
+ end
+
+ # FIX: have this handle the platform/new_platform/original_platform bullshit
+ def yaml_initialize(tag, vals) # :nodoc:
+ vals.each do |ivar, val|
+ instance_variable_set "@#{ivar}", val
+ end
+
+ @original_platform = @platform # for backwards compatibility
+ self.platform = Gem::Platform.new @platform
+ end
+
+ extend Deprecate
+
+ deprecate :test_suite_file, :test_file, 2011, 10
+ deprecate :test_suite_file=, :test_file=, 2011, 10
+ deprecate :loaded, :activated, 2011, 10
+ deprecate :loaded?, :activated?, 2011, 10
+ deprecate :loaded=, :activated=, 2011, 10
+ deprecate :installation_path, :base_dir, 2011, 10
+ deprecate :cache_gem, :cache_file, 2011, 10
+ # TODO:
+ # deprecate :has_rdoc, :none, 2011, 10
+ # deprecate :has_rdoc?, :none, 2011, 10
+ # deprecate :has_rdoc=, :none, 2011, 10
+ # deprecate :default_executable, :none, 2011, 10
+ # deprecate :default_executable=, :none, 2011, 10
+ # deprecate :spec_name, :spec_file, 2011, 10
+ # deprecate :file_name, :cache_file, 2011, 10
+ # deprecate :full_gem_path, :cache_file, 2011, 10
end
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index 895002df05..3b26d31611 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -17,6 +17,7 @@ begin
rescue Gem::LoadError
end
+require "rubygems/deprecate"
require 'minitest/autorun'
require 'fileutils'
require 'tmpdir'
@@ -25,6 +26,7 @@ require 'rubygems/package'
require 'rubygems/test_utilities'
require 'pp'
require 'zlib'
+require 'pathname'
Gem.load_yaml
require 'rubygems/mock_gem_ui'
@@ -44,6 +46,8 @@ module Gem
# requiring 'rubygems/test_case'
def self.source_index=(si)
+ raise "This method is not supported"
+ Gem::Specification.reset if si # HACK
@@source_index = si
end
@@ -82,12 +86,24 @@ end
class Gem::TestCase < MiniTest::Unit::TestCase
+ # TODO: move to minitest
+ def assert_path_exists path, msg = nil
+ msg = message(msg) { "Expected path '#{path}' to exist" }
+ assert File.exist?(path), msg
+ end
+
+ # TODO: move to minitest
+ def refute_path_exists path, msg = nil
+ msg = message(msg) { "Expected path '#{path}' to not exist" }
+ refute File.exist?(path), msg
+ end
+
include Gem::DefaultUserInteraction
undef_method :default_test if instance_methods.include? 'default_test' or
instance_methods.include? :default_test
- @@project_dir = Dir.pwd unless defined?(@@project_dir)
+ @@project_dir = Dir.pwd
##
# #setup prepares a sandboxed location to install gems. All installs are
@@ -106,14 +122,15 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@orig_gem_home = ENV['GEM_HOME']
@orig_gem_path = ENV['GEM_PATH']
- @current_dir = Dir.pwd
@ui = Gem::MockGemUi.new
+
tmpdir = nil
Dir.chdir Dir.tmpdir do tmpdir = Dir.pwd end # HACK OSX /private/tmp
+
if ENV['KEEP_FILES'] then
- @tempdir = File.join tmpdir, "test_rubygems_#{$$}.#{Time.now.to_i}"
+ @tempdir = File.join(tmpdir, "test_rubygems_#{$$}.#{Time.now.to_i}")
else
- @tempdir = File.join tmpdir, "test_rubygems_#{$$}"
+ @tempdir = File.join(tmpdir, "test_rubygems_#{$$}")
end
@tempdir.untaint
@gemhome = File.join @tempdir, 'gemhome'
@@ -126,6 +143,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem.ensure_gem_subdirectories @gemhome
+ @orig_LOAD_PATH = $LOAD_PATH.dup
+ $LOAD_PATH.map! { |s| File.expand_path s }
+
Dir.chdir @tempdir
@orig_ENV_HOME = ENV['HOME']
@@ -136,6 +156,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
FileUtils.mkdir_p @userhome
Gem.use_paths(@gemhome)
+
Gem.loaded_specs.clear
Gem.unresolved_deps.clear
@@ -191,8 +212,6 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem.pre_uninstall do |uninstaller|
@pre_uninstall_hook_arg = uninstaller
end
-
- @orig_LOAD_PATH = $LOAD_PATH.dup
end
##
@@ -209,15 +228,13 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem::RemoteFetcher.fetcher = nil
end
- Dir.chdir @current_dir
+ Dir.chdir @@project_dir
FileUtils.rm_rf @tempdir unless ENV['KEEP_FILES']
ENV['GEM_HOME'] = @orig_gem_home
ENV['GEM_PATH'] = @orig_gem_path
- Gem.clear_paths
-
_ = @orig_ruby
Gem.class_eval { @ruby = _ } if _
@@ -240,7 +257,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
end
- gem = File.join(@tempdir, spec.file_name).untaint
+ gem = File.join(@tempdir, File.basename(spec.cache_file)).untaint
Gem::Installer.new(gem, :wrappers => true).install
end
@@ -250,9 +267,19 @@ class Gem::TestCase < MiniTest::Unit::TestCase
def uninstall_gem spec
require 'rubygems/uninstaller'
- uninstaller = Gem::Uninstaller.new spec.name, :executables => true,
- :user_install => true
- uninstaller.uninstall
+ Gem::Uninstaller.new(spec.name,
+ :executables => true, :user_install => true).uninstall
+ end
+
+ ##
+ # creates a temporary directory with hax
+
+ def create_tmpdir
+ tmpdir = nil
+ Dir.chdir Dir.tmpdir do tmpdir = Dir.pwd end # HACK OSX /private/tmp
+ tmpdir = File.join tmpdir, "test_rubygems_#{$$}"
+ FileUtils.mkdir_p tmpdir
+ return tmpdir
end
##
@@ -285,7 +312,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Writes a binary file to +path+ which is relative to +@gemhome+
def write_file(path)
- path = File.join @gemhome, path
+ path = File.join @gemhome, path unless Pathname.new(path).absolute?
dir = File.dirname path
FileUtils.mkdir_p dir
@@ -296,6 +323,10 @@ class Gem::TestCase < MiniTest::Unit::TestCase
path
end
+ def all_spec_names
+ Gem::Specification.map(&:full_name)
+ end
+
##
# Creates a Gem::Specification with a minimum of extra work. +name+ and
# +version+ are the gem's name and version, platform, author, email,
@@ -317,26 +348,27 @@ class Gem::TestCase < MiniTest::Unit::TestCase
s.author = 'A User'
s.email = 'example@example.com'
s.homepage = 'http://example.com'
- s.has_rdoc = true
s.summary = "this is a summary"
s.description = "This is a test description"
yield(s) if block_given?
end
- path = File.join "specifications", spec.spec_name
- written_path = write_file path do |io|
- io.write(spec.to_ruby)
+ Gem::Specification.map # HACK: force specs to (re-)load before we write
+
+ written_path = write_file spec.spec_file do |io|
+ io.write spec.to_ruby_for_cache
end
- spec.loaded_from = written_path
+ spec.loaded_from = spec.loaded_from = written_path
- Gem.source_index.add_spec spec
+ Gem::Specification.add_spec spec.for_cache
return spec
end
def quick_spec name, version = '2'
+ # TODO: deprecate
require 'rubygems/specification'
spec = Gem::Specification.new do |s|
@@ -346,16 +378,15 @@ class Gem::TestCase < MiniTest::Unit::TestCase
s.author = 'A User'
s.email = 'example@example.com'
s.homepage = 'http://example.com'
- s.has_rdoc = true
s.summary = "this is a summary"
s.description = "This is a test description"
yield(s) if block_given?
end
- spec.loaded_from = @gemhome
+ spec.loaded_from = spec.spec_file
- Gem.source_index.add_spec spec
+ Gem::Specification.add_spec spec
return spec
end
@@ -365,7 +396,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# 'cache'</tt>. Automatically creates files based on +spec.files+
def util_build_gem(spec)
- dir = File.join(@gemhome, 'gems', spec.full_name)
+ dir = spec.gem_dir
FileUtils.mkdir_p dir
Dir.chdir dir do
@@ -379,8 +410,8 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem::Builder.new(spec).build
end
- FileUtils.mv spec.file_name,
- Gem.cache_gem("#{spec.original_name}.gem")
+ cache = spec.cache_file
+ FileUtils.mv File.basename(cache), cache
end
end
@@ -388,19 +419,16 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Removes all installed gems from +@gemhome+.
def util_clear_gems
- FileUtils.rm_rf File.join(@gemhome, 'gems')
- FileUtils.rm_rf File.join(@gemhome, 'specifications')
- Gem.source_index.refresh!
+ FileUtils.rm_rf File.join(@gemhome, "gems") # TODO: use Gem::Dirs
+ FileUtils.rm_rf File.join(@gemhome, "specifications")
+ Gem::Specification.reset
end
##
# Install the provided specs
def install_specs(*specs)
- specs.each do |spec|
- # TODO: inverted responsibility
- Gem.source_index.add_spec spec
- end
+ Gem::Specification.add_specs(*specs)
Gem.searcher = nil
end
@@ -409,19 +437,42 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# up properly. Use this instead of util_spec and util_gem.
def new_spec name, version, deps = nil, *files
- # TODO: unfactor and deprecate util_gem and util_spec
- spec, = unless files.empty? then
- util_gem name, version do |s|
- Array(deps).each do |n,v|
- s.add_dependency n, v
- end
- s.files.push(*files)
- end
- else
- util_spec name, version, deps
- end
- spec.loaded_from = File.join @gemhome, 'specifications', spec.spec_name
- spec.loaded = false
+ require 'rubygems/specification'
+
+ spec = Gem::Specification.new do |s|
+ s.platform = Gem::Platform::RUBY
+ s.name = name
+ s.version = version
+ s.author = 'A User'
+ s.email = 'example@example.com'
+ s.homepage = 'http://example.com'
+ s.summary = "this is a summary"
+ s.description = "This is a test description"
+
+ Array(deps).each do |n, req|
+ s.add_dependency n, (req || '>= 0')
+ end
+
+ s.files.push(*files) unless files.empty?
+
+ yield s if block_given?
+ end
+
+ spec.loaded_from = spec.spec_file
+
+ unless files.empty? then
+ write_file spec.spec_file do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ util_build_gem spec
+
+ cache_file = File.join @tempdir, 'gems', "#{spec.full_name}.gem"
+ FileUtils.mkdir_p File.dirname cache_file
+ FileUtils.mv spec.cache_file, cache_file
+ FileUtils.rm spec.spec_file
+ end
+
spec
end
@@ -429,6 +480,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Creates a spec with +name+, +version+ and +deps+.
def util_spec(name, version, deps = nil, &block)
+ # TODO: deprecate
raise "deps or block, not both" if deps and block
if deps then
@@ -449,6 +501,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# location are returned.
def util_gem(name, version, deps = nil, &block)
+ # TODO: deprecate
raise "deps or block, not both" if deps and block
if deps then
@@ -465,11 +518,10 @@ class Gem::TestCase < MiniTest::Unit::TestCase
cache_file = File.join @tempdir, 'gems', "#{spec.original_name}.gem"
FileUtils.mkdir_p File.dirname cache_file
- FileUtils.mv Gem.cache_gem("#{spec.original_name}.gem"), cache_file
- FileUtils.rm File.join(@gemhome, 'specifications', spec.spec_name)
+ FileUtils.mv spec.cache_file, cache_file
+ FileUtils.rm spec.spec_file
spec.loaded_from = nil
- spec.loaded = false
[spec, cache_file]
end
@@ -517,8 +569,8 @@ class Gem::TestCase < MiniTest::Unit::TestCase
This line is really, really long. So long, in fact, that it is more than eighty characters long! The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters. Without the wrapping, the text might not look good in the RSS feed.
Also, a list:
- * An entry that's actually kind of sort
- * an entry that's really long, which will probably get wrapped funny. That's ok, somebody wasn't thinking straight when they made it more than eighty characters.
+ * An entry that\'s actually kind of sort
+ * an entry that\'s really long, which will probably get wrapped funny. That's ok, somebody wasn't thinking straight when they made it more than eighty characters.
DESC
end
@@ -557,9 +609,7 @@ Also, a list:
util_build_gem spec
end
- FileUtils.rm_r File.join(@gemhome, 'gems', @pl1.original_name)
-
- Gem.source_index = nil
+ FileUtils.rm_r File.join(@gemhome, "gems", @pl1.original_name)
end
##
@@ -589,6 +639,7 @@ Also, a list:
@fetcher = Gem::FakeFetcher.new
util_make_gems(prerelease)
+ Gem::Specification.reset
@all_gems = [@a1, @a2, @a3a, @a_evil9, @b2, @c1_2].sort
@all_gem_names = @all_gems.map { |gem| gem.full_name }
@@ -596,14 +647,6 @@ Also, a list:
gem_names = [@a1.full_name, @a2.full_name, @a3a.full_name, @b2.full_name]
@gem_names = gem_names.sort.join("\n")
- @source_index = Gem::SourceIndex.new
- @source_index.add_spec @a1
- @source_index.add_spec @a2
- @source_index.add_spec @a3a
- @source_index.add_spec @a_evil9
- @source_index.add_spec @c1_2
- @source_index.add_spec @a2_pre if prerelease
-
Gem::RemoteFetcher.fetcher = @fetcher
end
@@ -612,37 +655,42 @@ Also, a list:
# Best used with +@all_gems+ from #util_setup_fake_fetcher.
def util_setup_spec_fetcher(*specs)
- specs = Hash[*specs.map { |spec| [spec.full_name, spec] }.flatten]
- si = Gem::SourceIndex.new specs
+ specs -= Gem::Specification._all
+ Gem::Specification.add_specs(*specs)
spec_fetcher = Gem::SpecFetcher.fetcher
+ prerelease, _ = Gem::Specification.partition { |spec|
+ spec.version.prerelease?
+ }
+
spec_fetcher.specs[@uri] = []
- si.gems.sort_by { |_, spec| spec }.each do |_, spec|
+ Gem::Specification.each do |spec|
spec_tuple = [spec.name, spec.version, spec.original_platform]
spec_fetcher.specs[@uri] << spec_tuple
end
spec_fetcher.latest_specs[@uri] = []
- si.latest_specs.sort.each do |spec|
+ Gem::Specification.latest_specs.each do |spec|
spec_tuple = [spec.name, spec.version, spec.original_platform]
spec_fetcher.latest_specs[@uri] << spec_tuple
end
spec_fetcher.prerelease_specs[@uri] = []
- si.prerelease_specs.sort.each do |spec|
+ prerelease.each do |spec|
spec_tuple = [spec.name, spec.version, spec.original_platform]
spec_fetcher.prerelease_specs[@uri] << spec_tuple
end
- (si.gems.merge si.prerelease_gems).sort_by { |_,spec| spec }.each do |_, spec|
- path = "#{@gem_repo}quick/Marshal.#{Gem.marshal_version}/#{spec.original_name}.gemspec.rz"
+ v = Gem.marshal_version
+ Gem::Specification.each do |spec|
+ path = "#{@gem_repo}quick/Marshal.#{v}/#{spec.original_name}.gemspec.rz"
data = Marshal.dump spec
data_deflate = Zlib::Deflate.deflate data
@fetcher.data[path] = data_deflate
end
- si
+ nil # force errors
end
##
diff --git a/lib/rubygems/test_utilities.rb b/lib/rubygems/test_utilities.rb
index 43f905f017..1e43fe5eb9 100644
--- a/lib/rubygems/test_utilities.rb
+++ b/lib/rubygems/test_utilities.rb
@@ -103,8 +103,8 @@ class Gem::FakeFetcher
end
def download spec, source_uri, install_dir = Gem.dir
- name = spec.file_name
- path = Gem.cache_gem(name, install_dir)
+ name = File.basename spec.cache_file
+ path = File.join install_dir, "cache", name
Gem.ensure_gem_subdirectories install_dir
@@ -120,7 +120,8 @@ class Gem::FakeFetcher
end
def download_to_cache dependency
- found = Gem::SpecFetcher.fetcher.fetch dependency
+ found = Gem::SpecFetcher.fetcher.fetch dependency, true, true,
+ dependency.prerelease?
return if found.empty?
diff --git a/lib/rubygems/text.rb b/lib/rubygems/text.rb
index ddf1e820a7..a4642f961d 100644
--- a/lib/rubygems/text.rb
+++ b/lib/rubygems/text.rb
@@ -21,7 +21,7 @@ module Gem::Text
while work.length > wrap do
if work =~ /^(.{0,#{wrap}})[ \n]/ then
- result << $1
+ result << $1.rstrip
work.slice!(0, $&.length)
else
result << work.slice!(0, wrap)
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index 02976866b1..1e370c38ee 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -48,26 +48,23 @@ class Gem::Uninstaller
# Constructs an uninstaller that will uninstall +gem+
def initialize(gem, options = {})
- @gem = gem
- @version = options[:version] || Gem::Requirement.default
- gem_home = options[:install_dir] || Gem.dir
- @gem_home = File.expand_path gem_home
+ @gem = gem
+ @version = options[:version] || Gem::Requirement.default
+ @gem_home = File.expand_path(options[:install_dir] || Gem.dir)
@force_executables = options[:executables]
- @force_all = options[:all]
- @force_ignore = options[:ignore]
- @bin_dir = options[:bin_dir]
+ @force_all = options[:all]
+ @force_ignore = options[:ignore]
+ @bin_dir = options[:bin_dir]
@format_executable = options[:format_executable]
# only add user directory if install_dir is not set
@user_install = false
@user_install = options[:user_install] unless options[:install_dir]
- spec_dir = File.join @gem_home, 'specifications'
- @source_index = Gem::SourceIndex.from_gems_in spec_dir
-
if @user_install then
- user_dir = File.join Gem.user_dir, 'specifications'
- @user_index = Gem::SourceIndex.from_gems_in user_dir
+ Gem.use_paths Gem.user_dir, @gem_home
+ else
+ Gem.use_paths @gem_home
end
end
@@ -76,14 +73,13 @@ class Gem::Uninstaller
# directory, and the cached .gem file.
def uninstall
- list = @source_index.find_name @gem, @version
- list += @user_index.find_name @gem, @version if @user_install
+ list = Gem::Specification.find_all_by_name(@gem, @version)
if list.empty? then
raise Gem::InstallError, "cannot uninstall, check `gem list -d #{@gem}`"
elsif list.size > 1 and @force_all then
- remove_all list.dup
+ remove_all list
elsif list.size > 1 then
gem_names = list.collect {|gem| gem.full_name} + ["All versions"]
@@ -92,21 +88,21 @@ class Gem::Uninstaller
_, index = choose_from_list "Select gem to uninstall:", gem_names
if index == list.size then
- remove_all list.dup
+ remove_all list
elsif index >= 0 && index < list.size then
- uninstall_gem list[index], list.dup
+ uninstall_gem list[index]
else
say "Error: must enter a number [1-#{list.size+1}]"
end
else
- uninstall_gem list.first, list.dup
+ uninstall_gem list.first
end
end
##
# Uninstalls gem +spec+
- def uninstall_gem(spec, specs)
+ def uninstall_gem(spec)
@spec = spec
unless dependencies_ok? spec
@@ -121,7 +117,7 @@ class Gem::Uninstaller
end
remove_executables @spec
- remove @spec, specs
+ remove @spec
Gem.post_uninstall_hooks.each do |hook|
hook.call self
@@ -137,10 +133,8 @@ class Gem::Uninstaller
def remove_executables(spec)
return if spec.nil? or spec.executables.empty?
- bindir = @bin_dir ? @bin_dir : Gem.bindir(spec.installation_path)
-
- list = @source_index.find_name(spec.name).delete_if { |s|
- s.version == spec.version
+ list = Gem::Specification.find_all { |s|
+ s.name == spec.name && s.version != spec.version
}
executables = spec.executables.clone
@@ -165,6 +159,8 @@ class Gem::Uninstaller
unless remove then
say "Executables and scripts will remain installed."
else
+ bindir = @bin_dir || Gem.bindir(spec.base_dir)
+
raise Gem::FilePermissionError, bindir unless File.writable? bindir
spec.executables.each do |exe_name|
@@ -181,7 +177,7 @@ class Gem::Uninstaller
# NOTE: removes uninstalled gems from +list+.
def remove_all(list)
- list.dup.each { |spec| uninstall_gem spec, list }
+ list.each { |spec| uninstall_gem spec }
end
##
@@ -191,7 +187,7 @@ class Gem::Uninstaller
# Warning: this method modifies the +list+ parameter. Once it has
# uninstalled a gem, it is removed from that list.
- def remove(spec, list)
+ def remove(spec)
unless path_ok?(@gem_home, spec) or
(@user_install and path_ok?(Gem.user_dir, spec)) then
e = Gem::GemNotInHomeException.new \
@@ -201,28 +197,27 @@ class Gem::Uninstaller
raise e
end
- raise Gem::FilePermissionError, spec.installation_path unless
- File.writable?(spec.installation_path)
+ raise Gem::FilePermissionError, spec.base_dir unless
+ File.writable?(spec.base_dir)
FileUtils.rm_rf spec.full_gem_path
- original_platform_name = [
- spec.name, spec.version, spec.original_platform].join '-'
+ # TODO: should this be moved to spec?... I vote eww (also exists in docmgr)
+ old_platform_name = [spec.name,
+ spec.version,
+ spec.original_platform].join '-'
- spec_dir = File.join spec.installation_path, 'specifications'
- gemspec = File.join spec_dir, spec.spec_name
+ gemspec = spec.spec_file
unless File.exist? gemspec then
- gemspec = File.join spec_dir, "#{original_platform_name}.gemspec"
+ gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec")
end
FileUtils.rm_rf gemspec
- gem = Gem.cache_gem(spec.file_name, spec.installation_path)
-
- unless File.exist? gem then
- gem = Gem.cache_gem("#{original_platform_name}.gem", spec.installation_path)
- end
+ gem = spec.cache_file
+ gem = File.join(spec.cache_dir, "#{old_platform_name}.gem") unless
+ File.exist? gem
FileUtils.rm_rf gem
@@ -230,14 +225,14 @@ class Gem::Uninstaller
say "Successfully uninstalled #{spec.full_name}"
- list.delete spec
+ Gem::Specification.remove_spec spec
end
##
# Is +spec+ in +gem_dir+?
def path_ok?(gem_dir, spec)
- full_path = File.join gem_dir, 'gems', spec.full_name
+ full_path = File.join gem_dir, 'gems', spec.full_name
original_path = File.join gem_dir, 'gems', spec.original_name
full_path == spec.full_gem_path || original_path == spec.full_gem_path
@@ -246,8 +241,7 @@ class Gem::Uninstaller
def dependencies_ok?(spec)
return true if @force_ignore
- deplist = Gem::DependencyList.from_source_index @source_index
- deplist.add(*@user_index.gems.values) if @user_install
+ deplist = Gem::DependencyList.from_specs
deplist.ok_to_remove?(spec.full_name)
end
@@ -255,11 +249,13 @@ class Gem::Uninstaller
msg = ['']
msg << 'You have requested to uninstall the gem:'
msg << "\t#{spec.full_name}"
- spec.dependent_gems.each do |gem,dep,satlist|
+
+ spec.dependent_gems.each do |dep_spec, dep, satlist|
msg <<
- ("#{gem.name}-#{gem.version} depends on " +
- "[#{dep.name} (#{dep.requirement})]")
+ ("#{dep_spec.name}-#{dep_spec.version} depends on " +
+ "[#{dep.name} (#{dep.requirement})]")
end
+
msg << 'If you remove this gems, one or more dependencies will not be met.'
msg << 'Continue with Uninstall?'
return ask_yes_no(msg.join("\n"), true)
@@ -272,7 +268,4 @@ class Gem::Uninstaller
filename
end
end
-
-
end
-
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 87d13dab26..1dbcf38eee 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -90,44 +90,40 @@ module Gem::UserInteraction
include Gem::DefaultUserInteraction
- ##
- # :method: alert
+ def alert(*args)
+ ui.alert(*args)
+ end
- ##
- # :method: alert_error
+ def alert_error(*args)
+ ui.alert_error(*args)
+ end
- ##
- # :method: alert_warning
+ def alert_warning(*args)
+ ui.alert_warning(*args)
+ end
- ##
- # :method: ask
+ def ask(*args)
+ ui.ask(*args)
+ end
- ##
- # :method: ask_yes_no
+ def ask_for_password(*args)
+ ui.ask_for_password(*args)
+ end
- ##
- # :method: choose_from_list
+ def ask_yes_no(*args)
+ ui.ask_yes_no(*args)
+ end
- ##
- # :method: say
+ def choose_from_list(*args)
+ ui.choose_from_list(*args)
+ end
- ##
- # :method: terminate_interaction
-
- [:alert,
- :alert_error,
- :alert_warning,
- :ask,
- :ask_for_password,
- :ask_yes_no,
- :choose_from_list,
- :say,
- :terminate_interaction ].each do |methname|
- class_eval %{
- def #{methname}(*args)
- ui.#{methname}(*args)
- end
- }, __FILE__, __LINE__
+ def say(*args)
+ ui.say(*args)
+ end
+
+ def terminate_interaction(*args)
+ ui.terminate_interaction(*args)
end
end