ruby-changes:26410
From: drbrain <ko1@a...>
Date: Wed, 19 Dec 2012 16:19:21 +0900 (JST)
Subject: [ruby-changes:26410] drbrain:r38461 (trunk): * lib/rubygems/commands/query_command.rb: Refactored to improve
drbrain 2012-12-19 16:19:10 +0900 (Wed, 19 Dec 2012) New Revision: 38461 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38461 Log: * 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. Modified files: trunk/ChangeLog trunk/lib/rubygems/commands/query_command.rb trunk/lib/rubygems/specification.rb trunk/lib/rubygems/test_case.rb trunk/lib/rubygems/uninstaller.rb trunk/test/rubygems/test_gem_commands_query_command.rb trunk/test/rubygems/test_gem_commands_uninstall_command.rb trunk/test/rubygems/test_gem_uninstaller.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 38460) +++ ChangeLog (revision 38461) @@ -1,3 +1,19 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Wed Dec 19 16:18:22 2012 Eric Hodel <drbrain@s...> + + * 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@s...> * doc/syntax/methods.rdoc: Add () around keyword arguments example for Index: lib/rubygems/specification.rb =================================================================== --- lib/rubygems/specification.rb (revision 38460) +++ lib/rubygems/specification.rb (revision 38461) @@ -622,7 +622,6 @@ class Gem::Specification https://github.com/ruby/ruby/blob/trunk/lib/rubygems/specification.rb#L622 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| Index: lib/rubygems/commands/query_command.rb =================================================================== --- lib/rubygems/commands/query_command.rb (revision 38460) +++ lib/rubygems/commands/query_command.rb (revision 38461) @@ -162,12 +162,18 @@ class Gem::Commands::QueryCommand < Gem: https://github.com/ruby/ruby/blob/trunk/lib/rubygems/commands/query_command.rb#L162 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,97 +188,125 @@ class Gem::Commands::QueryCommand < Gem: https://github.com/ruby/ruby/blob/trunk/lib/rubygems/commands/query_command.rb#L188 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 - else - platforms.sort.reverse.map do |version, pls| - if pls == [Gem::Platform::RUBY] then - version - else - ruby = pls.delete Gem::Platform::RUBY - platform_list = [ruby, *pls.sort].compact - "#{version} #{platform_list.join ' '}" - end - end - end.join ', ' + def entry_details entry, spec, specs, platforms + return unless options[:details] - entry << " (#{list})" - end + entry << "\n" - if options[:details] then - detail_tuple = matching_tuples.first + 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 - spec = detail_tuple.last + def entry_versions entry, name_tuples, platforms + return unless options[:versions] - unless spec.kind_of? Gem::Specification - spec = spec.fetch_spec detail_tuple.first + 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 + version + else + ruby = pls.delete Gem::Platform::RUBY + platform_list = [ruby, *pls.sort].compact + "#{version} #{platform_list.join ' '}" + end end + end - entry << "\n" + entry << " (#{list.join ', '})" + end - non_ruby = platforms.any? do |_, pls| - pls.any? { |pl| pl != Gem::Platform::RUBY } - end + def make_entry entry_tuples, platforms + detail_tuple = entry_tuples.first + name_tuple, latest_spec = detail_tuple - if non_ruby then - if platforms.length == 1 then - title = platforms.values.length == 1 ? 'Platform' : 'Platforms' - entry << " #{title}: #{platforms.values.sort.join ', '}\n" - else - entry << " Platforms:\n" - platforms.sort_by do |version,| - version - end.each do |version, pls| - label = " #{version}: " - data = format_text pls.sort.join(', '), 68, label.length - data[0, label.length] = label - entry << data << "\n" - end - end - end + latest_spec = latest_spec.fetch_spec name_tuple unless + Gem::Specification === latest_spec - 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 + name_tuples, specs = entry_tuples.flatten.partition do |item| + Gem::NameTuple === item + end - if spec.homepage and not spec.homepage.empty? then - entry << "\n" << format_text("Homepage: #{spec.homepage}", 68, 4) - end + entry = [latest_spec.name] - 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 + entry_versions entry, name_tuples, platforms + entry_details entry, latest_spec, specs, platforms - 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 + 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 - entry << "\n\n" << format_text(spec.summary, 68, 4) + 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 - output << entry end + end - say output.join(options[:details] ? "\n\n" : "\n") + def spec_platforms entry, platforms + non_ruby = platforms.any? do |_, pls| + pls.any? { |pl| pl != Gem::Platform::RUBY } + end + + return unless non_ruby + + if platforms.length == 1 then + title = platforms.values.length == 1 ? 'Platform' : 'Platforms' + entry << " #{title}: #{platforms.values.sort.join ', '}\n" + else + entry << " Platforms:\n" + platforms.sort_by do |version,| + version + end.each do |version, pls| + label = " #{version}: " + data = format_text pls.sort.join(', '), 68, label.length + data[0, label.length] = label + entry << data << "\n" + end + end + end + + def spec_summary entry, spec + entry << "\n\n" << format_text(spec.summary, 68, 4) end end Index: lib/rubygems/uninstaller.rb =================================================================== --- lib/rubygems/uninstaller.rb (revision 38460) +++ lib/rubygems/uninstaller.rb (revision 38461) @@ -72,7 +72,19 @@ class Gem::Uninstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/uninstaller.rb#L72 # 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? @@ -80,7 +92,7 @@ class Gem::Uninstaller https://github.com/ruby/ruby/blob/trunk/lib/rubygems/uninstaller.rb#L92 list, other_repo_specs = list.partition do |spec| @gem_home == spec.base_dir or - (@user_install and spec.base_dir == Gem.user_dir) + (@user_install and spec.base_dir == Gem.user_dir) end if list.empty? then Index: lib/rubygems/test_case.rb =================================================================== --- lib/rubygems/test_case.rb (revision 38460) +++ lib/rubygems/test_case.rb (revision 38461) @@ -466,6 +466,19 @@ class Gem::TestCase < MiniTest::Unit::Te https://github.com/ruby/ruby/blob/trunk/lib/rubygems/test_case.rb#L466 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 def install_default_specs(*specs) @@ -572,7 +585,7 @@ class Gem::TestCase < MiniTest::Unit::Te https://github.com/ruby/ruby/blob/trunk/lib/rubygems/test_case.rb#L585 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 Index: test/rubygems/test_gem_commands_uninstall_command.rb =================================================================== --- test/rubygems/test_gem_commands_uninstall_command.rb (revision 38460) +++ test/rubygems/test_gem_commands_uninstall_command.rb (revision 38461) @@ -175,23 +175,5 @@ class TestGemCommandsUninstallCommand < https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_uninstall_command.rb#L175 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 Index: test/rubygems/test_gem_uninstaller.rb =================================================================== --- test/rubygems/test_gem_uninstaller.rb (revision 38460) +++ test/rubygems/test_gem_uninstaller.rb (revision 38461) @@ -172,6 +172,38 @@ class TestGemUninstaller < Gem::Installe https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_uninstaller.rb#L172 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::Installe https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_uninstaller.rb#L297 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::Installe https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_uninstaller.rb#L319 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::Installe https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_uninstaller.rb#L343 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::Installe https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_uninstaller.rb#L360 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::Installe https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_uninstaller.rb#L377 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") Index: test/rubygems/test_gem_commands_query_command.rb =================================================================== --- test/rubygems/test_gem_commands_query_command.rb (revision 38460) +++ test/rubygems/test_gem_commands_query_command.rb (revision 38461) @@ -106,7 +106,6 @@ pl (1 i386-linux) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L106 @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) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L122 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) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L145 @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) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L165 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) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L351 @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) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L375 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\) https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_commands_query_command.rb#L394 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.o (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/