Factor out gem-* commands to a separate plugin

This code doesn't really belong to Pry Core. It is all about RubyGems, but Pry
is all about introspection and debugging. We used to have some gem-* commands in
the past and it wasn't a big issue but now, since we have 7 commands, they
really deserve their own place.

These commands were moved to:
https://github.com/pry/pry-gem
This commit is contained in:
Kyrylo Silin 2019-03-10 18:42:39 +02:00
parent 595ee86ac6
commit d4c653f156
12 changed files with 3 additions and 405 deletions

View File

@ -266,4 +266,3 @@ Style/PerlBackrefs:
- 'lib/pry/input_completer.rb'
- 'lib/pry/last_exception.rb'
- 'lib/pry/method.rb'
- 'lib/pry/rubygem.rb'

View File

@ -5,6 +5,9 @@
* Deleted `install-command` ([#1979](https://github.com/pry/pry/pull/1979))
* Deleted `Pry::Helpers::BaseHelpers#command_dependencies_met?`
([#1979](https://github.com/pry/pry/pull/1979))
* Deleted commands: `gem_cd`, `gem_install`, `gem_list`, `gem_open`,
`gem_readme`, `gem_search`, `gem_stats`
([#1981](https://github.com/pry/pry/pull/1981))
### [v0.12.2][v0.12.2] (November 12, 2018)

View File

@ -47,7 +47,6 @@ require 'pry/color_printer'
require 'pry/pager'
require 'pry/terminal'
require 'pry/editor'
require 'pry/rubygem'
require 'pry/indent'
require 'pry/object_path'
require 'pry/output'
@ -102,13 +101,6 @@ require 'pry/commands/exit_all'
require 'pry/commands/exit_program'
require 'pry/commands/find_method'
require 'pry/commands/fix_indent'
require 'pry/commands/gem_cd'
require 'pry/commands/gem_install'
require 'pry/commands/gem_list'
require 'pry/commands/gem_open'
require 'pry/commands/gem_readme'
require 'pry/commands/gem_search'
require 'pry/commands/gem_stats'
require 'pry/commands/help'
require 'pry/commands/hist'
require 'pry/commands/import_set'

View File

@ -1,28 +0,0 @@
class Pry
class Command
class GemCd < Pry::ClassCommand
match 'gem-cd'
group 'Gems'
description "Change working directory to specified gem's directory."
command_options argument_required: true
banner <<-'BANNER'
Usage: gem-cd GEM_NAME
Change the current working directory to that in which the given gem is
installed.
BANNER
def process(gem)
Dir.chdir(Rubygem.spec(gem).full_gem_path)
output.puts(Dir.pwd)
end
def complete(str)
Rubygem.complete(str)
end
end
Pry::Commands.add_command(Pry::Command::GemCd)
end
end

View File

@ -1,34 +0,0 @@
class Pry
class Command
class GemInstall < Pry::ClassCommand
match 'gem-install'
group 'Gems'
description 'Install a gem and refresh the gem cache.'
command_options argument_required: true
banner <<-'BANNER'
Usage: gem-install GEM_NAME
Installs the given gem, refreshes the gem cache, and requires the gem for you
based on a best guess from the gem name.
gem-install pry-stack_explorer
BANNER
def setup
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
end
def process(gem)
Rubygem.install(gem)
output.puts "Gem `#{green(gem)}` installed."
require gem
rescue LoadError
require_path = gem.split('-').join('/')
require require_path
end
end
Pry::Commands.add_command(Pry::Command::GemInstall)
end
end

View File

@ -1,35 +0,0 @@
class Pry
class Command
class GemList < Pry::ClassCommand
match 'gem-list'
group 'Gems'
description 'List and search installed gems.'
banner <<-'BANNER'
Usage: gem-list [REGEX]
List all installed gems, when a regex is provided, limit the output to those
that match the regex.
BANNER
def process(pattern = nil)
pattern = Regexp.compile(pattern || '')
gems = Rubygem.list(pattern).group_by(&:name)
gems.each do |gem, specs|
specs.sort! do |a, b|
Gem::Version.new(b.version) <=> Gem::Version.new(a.version)
end
versions = specs.each_with_index.map do |spec, index|
index == 0 ? bright_green(spec.version.to_s) : green(spec.version.to_s)
end
output.puts "#{default gem} (#{versions.join ', '})"
end
end
end
Pry::Commands.add_command(Pry::Command::GemList)
end
end

View File

@ -1,31 +0,0 @@
class Pry
class Command
class GemOpen < Pry::ClassCommand
match 'gem-open'
group 'Gems'
description 'Opens the working directory of the gem in your editor.'
command_options argument_required: true
banner <<-'BANNER'
Usage: gem-open GEM_NAME
Change the current working directory to that in which the given gem is
installed, and then opens your text editor.
gem-open pry-exception_explorer
BANNER
def process(gem)
Dir.chdir(Rubygem.spec(gem).full_gem_path) do
Pry::Editor.new(_pry_).invoke_editor(".", 0, false)
end
end
def complete(str)
Rubygem.complete(str)
end
end
Pry::Commands.add_command(Pry::Command::GemOpen)
end
end

View File

@ -1,30 +0,0 @@
class Pry
class Command
class GemReadme < Pry::ClassCommand
match 'gem-readme'
description 'Show the readme bundled with a rubygem'
group 'Gems'
command_options argument_required: true
banner <<-BANNER
gem-readme gem
Show the readme bundled with a rubygem
BANNER
def process(name)
spec = Gem::Specification.find_by_name(name)
glob = File.join(spec.full_gem_path, 'README*')
readme = Dir[glob][0]
if File.exist?(readme.to_s)
_pry_.pager.page File.read(readme)
else
raise Pry::CommandError, "Gem '#{name}' doesn't appear to have a README"
end
rescue Gem::LoadError
raise Pry::CommandError,
"Gem '#{name}' wasn't found. Are you sure it is installed?"
end
Pry::Commands.add_command(self)
end
end
end

View File

@ -1,45 +0,0 @@
class Pry
class Command
class GemSearch < Pry::ClassCommand
match 'gem-search'
description 'Search for a gem with the rubygems.org JSON API'
group 'Gems'
command_options argument_required: true
banner <<-BANNER
gem-search [options] gem
Search for a gem with the rubygems.org HTTP API
BANNER
API_ENDPOINT = 'https://rubygems.org/api/v1/search.json'.freeze
def setup
require 'json' unless defined?(JSON)
require 'net/http' unless defined?(Net::HTTP)
end
def options(opt)
opt.on :l, :limit, 'Limit the number of results (max: 30)',
default: 10,
as: Integer,
argument: true
end
def process(str)
uri = URI.parse(API_ENDPOINT)
uri.query = URI.encode_www_form(query: str)
gems = JSON.parse(Net::HTTP.get(uri))
_pry_.pager.page list_as_string(gems, opts[:limit])
end
private
def list_as_string(gems, limit = 10)
gems[0..limit - 1].map do |gem|
name, version, info = gem.values_at 'name', 'version', 'info'
"#{bold(name)} #{bold('v' + version)} \n#{info}\n\n"
end.join
end
Pry::Commands.add_command(self)
end
end
end

View File

@ -1,91 +0,0 @@
class Pry
class Command
class GemStat < Pry::ClassCommand
require 'json'
require 'net/http'
STAT_HOST = "rubygems.org".freeze
STAT_PORT = 443
STAT_PATH = "/api/v1/gems/%s.json".freeze
FAIL_WHALE = <<-FAILWHALE.freeze
W W W
W W W W
'. W
.-""-._ \ \.--|
/ "-..__) .-'
| _ /
\'-.__, .__.,'
`'----'._\--'
VVVVVVVVVVVVVVVVVVVVV
FAILWHALE
match 'gem-stat'
description 'Show the statistics of a gem (requires internet connection)'
group 'Gems'
command_options argument_required: true
banner <<-BANNER
gem-stats name
Show the statistics of a gem.
Requires an internet connection.
BANNER
def process(name)
client = Net::HTTP.start STAT_HOST, STAT_PORT, use_ssl: true
res = client.get STAT_PATH % URI.encode_www_form_component(name)
case res
when Net::HTTPOK
_pry_.pager.page format_gem(JSON.parse(res.body))
when Net::HTTPServiceUnavailable
_pry_.pager.page <<-FAILURE
#{bright_blue(FAIL_WHALE)}
#{bright_red('Ruby On Rails')}
#{bright_red('Net::HTTPServiceUnavailable')}
FAILURE
else
raise Pry::CommandError, "Bad response (#{res.class})"
end
ensure
client.finish if client
end
private
def format_gem(h)
h = Pry::Config.from_hash(h)
format_str = unindent <<-FORMAT
%{name} %{version}
--
Total Downloads : %{downloads}
Version Downloads : %{version_downloads}
#{red('Dependencies')} (runtime)
--
%{rdependencies}
#{red('Dependencies')} (development)
%{ddependencies}
FORMAT
format(
format_str,
name: green(h.name),
version: bold("v#{h.version}"),
downloads: h.downloads,
version_downloads: h.version_downloads,
rdependencies: format_dependencies(h.dependencies.runtime),
ddependencies: format_dependencies(h.dependencies.development)
)
end
def format_dependencies(rdeps)
return bold('None') if rdeps.empty?
with_line_numbers(
rdeps.map { |h| "#{h['name']} (#{h['requirements']})" }.join("\n"),
1,
:bold
)
end
Pry::Commands.add_command(self)
end
end
end

View File

@ -1,82 +0,0 @@
require 'rubygems'
class Pry
module Rubygem
class << self
def installed?(name)
if Gem::Specification.respond_to?(:find_all_by_name)
Gem::Specification.find_all_by_name(name).any?
else
Gem.source_index.find_name(name).first
end
end
# Get the gem spec object for the given gem name.
#
# @param [String] name
# @return [Gem::Specification]
def spec(name)
specs = if Gem::Specification.respond_to?(:each)
Gem::Specification.find_all_by_name(name)
else
Gem.source_index.find_name(name)
end
first_spec = specs.max_by { |spec| Gem::Version.new(spec.version) }
first_spec || raise(CommandError, "Gem `#{name}` not found")
end
# List gems matching a pattern.
#
# @param [Regexp] pattern
# @return [Array<Gem::Specification>]
def list(pattern = /.*/)
if Gem::Specification.respond_to?(:each)
Gem::Specification.select { |spec| spec.name =~ pattern }
else
Gem.source_index.gems.values.select { |spec| spec.name =~ pattern }
end
end
# Completion function for gem-cd and gem-open.
#
# @param [String] so_far what the user's typed so far
# @return [Array<String>] completions
def complete(so_far)
if so_far =~ / ([^ ]*)\z/
list(/\A#{$2}/).map(&:name)
else
list.map(&:name)
end
end
# Installs a gem with all its dependencies.
#
# @param [String] name
# @return [void]
def install(name)
require 'rubygems/dependency_installer'
gem_config = Gem.configuration['gem']
gemrc_opts = (gem_config.nil? ? "" : gem_config.split(' '))
destination = if gemrc_opts.include?('--user-install')
Gem.user_dir
elsif File.writable?(Gem.dir)
Gem.dir
else
Gem.user_dir
end
installer = Gem::DependencyInstaller.new(install_dir: destination)
installer.install(name)
rescue Errno::EACCES
raise CommandError,
"Insufficient permissions to install #{green(name)}."
rescue Gem::GemNotFoundException
raise CommandError,
"Gem #{green(name)} not found. Aborting installation."
else
Gem.refresh
end
end
end
end

View File

@ -1,20 +0,0 @@
describe "gem-list" do
it 'should not raise when invoked' do
expect { pry_eval(self, 'gem-list') }.to_not raise_error
end
it 'should work arglessly' do
list = pry_eval('gem-list')
expect(list).to match(/rspec \(/)
end
it 'should find arg' do
prylist = pry_eval('gem-list method_source')
expect(prylist).to match(/method_source \(/)
expect(prylist).not_to match(/rspec/)
end
it 'should return non-results as silence' do
expect(pry_eval('gem-list aoeuoueouaou')).to be_empty
end
end