■ Ruby のリファレンスマニュアル用ツール

すいません、最近使っていないのでちゃんと動くか確認できていません。 ごめんなさい。

とっても特殊なツールです。 Web ブラウザ(ここでは Mozilla または Netscape)で、本家の Ruby リファレンスマニュアル にアクセスします。 これが Web ブラウザのキャッシュに残ります。 この Web ブラウザのキャッシュの中身をとりだして、自分用の Ruby リファレンスマニュアルにします。 と、特殊ですよね。 キャッシュにあるファイルを有効利用するというか... 本来なら本家から直接とりこむのも「あり」なのですが、全部ネットでとってくるのも... でも、最初が大変なのである程度自動化したいというのは目標です。 私はクリッククリックしてしまったのですが...

私の最初の目的は、「自分のローカルなシステムで Ruby リファレンスマニュアルを本家と同じ形式で使いたい」です。 本家の場合あれこれ工夫されている環境なので、HTML ファイルに分割されたファイルが存在するわけではありません。 Namazu で検索を行ないたいということもあって、私は HTML での分割されたファイルがほしかったのです。

最新の Ruby リファレンスマニュアル。 ノウハウとかいろいろな情報がたくさん含まれています。 これって、やっぱりみなさんでわいわい作業されているからですよね。 いいですね。

感謝: Ruby リファレンスマニュアルを作成、更新しているみなさま、ホントありがとうございます。 いろいろなノウハウもありとても勉強になります。 Ruby Documentation Project のみなさま、ありがとうございます。 RWiki 関連のみなさま、ありがとうございます。

青木大輔さんが HTML ファイルの分割されたものを準備していたりします。 http://www7.tok2.com/home/misc/ruby.html RAA からもたどることができます。 気がついたのは、私がスクリプトを作成してしまってからでした。 とってもよくできているので、みなさまどうぞ! 私のスクリプトは、サンプルとして考えてください。

■ 使い方

目的は、自分の Linux などのシステムで、Apache などの Web サーバを使用し Web ブラウザで参照できるようにします。 これでいつでもどこでも参照できますし、Namazu などと組み合わせると検索もいい感じでできてしまいます。 ぜひ Namazu しましょう!

実施するための基本的な手順は次のとおりです。

注意事項は次のとおりです。

URL 用にエンコードされているファイル名を EUC Japan の漢字コードに変換しています。 これは Apache などの HTTP サーバ経由でアクセスするためです。 もし HTTP サーバを経由せずに直接 HTML ファイルを Web ブラウザで参照する場合はこのファイル名の変換部分は実施しなくて OK と思います。 具体的にはスクリプト中の decodename を呼びださないようにしてください。

本家の Ruby リファレンスマニュアルが更新された場合は、Web ブラウザで参照するだけで OK です。 Web ブラウザで参照後、スクリプトを実行するとアクセスしたページが置き換わります。 更新の確認は、 「 recent 」 ページがとても便利です。

■ ダウンロード 変換済 Ruby リファレンスマニュアル

Ruby リファレンスマニュアルは、300 以上のファイルから作成されています。 最初は Web ブラウザでアクセスするのは大変と思います。 これが最大の問題点と思います。 私の手もとにある変換済のリファレンスマニュアルを用意しました。

残念ながら、Win 系のシステムでは、ファイル名に EUC Japan 漢字コードは使用できないと思います。

今回のスクリプトは、ファイル名を変換する機能をそのまま使用した場合は ruby-ref-1.6.http.tar.bz2 に対応しています。 ファイル名の変換を使用しないように修正すれば ruby-ref-1.6.file.tar.bz2 で使えるようになります。 もとファイルは残しておき、いろいろ試してみてください。

■ 検索できるように Namazu しましょう

とっても便利な Ruby リファレンスマニュアル。 これを手元で検索できるようにすれば、とっても便利になります。 Linux と Namazu を使っての例になりますが、同じことがいろいろなプラットフォームで可能ですので、ここの情報をヒントに試してみてください。

私の実施環境は次の通りです。

