* lib/rubygems/commands/query_command.rb: Refactored to improve

maintainability.
* test/rubygems/test_gem_commands_query_command.rb:  Note default gems
  in gem list details.

* lib/rubygems/uninstaller.rb:  Detect all gems for uninstallation.
  This allows duplicate installs of default gems to be removed.
* lib/rubygems/specification.rb:  Allow use of ::each_spec.
* lib/rubygems/test_case.rb:  Added install_default_gems.
* test/rubygems/test_gem_commands_uninstall_command.rb:  Moved test
  down to the uninstaller tests.
* test/rubygems/test_gem_uninstaller.rb:  Test for uninstallation of
  default gems and duplicate default gems.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38461 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2012-12-19 07:19:10 +00:00
parent 1b2be1f8ca
commit eddc0685f2
8 changed files with 242 additions and 125 deletions

View File

@ -1,3 +1,19 @@
Wed Dec 19 16:18:22 2012 Eric Hodel <drbrain@segment7.net>
* lib/rubygems/commands/query_command.rb: Refactored to improve
maintainability.
* test/rubygems/test_gem_commands_query_command.rb: Note default gems
in gem list details.
* lib/rubygems/uninstaller.rb: Detect all gems for uninstallation.
This allows duplicate installs of default gems to be removed.
* lib/rubygems/specification.rb: Allow use of ::each_spec.
* lib/rubygems/test_case.rb: Added install_default_gems.
* test/rubygems/test_gem_commands_uninstall_command.rb: Moved test
down to the uninstaller tests.
* test/rubygems/test_gem_uninstaller.rb: Test for uninstallation of
default gems and duplicate default gems.
Wed Dec 19 15:23:50 2012 Eric Hodel <drbrain@segment7.net>
* doc/syntax/methods.rdoc: Add () around keyword arguments example for

View File

@ -162,12 +162,18 @@ class Gem::Commands::QueryCommand < Gem::Command
n.downcase
end
output_versions output, versions
say output.join(options[:details] ? "\n\n" : "\n")
end
def output_versions output, versions
versions.each do |gem_name, matching_tuples|
matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse
platforms = Hash.new { |h,version| h[version] = [] }
matching_tuples.map do |n,_|
matching_tuples.each do |n, _|
platforms[n.version] << n.platform if n.platform
end
@ -182,11 +188,29 @@ class Gem::Commands::QueryCommand < Gem::Command
end
end
entry = gem_name.dup
output << make_entry(matching_tuples, platforms)
end
end
if options[:versions] then
list = if platforms.empty? or options[:details] then
matching_tuples.map { |n,_| n.version }.uniq
def entry_details entry, spec, specs, platforms
return unless options[:details]
entry << "\n"
spec_platforms entry, platforms
spec_authors entry, spec
spec_homepage entry, spec
spec_license entry, spec
spec_loaded_from entry, spec, specs
spec_summary entry, spec
end
def entry_versions entry, name_tuples, platforms
return unless options[:versions]
list =
if platforms.empty? or options[:details] then
name_tuples.map { |n| n.version }.uniq
else
platforms.sort.reverse.map do |version, pls|
if pls == [Gem::Platform::RUBY] then
@ -197,27 +221,74 @@ class Gem::Commands::QueryCommand < Gem::Command
"#{version} #{platform_list.join ' '}"
end
end
end.join ', '
entry << " (#{list})"
end
if options[:details] then
detail_tuple = matching_tuples.first
spec = detail_tuple.last
unless spec.kind_of? Gem::Specification
spec = spec.fetch_spec detail_tuple.first
entry << " (#{list.join ', '})"
end
entry << "\n"
def make_entry entry_tuples, platforms
detail_tuple = entry_tuples.first
name_tuple, latest_spec = detail_tuple
latest_spec = latest_spec.fetch_spec name_tuple unless
Gem::Specification === latest_spec
name_tuples, specs = entry_tuples.flatten.partition do |item|
Gem::NameTuple === item
end
entry = [latest_spec.name]
entry_versions entry, name_tuples, platforms
entry_details entry, latest_spec, specs, platforms
entry.join
end
def spec_authors entry, spec
authors = "Author#{spec.authors.length > 1 ? 's' : ''}: "
authors << spec.authors.join(', ')
entry << format_text(authors, 68, 4)
end
def spec_homepage entry, spec
return if spec.homepage.nil? or spec.homepage.empty?
entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
end
def spec_license entry, spec
return if spec.license.nil? or spec.license.empty?
licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: "
licenses << spec.licenses.join(', ')
entry << "\n" << format_text(licenses, 68, 4)
end
def spec_loaded_from entry, spec, specs
return unless spec.loaded_from
if specs.length == 1 then
default = spec.default_gem? ? ' (default)' : nil
entry << "\n" << " Installed at#{default}: #{spec.base_dir}"
else
label = 'Installed at'
specs.each do |s|
version = s.version.to_s
version << ', default' if s.default_gem?
entry << "\n" << " #{label} (#{version}): #{s.base_dir}"
label = ' ' * label.length
end
end
end
def spec_platforms entry, platforms
non_ruby = platforms.any? do |_, pls|
pls.any? { |pl| pl != Gem::Platform::RUBY }
end
if non_ruby then
return unless non_ruby
if platforms.length == 1 then
title = platforms.values.length == 1 ? 'Platform' : 'Platforms'
entry << " #{title}: #{platforms.values.sort.join ', '}\n"
@ -234,46 +305,9 @@ class Gem::Commands::QueryCommand < Gem::Command
end
end
authors = "Author#{spec.authors.length > 1 ? 's' : ''}: "
authors << spec.authors.join(', ')
entry << format_text(authors, 68, 4)
if spec.rubyforge_project and not spec.rubyforge_project.empty? then
rubyforge = "Rubyforge: http://rubyforge.org/projects/#{spec.rubyforge_project}"
entry << "\n" << format_text(rubyforge, 68, 4)
end
if spec.homepage and not spec.homepage.empty? then
entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4)
end
if spec.license and not spec.license.empty? then
licenses = "License#{spec.licenses.length > 1 ? 's' : ''}: "
licenses << spec.licenses.join(', ')
entry << "\n" << format_text(licenses, 68, 4)
end
if spec.loaded_from then
if matching_tuples.length == 1 then
loaded_from = File.dirname File.dirname(spec.loaded_from)
entry << "\n" << " Installed at: #{loaded_from}"
else
label = 'Installed at'
matching_tuples.each do |n,s|
loaded_from = File.dirname File.dirname(s.loaded_from)
entry << "\n" << " #{label} (#{n.version}): #{loaded_from}"
label = ' ' * label.length
end
end
end
def spec_summary entry, spec
entry << "\n\n" << format_text(spec.summary, 68, 4)
end
output << entry
end
say output.join(options[:details] ? "\n\n" : "\n")
end
end

