Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search for pages instead of gems #11

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ gem-man(1) -- view a gem's man page

## SYNOPSIS

gem man <GEM>
gem man <SECTION> <GEM>
gem man --system <GEM>
gem man --latest <GEM>
gem man <PAGE>
gem man <SECTION> <PAGE>
gem man --gem <GEM>
gem man --system <PAGE>
gem man --latest <PAGE>
gem man --exact <GEM>
gem man --all

Expand All @@ -26,16 +27,21 @@ your shell).

Metalicious.

## GEM
## PAGE

`gem man` expects to be passed the name of an installed gem. If there
are multiple man pages found for the gem, you will be asked which
you'd like to view. If only a single man page is found it will be
displayed.
Name of the manual page to view. All installed gems are searched for a
manual page named like this.

Man pages are any files whose extension is a single digit [0-9],
e.g. `ronn.1`.

## GEM

If `--gem` is specified, `gem man` expects to be passed the name of an
installed gem. If there are multiple man pages found for the gem, you
will be asked which you'd like to view. If only a single man page is
found it will be displayed.

## SECTION

Specifying a `SECTION` as the first argument narrows the search to man
Expand All @@ -53,6 +59,9 @@ man` will ask which you'd prefer.

You can specify gems or list available gems using a few options.

* `-g`, `--gem`:
Display pages in a specific gem instead of searching all gems.

* `-s`, `--system`:
Fall back to searching for system manuals. That is, `gem man -s
mac` will first look for a gem named `mac` with a man page before
Expand All @@ -65,7 +74,7 @@ You can specify gems or list available gems using a few options.
* `-v`, `--version`:
Specify version of gem to man.

* `-e`, `--exact`:
* `-e`, `--exact` (only with `-g`):
Only list exact matches.

* `-a`, `--all`:
Expand All @@ -81,6 +90,7 @@ See `gem help man` to view the options at any time.

gem man mustache
gem man 1 ronn
gem man -g ronn
gem man -a

## AUTHORING
Expand Down
87 changes: 73 additions & 14 deletions lib/rubygems/commands/man_command.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# -*- coding: utf-8 -*-
# Much of this is stolen from the `open_gem` RubyGem's "read"
# command - thanks Adam!
#
# http://github.com/adamsanderson/open_gem/blob/dfddaa286e/lib/rubygems/commands/read_command.rb
class Gem::Commands::ManCommand < Gem::Command
include Gem::VersionOption

# Search result struct for #get_specs_for_page. +spec+ is the
# Gem::Specification, +manpath+ the relative path of the manpage
# to the spec’s +man_dir+.
SpecPage = Struct.new(:spec, :manpath)

def initialize
super 'man', "Open a gem's manual",
:command => nil,
Expand All @@ -17,6 +23,7 @@ def initialize
add_latest_version_option
add_version_option
add_exact_match_option
add_list_gem_option
end

def usage
Expand Down Expand Up @@ -55,6 +62,12 @@ def add_exact_match_option
end
end

def add_list_gem_option
add_option("-g", "--gem", "List manpages for a gem") do |value, options|
options[:gem] = true
end
end

def execute
if get_one_optional_argument =~ /^\d$/
section = get_one_optional_argument
Expand All @@ -63,10 +76,8 @@ def execute
if options[:all]
puts "These gems have man pages:", ''

specs = Gem::Specification.respond_to?(:each) ? Gem::Specification : Gem.source_index.gems
specs.each do |*name_and_spec|
spec = name_and_spec.pop
puts "#{spec.name} #{spec.version}" if spec.has_manpage?
gems_with_manpages.each do |spec|
puts "#{spec.name} #{spec.version}"
end
else
# gem man 1 mustache
Expand All @@ -77,17 +88,50 @@ def execute
name, section = section, nil
end

# Try to read manpages.
if spec = get_spec(name) { |s| s.has_manpage?(section) }
read_manpage(spec, section)
elsif options[:system]
exec "man #{section} #{name}"
if options[:gem]
# Try to read manpages.
if spec = get_spec(name) { |s| s.has_manpage?(section) }
read_manpage(spec, section)
elsif options[:system]
exec "man #{section} #{name}"
else
abort "No manual entry for #{name}"
end
else
abort "No manual entry for #{name}"
results = get_specs_for_page(name, section)

if results.count.zero?
# No gemspec contains this manpage. Try system manpages
# if requested, otherwise fail.
if options[:system]
exec "man #{section} #{name}"
else
abort "No manual entry for #{name}"
end
elsif results.count == 1
# Exactly one gemspec contains this manpage. Display.
exec "man #{File.join(results.first.spec.man_dir, results.first.manpath)}"
else
# Multiple gemspecs contain this manpage. Let the user choose
# depending on the :latest option.
if options[:latest]
i = -1 # Last ist newest
else
choices = results.map{|s| "#{s.spec.name} #{s.version}"}
c, i = choose_from_list "Open which gem?", choices
end

exec "man #{File.join(results[i].spec.man_dir, results[i].manpath)}" if i
end
end
end
end

def gems_with_manpages
specs = Gem::Specification.respond_to?(:each) ? Gem::Specification : Gem.source_index.gems
specs.select{|*name_and_spec| name_and_spec.pop.has_manpage?}
end

def read_manpage(spec, section = nil)
return if spec.nil?

Expand All @@ -113,10 +157,6 @@ def read_manpage(spec, section = nil)
end
end

def gem_path(spec)
File.join(spec.installation_path, "gems", spec.full_name)
end

def get_spec(name, &block)
# Since Gem::Dependency.new doesn't want a Regexp
# We'll do it ourself!
Expand Down Expand Up @@ -146,4 +186,23 @@ def get_spec(name, &block)
specs[i] if i
end
end

# Search all existing specs for the given manpage.
# If requested, only search a specific section.
# Returns a sorted array of SpecPage instances.
def get_specs_for_page(name, section = nil)
gems_with_manpages.map do |spec|
# Check if this gem has the manpage. #manpages(section)
# narrows candidates, the final comparison excludes the
# trailing digit ([0..-2] part).
if manpath = spec.manpages(section).find{|path| path.split(".")[0..-2].join == name}
if options[:version].specific?
return [SpecPage.new(spec, manpath)] if options[:version].satisfied_by?(spec.version)
else
SpecPage.new(spec, manpath)
end
end
end.compact.sort_by{|s| s.spec.version}
end

end