Vine Linux 2.1CR の場合 /home/httpd/cgi-bin/.namazurc に一行加えて

Replace        /home/httpd/html/       http://localhost/
/usr/share/namazu/index/ruby というディレクトリを自分で書き込めるように作成しておいて。 Ruby のリファレンスマニュアルを /home/httpd/html/ruby/ref に入れておいて。
#! /bin/sh -x
# /home/tetsu/bin/mknmz_ruby.sh
# created: October 20,2001 Saturday 21:22:49
# author: tetsu(WATANABE Tetsuya)
# $Id$

dir=/usr/share/namazu/index/ruby

LANG=ja_JP.eucJP
LC_ALL=ja_JP.eucJP
LANGUAGE=ja_JP.eucJP

if [ -d $dir ]; then
  cd $dir
  mknmz /home/httpd/html/ruby/ref
fi
というシェルスクリプトを実行して。 /home/httpd/html/search.html に一行追加して(漢字コードは EUC)。
 <LI><INPUT TYPE="CHECKBOX" NAME="dbname" VALUE="ruby" CHECKED>Ruby
これで検索可能に。

手元の Ruby リファレンスマニュアルを更新したら、上記のシェルスクリプトを再実行すれば OK です。

■ ソースコード

Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。

このスクリプトは、漢字コードを EUC Japan として使用してください。 一部漢字コードに依存している記述をしています。

● getrefcache.rb

#! /usr/local/bin/ruby -Ke
# /home/tetsu/src/ruby/html/ref/getrefcache.rb
# Created: October 19,2001 Friday 11:11:40
# Author: tetsu(WATANABE Tetsuya)
# $Id: getrefcache.rb,v 1.12 2002/05/12 00:05:48 tetsu Exp $
# usage:

# 注意: このプログラムは、カレントディレクトリにファイルを作成します。

Re1 = Regexp.new('http://www.ruby-lang.org/ja/man-1.6/style.css')
Re2 = Regexp.new('<p class="headerURL">')
Re3 = Regexp.new('/ja/man-1\.6/\?cmd=edit;name=')
Re4 = Regexp.new('<hr />')