View File

@ -622,7 +622,6 @@ class Gem::Specification
File.join(Gem.default_dir, "specifications", "default")
end
private
def each_spec(search_dirs) # :nodoc:
search_dirs.each { |dir|
Dir[File.join(dir, "*.gemspec")].each { |path|

View File

@ -465,6 +465,19 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem.searcher = nil
end
##
# Installs the provided default specs including writing the spec file
def install_default_gems(*specs)
install_default_specs(*specs)
specs.each do |spec|
open spec.loaded_from, 'w' do |io|
io.write spec.to_ruby_for_cache
end
end
end
##
# Install the provided default specs
@ -572,7 +585,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
block = proc do |s|
# Since Hash#each is unordered in 1.8, sort
# the keys and iterate that way so the tests are
# deteriminstic on all implementations.
# deterministic on all implementations.
deps.keys.sort.each do |n|
s.add_dependency n, (deps[n] || '>= 0')
end

View File

@ -72,7 +72,19 @@ class Gem::Uninstaller
# directory, and the cached .gem file.
def uninstall
list = Gem::Specification.find_all_by_name(@gem, @version)
dependency = Gem::Dependency.new @gem, @version
list = []
dirs =
Gem::Specification.dirs +
[Gem::Specification.default_specifications_dir]
Gem::Specification.each_spec dirs do |spec|
next unless dependency.matches_spec? spec
list << spec
end
default_specs, list = list.partition do |spec|
spec.default_gem?

View File

@ -106,7 +106,6 @@ pl (1 i386-linux)
@a2.summary = 'This is a lot of text. ' * 4
@a2.authors = ['Abraham Lincoln', 'Hirohito']
@a2.homepage = 'http://a.example.com/'
@a2.rubyforge_project = 'rubygems'
util_clear_gems
util_setup_spec_fetcher @a1, @a2, @pl1
@ -123,7 +122,6 @@ pl (1 i386-linux)
a (2)
Authors: Abraham Lincoln, Hirohito
Rubyforge: http://rubyforge.org/projects/rubygems
Homepage: http://a.example.com/
This is a lot of text. This is a lot of text. This is a lot of text.
@ -147,7 +145,6 @@ pl (1)
@a2.summary = 'This is a lot of text. ' * 4
@a2.authors = ['Abraham Lincoln', 'Hirohito']
@a2.homepage = 'http://a.example.com/'
@a2.rubyforge_project = 'rubygems'
@a2.platform = 'universal-darwin'
util_clear_gems
@ -168,7 +165,6 @@ a (2, 1)
1: x86-linux
2: universal-darwin
Authors: Abraham Lincoln, Hirohito
Rubyforge: http://rubyforge.org/projects/rubygems
Homepage: http://a.example.com/
This is a lot of text. This is a lot of text. This is a lot of text.
@ -355,7 +351,6 @@ pl (1 i386-linux)
@a2.summary = 'This is a lot of text. ' * 4
@a2.authors = ['Abraham Lincoln', 'Hirohito']
@a2.homepage = 'http://a.example.com/'
@a2.rubyforge_project = 'rubygems'
@a2.platform = 'universal-darwin'
util_clear_gems
@ -380,7 +375,6 @@ a (2, 1)
1: x86-linux
2: universal-darwin
Authors: Abraham Lincoln, Hirohito
Rubyforge: http://rubyforge.org/projects/rubygems
Homepage: http://a.example.com/
Installed at -
-
@ -400,5 +394,43 @@ pl \(1\)
assert_match expected, @ui.output
end
def test_execute_default_details
default_gem_dir = Gem::Specification.default_specifications_dir
@a1.loaded_from =
File.join default_gem_dir, @a1.spec_name
@cmd.handle_options %w[-l -d]
use_ui @ui do
@cmd.execute
end
str = @ui.output
expected = <<-EOF
*** LOCAL GEMS ***
a (3.a, 2, 1)
Author: A User
Homepage: http://example.com
Installed at (3.a): #{@gemhome}
(2): #{@gemhome}
(1, default): #{@a1.base_dir}
this is a summary
pl \(1\)
Platform: i386-linux
Author: A User
Homepage: http://example.com
Installed at: #{@gemhome}
this is a summary
EOF
assert_equal expected, @ui.output
end
end

View File

@ -175,23 +175,5 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase
assert Gem::Specification.find_all_by_name('x').length == 0
end
def test_execute_default_gem
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
install_default_specs(default_gem_spec)
ui = Gem::MockGemUi.new
@cmd.options[:args] = %w[default]
@cmd.options[:executables] = true
use_ui ui do
e = assert_raises Gem::InstallError do
@cmd.execute
end
assert_equal "gem \"default\" cannot be uninstalled because it is a default gem",
e.message
end
end
end

View File

@ -172,6 +172,38 @@ class TestGemUninstaller < Gem::InstallerTestCase
assert_same uninstaller, @post_uninstall_hook_arg
end
def test_uninstall_default_gem
spec = new_default_spec 'default', '2'
install_default_gems spec
uninstaller = Gem::Uninstaller.new spec.name, :executables => true
e = assert_raises Gem::InstallError do
uninstaller.uninstall
end
assert_equal 'gem "default" cannot be uninstalled ' +
'because it is a default gem',
e.message
end
def test_uninstall_default_gem_with_same_version
default_spec = new_default_spec 'default', '2'
install_default_gems default_spec
spec = new_spec 'default', '2'
install_gem spec
Gem::Specification.reset
uninstaller = Gem::Uninstaller.new spec.name, :executables => true
uninstaller.uninstall
refute_path_exists spec.gem_dir
end
def test_uninstall_nonexistent
uninstaller = Gem::Uninstaller.new 'bogus', :executables => true
@ -265,8 +297,8 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_uninstall_prompts_about_broken_deps
util_gem 'r', '1', 'q' => '= 1'
util_gem 'q', '1'
quick_gem 'r', '1' do |s| s.add_dependency 'q', '= 1' end
quick_gem 'q', '1'
un = Gem::Uninstaller.new('q')
ui = Gem::MockGemUi.new("y\n")
@ -287,10 +319,10 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_uninstall_only_lists_unsatified_deps
util_gem 'r', '1', 'q' => '~> 1.0'
util_gem 'x', '1', 'q' => '= 1.0'
util_gem 'q', '1.0'
util_gem 'q', '1.1'
quick_gem 'r', '1' do |s| s.add_dependency 'q', '~> 1.0' end
quick_gem 'x', '1' do |s| s.add_dependency 'q', '= 1.0' end
quick_gem 'q', '1.0'
quick_gem 'q', '1.1'
un = Gem::Uninstaller.new('q', :version => "1.0")
ui = Gem::MockGemUi.new("y\n")
@ -311,9 +343,9 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_uninstall_doesnt_prompt_when_other_gem_satifies_requirement
util_gem 'r', '1', 'q' => '~> 1.0'
util_gem 'q', '1.0'
util_gem 'q', '1.1'
quick_gem 'r', '1' do |s| s.add_dependency 'q', '~> 1.0' end
quick_gem 'q', '1.0'
quick_gem 'q', '1.1'
un = Gem::Uninstaller.new('q', :version => "1.0")
ui = Gem::MockGemUi.new("y\n")
@ -328,11 +360,8 @@ class TestGemUninstaller < Gem::InstallerTestCase
end
def test_uninstall_doesnt_prompt_when_removing_a_dev_dep
util_gem('r', '1') do |s|
s.add_development_dependency "q", "= 1.0"
end
util_gem 'q', '1.0'
quick_gem 'r', '1' do |s| s.add_development_dependency 'q', '= 1.0' end
quick_gem 'q', '1.0'
un = Gem::Uninstaller.new('q', :version => "1.0")
ui = Gem::MockGemUi.new("y\n")
@ -348,11 +377,11 @@ class TestGemUninstaller < Gem::InstallerTestCase
def test_uninstall_prompt_includes_dep_type
util_gem 'r', '1' do |s|
quick_gem 'r', '1' do |s|
s.add_development_dependency 'q', '= 1'
end
util_gem 'q', '1'
quick_gem 'q', '1'
un = Gem::Uninstaller.new('q', :check_dev => true)
ui = Gem::MockGemUi.new("y\n")