Chmer用のchmバンドルを生成するRuby Script

id:cho45さんが作っているMac OS X用 CHTMLビューア Chmerがめちゃめちゃすばらしいです。
ただ、http://coderepos.org/share/browser/lang/ruby/misc/chm-generators/generate-local-gems-chm-bundle.rbの生成スクリプトがちょっと希望と違う感じでしたので、似たようなものを作ってみました。

引数にgemの名前を与えると、そのgemのRDocをchmバンドルにまとめます。-Vオプションで生成対象のgemのバージョンを指定することもできます。

$ gem2chemr_chm.rb rspec
  #=> generates latest ./rspec-1.0.8.chm
$ gem2chemr_chm.rb rspec -v 1.0.5
  #=> generates latest ./rspec-1.0.5.chm

chmバンドルの生成には http://coderepos.org/share/wiki/Chemr#GeneratingScript こちらのスクリプトを使っています。ソース中で'require "gen_chm_bundle"'してるところがそこですので、読み込めるよう適宜調整してください。

以下ソース

#!/usr/bin/env ruby
=begin
This script generate chm bundle for Chemer, very cool CHTML viewer on Mac OS X.
(http://coderepos.org/share/wiki/Chemr)

eg:
$ gem2chemr_chm.rb rspec
  #=> generates latest ./rspec-1.0.8.chm

$ gem2chemr_chm.rb rspec -v 1.0.5
  #=> generates latest ./rspec-1.0.5.chm

This script depends on Chemr trac's GeneratingScript saved with name 'gen_chm_bundle.rb'
(http://coderepos.org/share/wiki/Chemr#GeneratingScript)

Licensed under GPL v2 or later.

Written by MOROHASHI Kyosuke (moronatural@gmail.com) on 2007/11/11
=end

require 'rubygems'
require 'hpricot'
require 'yaml'

# save "http://coderepos.org/share/wiki/Chemr#GeneratingScript" as gen_chm_bundle.rb and add -Ipath/to/dir.
require 'gen_chm_bundle'

class ChemrDoc
  module Errors
    GemNotFound = Class.new(RuntimeError)
    NoRdoc = Class.new(RuntimeError)
  end

  INDEX = "/files/README.html"
  SYMLINK_NAME = "rdoc"

  RdocLink = Struct.new(:text, :href) do
    def local_path
      File.join("rdoc", href)
    end
  end

  def initialize(gem_name, version=nil)
    @gem = ::Gem.source_index.find_name(gem_name, version).last
    raise GemNotFound if @gem.nil?
  end

  def rdoc_path
    @gem.full_gem_path.gsub(/\/gems\//, '/doc/')+"/rdoc"
  end

  def keywords(source = "fr_method_index.html")
    links_and_keywords(source).map{|link| [link.text, [link.local_path]]}
  end

  def toc(source = "fr_class_index.html")
    _toc = links_and_keywords(source).inject([]) do |arr, link|
      arr<< { :local=>link.local_path, :children=>[], :name=>link.text }
    end
    {:children => _toc}
  end

  def generate
    raise Errors::NoRdoc unless @gem.has_rdoc?

    name = "#{@gem.name}-#{@gem.version}"
    root, resouce = mkchmbundle((name+".chm"), name, File.join(SYMLINK_NAME, INDEX), keywords, toc)
    rdoc_link = resouce + SYMLINK_NAME
    rdoc_link.make_symlink(rdoc_path)
  end

  private
  def links_and_keywords(source)
    hpricot_doc = Dir.chdir(rdoc_path){ Hpricot( File.read("./#{source}") ) }
    (hpricot_doc / "a" ).map{|link| RdocLink.new(link.inner_html, link.attributes["href"]) }
  end
end

if __FILE__ == $0
  require 'optparse'
  options = {}
  OptionParser.new do |opts|
    opts.banner = "Usage: #{$0} gem_name [-V gem_version]"
    opts.on("-V gem_version", "--gem-version") do |v|
      options[:gem_version] = v
    end
  end.parse!

  ChemrDoc.new(ARGV.shift, options[:gem_version]).generate
end