# Ruby ref 形式のファイルなら、出力用ファイル名を返す
# ちゃう場合は nil
def ck_ref(r)
  ofile = nil
  flag = false
  while l = r.gets
    return if /\000/ =~ l       # return nil
    break if Re4 =~ l
    if flag && Re2 =~ l
      return l.scan(/\?cmd=view;name=(.+)\"/)[0][0]
    end
    if flag && Re3 =~ l
      ofile = l.scan(/\?cmd=edit;name=(.+)\"/)[0][0]
    end
    if Re1 =~ l
      flag = true
    end
  end
  return ofile
end

# 変換する 入力はファイル全部
def conv(l)
  l.gsub('http://www.ruby-lang.org/ja/man-1.6/style.css', 'style.css').
    gsub('href="(?:http://www.ruby-lang.org)?/ja/man-1.6/\?cmd=view;name=(.+?)"') do
    x = $1
    if x =~ /\#/
      a, b = x.split(/\#/)
      x = a.gsub(/%2F/, '/') + '.html#' + b       # cgi/ digest/ net/
    else
      x = x.gsub(/%2F/, '/') + '.html'            # cgi/ digest/ net/
    end
    'href="' + x + '"'
  end.
    gsub('http://www.ruby-lang.org/ja/man-1.6/', 'Ruby%A5%EA%A5%D5%A5%A1%A5%EC%A5%F3%A5%B9%A5%DE%A5%CB%A5%E5%A5%A2%A5%EB.html').
    gsub(/<link rev=\"made\" href=\"mailto:kazu@ruby-lang.org\" \/>/, '<META http-equiv="Content-Type" content="text/html; charset=euc-jp">
').
    gsub(/<div class="(?:navi|hotlinks)">.+?\/div>/m, '
<script language="JavaScript">
<!--
last_mod = new Date(document.lastModified);
document.writeln("Last Modified: ", last_mod);
//-->
</script>
').
    gsub(/<address.+?\/address>/m, '').
    gsub(/<p class="headerURL">URL: .+?<\/a><\/p>/, '').
    gsub(/([ -熙])\s*\n\s*([ -熙])/, '\1\2')
end

# ファイル名変換
def decodename(name)
  name.gsub(/%[\dA-F]{2}/) { |x|
    x[1, 2].hex.chr
  }
end

# ファイル名にディレクトリ名があれば作成
def multi_mkdir(mpath, mask = 0777 ^ File.umask)
  path = ''
  mpath.split('/').each do |f|
    path.concat(f)
    Dir.mkdir(path, mask) unless path == '' || File.exist?(path)
    path.concat('/')
  end
end

def get_cachedir
  dirs = []
  appreg = File.expand_path('~/.mozilla/appreg')
  if File.exist? appreg
    f = File.open(appreg)
    app = f.read.split(/[^\dA-Za-z\/.]+/) # strip
    f.close
    dirs.push app.grep(/\.mozilla/)[0] + '/Cache'
  end
  pref = File.expand_path('~/.netscape/preferences.js')
  if File.exist? pref
    f = File.open(pref)
    while l = f.gets
      if /browser\.cache\.directory/ =~ l
#       dirs.push l.scan(/\((.+)\)/)[0][0].gsub(/\"/, '').split(/, /)[1]        
        break
      end
    end
    f.close
  end
  dirs
end

# Cache ディレクトリから、リファレンス用のファイルを拾い集める。
dirs = get_cachedir
p dirs

require 'find'

files = []
dirs.each do |d|
  Find.find(d) do |ifile|
    if File.file? ifile
      r = File.open(ifile)
      ofile = ck_ref(r)
      r.close
      if ofile
        ofile = decodename(ofile)
        files.push([ifile, ofile])
      end
    end
  end
end
p files.size                    # みつけた件数

# カレントディレクトリにあるファイルと日付を比較
count = 0                       # 何件処理したか

for arr in files do
  ifile, ofile = arr
  ofile.concat('.html')
  multi_mkdir(File.dirname(ofile)) if /\// =~ ofile # 「/」あり処理

  if File.exist?(ofile)         # すでにある場合の比較(時間)
    imtime = File.mtime(ifile)
    omtime = File.mtime(ofile)
    next if imtime < omtime
  end

  r = File.open(ifile)
  o = File.open(ofile, 'w')
  while l = r.gets(nil)
    o.print conv(l)
  end
  o.close
  r.close

  count += 1
end

p count                         # 新規の件数

● mkidx.rb

補助ツールです。 全ファイルを引数として渡すと、参照用のタグを集めて、独自の索引を作成します。 Web ブラウザを経由してアクセスすることを考えて URL を生成しています。 生成するファイル名は metaindex.html としてください。 とても大きなインデックスファイルを作成してしまいます。 でも、探すのには便利ですよ。

#! /usr/local/bin/ruby -Ke
# /home/tetsu/src/ruby/mkidx.rb
# Created: March 03,1998 Tuesday 21:30:27
# Author: tetsu(WATANABE Tetsuya)
# $Id: mkidx.rb,v 1.10 2002/05/09 18:04:03 tetsu Exp $
# usage:

class String
  def to_url
    self.gsub(/[、-瑤]/) do |euc|
      sprintf('%%%02X%%%02X', euc[0], euc[1])
    end.gsub(/:/, '%3A')
  end
end

require 'cgi-lib'

title = ''
body = ''

ARGV.sort! do |a, b|
  a.downcase <=> b.downcase
end

while gets(nil)
  next if /(index|metaindex)\.html/ =~ File.basename(ARGF.filename)

  h = {}

  if ~/<title>(.+?)<\/title>/i
    title = Regexp.last_match[1]            # $1
  end

  $_.gsub!(/<a href=.*?>(.+?)<\/a>/, '\1')
  $_.scan(/<a name=\"(.+?)\".*?>((\n|.)*?)<\/a>/i) {
    name = Regexp.last_match[1]             # $1
    item = Regexp.last_match[2]             # $2
    if item.length == 0 or item == "\n"
      pos = Regexp.last_match.post_match.index("\n") # $'
      if pos == 0
        pos = Regexp.last_match.post_match.index("\n", 1)
      end
      item = Regexp.last_match.post_match[0, pos]
    end
        
    item.gsub!(/(^\s+|<d[td]>|<a href=.*?>|<\/a>|<\/?sup>|<\/?small>|\n)/i, '')
    url = ARGF.filename + '#' + name
    h[CGI::tag('li') { CGI::tag('a', 'href' => url.to_url) { item } }] = true
  }

  li = ''
  h.keys.sort do |a, b|
    a.downcase <=> b.downcase
  end.each do |k|
    li.concat(k)
  end

  # \n は、w3m のメモリ確保の関係から
  # 一行に全部入れてしまうとダメらしい。
  body.concat(CGI::tag('dt') { title } + CGI::tag('dd') { CGI::tag('ul') { li } } + "\n")
end

puts CGI::tag('html') {
  CGI::tag('head') {
    CGI::tag('meta', { 'http-equiv' => 'Content-Type', 'content' => 'text/html; charset=euc-jp' }) + \
    CGI::tag('title') { 'metaindex for Ruby reference manual' } + \
    CGI::tag('link', 'href' => '/base.css', 'type' => 'text/css', 'rel' => 'stylesheet')
  } +\
  CGI::tag("body") { body }
}

● euclink.rb

ファイル名を EUC Japan にした場合に、シンボリックリンクで URL 用にエンコード(「%数字数字」形式)し直します。 File::symlink の部分を File.rename に変更することで、ファイル名を URL 用にエンコード(「%数字数字」形式)し直します。 HTTP (Web サーバ) を利用する場合は、EUC Japan のファイル名を使用し、直接 w3m などの Web ブラウザからファイルをアクセスする場合は URL 用にエンコード(「%数字数字」形式)したファイル名を使います。

#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/html/ref/euclink.rb
# Created: October 26,2001 Friday 14:37:41
# Author: tetsu(WATANABE Tetsuya)
# $Id: euclink.rb,v 1.1 2001/10/26 06:18:05 tetsu Exp $
# usage:

# ファイル名変換
def decodename(name)
  name.gsub(/%[\dA-F]{2}/) { |x|
    x[1, 2].hex.chr
  }
end

# href から URL を拾いだす
href = {}

while gets
  if ~/href=/
    ref = $_.scan(/href=\"(.+?)\"/)[0][0]
    ref.sub!(/\#.*$/, '')
    if ref =~ /^(?:http|mailto|ftp):/ || ref =~ /cmd=view;name=/
    else
      href[ref] = ref
    end
  end
end

# ファイル名に該当するものを変換しすでに存在していたら
# シンボリックリンク

href.keys.sort.each do |k|
  de = decodename(k)
  if de != k
    if File.exist?(de)
      if File.exist?(k)
        puts "EXIST:#{k}"
      else
        File.symlink(de, k)
      end
    else
      puts "non Exist:#{de}"
    end
  end
end

■ 解説

メインの流れはできるだけわかりやすく書いたつもりなのですが(汚い処理はサブで実施)、こんな感じです。

という感じです。

ちなみにこのスクリプト。 最初は 4 つの分割したテスト用スクリプトでした。

このほかに、もともとつくっていた漢字の処理用のスクリプトを使っていたのでいた。 これらをあわせたのが上記のスクリプトだったりします。 あわせなくてもよかったのかもしれませんが、必要なファイルだけを扱うのには、ファイルをみつける処理からはじまるので...

最初、

Ruby のリファレンスマニュアル っていいな。 Ruby のホームページから参照できる RD 形式をダイナミックに HTML 化しているのって、とってもスタイルもよくていいですよね。 あれをローカルの環境でいつでも読めるようにしたいのですが、なにか方法はないものでしょうか? 参照時の URL を固定値(フィル名と参照タグ)で置き換えて、wget みたいなものでファイルに落して、HTML ファイル分割化する? うーむ。 ruby-lang.org に負荷もかかるしなぁ。 でも、そういうマニュアルがほしいな。 しばらく考えて、自分で作ってみようかな。 休み休み get すれば、そんなに負荷にならないかも... 更新後の update は、更新情報があるので、それを取り込めたらいいな...
と思いました。 で、簡単に方針を決めて
あるものは使う。 Mozilla の cache にあるものは、それを活用。 Mozilla が管理しているファイル名と、URL の関係はよくわからないけれど、ファイルの中身には情報があるから。 リファレンスマニュアル関係はキーワードがたくさん含まれているから、みつけるのは容易かな。 さて、最初に cache からの再構築用スクリプトでも書きますか。
小さなスクリプトを書いて確認です。
で、書き換え実行後。 確認するとミスがあったり。 最長一致で変になっていたり。 消して、ファイル名戻して...
$ ruby -i.bak hen.rb *.html

確認(w3m)

$ rm -f *.html
$ rename.rb 'sub(/.bak/, "")' *.html.bak
の繰り返し。
こんな感じではじめました。 個別のスクリプトが一通りのことが OK になってから、今回のスクリプトに処理をまとめて書き直したのでした。

作成された HTML ファイルの特徴は次のようなものです。

「HTTP サーバを動かさずにファイルを直接参照したい」という要求も考えられるので、ファイル名を元に戻すスクリプトを追加しました。 ダウンロード用ファイルも、ファイル名を URL 用にエンコードされているものを用意しています。 こちらは Win 環境でも使用できると思います。 活用してくれるといいなと思います。

Mozilla では、ファイルのサイズやリンクが多い場合、うまく動かないことがあるようです。 リファレンスの参照には、w3m がとても便利です。 さくさく動きますよ。 私は、次のように alisa していたりします。

$ alias ref
ref='w3m http://localhost/ruby/ref/'
$ alias refm
refm='w3m http://localhost/ruby/ref/metaindex.html'

説明の後付けが多いので、体系がとれていません。 すいません。 これから整理していきます。

■ 履歴

● getrefcache.rb

1.12 2002/5/12

char-set の指定と Last Modified: の修正です。

1.11 2002/5/4

cgi/ digest/ net/ などの対応が不十分だったので、修正。

1.10 2002/4/3

Netscape 用ディレクトリを対象から削除。 コメントにしただけなので必要な場合は有効にしてください。

1.9 2001/11/4

Mozilla 以外に Netscape 用ディレクトリも自動確認

1.8 2001/10/21

-Ke を追加

1.7 2001/10/20

ファイルの更新時を「Last Modified」(JavaScript) で表示

1.6 2001/10/20

Mozilla なら ~/.mozilla/appreg からディレクトリ情報をとりだす

● mkidx.rb

1.10 2002/5/9

charset を明示するようにしました。

1.9 2002/5/9

コメント追加です。

1.8 2002/5/9

「ファイル名」「項目名」について、アルファベットの大文字小文字ともに同じとして扱うように sort です。

1.7 2002/5/8

ファイル名に「::」が含まれているときの対応。 以前や実施していたんですが、削除したらやっぱりダメだったので。

1.6 2002/5/7

CGI::print は、不用でした。 CGI じゃないし。

1.5 2002/5/5

タイトルのつけ忘れを修正。 Regexp.last_match へ修正。

1.4 2002/5/5

cgi-lib で書き直し。 メモリを多く使うようになりましたが、スクリプトは読みやすくなりました。

1.3 2002/5/4

cgi/ digest/ net/ などの対応が不十分だったので、修正。

1.2 2001/11/16

実現方法を修正しました。

1.1 2001/11/16

すべてのファイルを対象にしたインデックスファイルを作成するために

● euclink.rb

1.1 2001/10/26

いったん変更してしまったファイル名をもとに戻すために


渡辺哲也(WATANABE Tetsuya): Tetsuya.WATANABE atmark nifty.com