Yahoo!天気予報のデザインが変わっていた

昨日あたりからYahoo!天気予報のデザインが微妙に変わっていた。

俺は昔からYahoo!天気予報のHTMLを正規表現で切り取って読み易い形に加工するRubyスクリプトを書いて使っている。Yahoo!天気予報も例に漏れず、この手の情報サイトはデザインに凝りすぎて一覧性が悪いのが多い。俺的にはこの程度の情報は一画面で見られないと気がすまない。出力結果は以下のようになる。

天気
2日 18時   曇り 21度 74% 0mm/h 北東1m/s
2日 21時   曇り 19度 82% 0mm/h 北東1m/s
3日 00時   曇り 18度 84% 0mm/h 東北東1m/s
3日 03時   晴れ 18度 84% 0mm/h 北2m/s
3日 06時   晴れ 17度 86% 0mm/h 北北東2m/s
3日 09時   晴れ 19度 80% 0mm/h 北東2m/s
3日 12時   晴れ 21度 68% 0mm/h 東北東2m/s
3日 15時   曇り 22度 66% 0mm/h 北東2m/s
3日 18時   曇り 20度 72% 0mm/h 東北東2m/s
3日 21時   曇り 20度 74% 0mm/h 東北東2m/s

週間天気
10/4(木) 曇時々雨 19-23度 50%
10/5(金) 曇時々晴 19-24度 30%
10/6(土) 晴時々曇 17-25度 20%
10/7(日) 曇時々晴 18-25度 30%
10/8(月) 曇り 20-24度 40%
10/9(火) 曇り 18-23度 40%

デザイン変更に耐えられるように予めテスト機構を作っていたからあっさり対応できた。Yahoo!天気予報には今日明日の天気予報と週間天気予報があるが、嬉しいことに週間天気予報のルーチンは無変更のままでよかった。たとえ小さいスクリプトであってもテストを書くのは大事だとつくづく感じた。

今ならscRubytなどのweb scraping libraryを使うべきなんだろうけど、このレベルならまだ正規表現でも十分対応できる。

ずぃーしぇる?

俺は「ぜっとしぇる」と読んでるけど、みんなはずぃーしぇる派なんだろうか?ちなみにウルトラマンを倒した最強最後の怪獣(だった?*1)「ゼットン」は「Z」と「ん」の日英最後の文字をくっつけたのが名前の由来だとさ。つまりzshゼットンは同類。

*1:詳しくないんでよく知らん

optparse.rbを使ったスクリプトのオプション補完を自動生成する

zshのオプション補完

zshの補完が恐しいまでに高機能なのはユーザーみんな知っているだろう。なんと、オプションまでも補完してくれる。たとえば、lsだって以下の状態でタブを打つと、

$ ls -[Tab]
option
--all                 -a  -- list entries starting with .
--almost-all          -A  -- list all except . and ..
--author                  -- print the author of each file
--block-size              -- specify block size
--classify            -F  -- append file type indicators
--color                   -- control use of color
--dereference         -L  -- list referenced file for sym link
--directory           -d  -- list directory entries instead of contents
--dired               -D  -- generate output designed for Emacs' dired mode
--escape              -b  -- print octal escapes for control characters
--file-type           -p  -- append file type indicators except *
--full-time               -- list both full date and full time
--help                    -- display help information
--hide-control-chars  -q  -- hide control chars
--human-readable      -h  -- print sizes in human readable form
--ignore              -I  -- don't list entire matching pattern
--ignore-backups      -B  -- don't list entries ending with ~
--inode               -i  -- print file inode numbers
--kilobytes           -k  -- use block size of 1k
--literal             -N  -- print raw characters
--no-group            -G  -- inhibit display of group information
--numeric-uid-gid     -n  -- numeric uid, gid
--quote-name          -Q  -- quote names
--recursive           -R  -- list subdirectories recursively
--reverse             -r  -- reverse sort order
--si                  -H  -- sizes in human readable form; powers of 1000
--size                -s  -- display size of each file in blocks
--tabsize             -T  -- specify tab size
--time                    -- specify time to show
--time-style              -- show times using specified style
--version                 -- display version information
--width               -w  -- specify screen width
-1                        -- single column output
-C                        -- list entries in columns sorted vertically
-S                        -- sort by size
-U                        -- unsorted
-X                        -- sort by extension
-c                        -- status change time
-f                        -- unsorted, all, short list
-g                        -- long listing but without owner information
-l                        -- long listing
-m                        -- comma separated
-o                        -- no group, long
-t                        -- sort by modification time
-u                        -- access time
-v                        -- sort by version (filename treated numerically)
-x                        -- sort horizontally
--dereference-command-line                      --indicator-style          --sort
--dereference-command-line-symlink-to-dir       --quoting-style
--format                                        --show-control-chars

補完候補がリストされるばかりか説明まで書いててくれる。物忘れの激しい人にも優しいなんとも親切設計だ。あ、もちろん

autoload -U compinit
compinit

の2行は.zshrcに入っているよね?入れてなければ今すぐ入れろ!
もちろんこれだけではなくてオプションの引数も文脈に合った補完をさせることもできるし、代表的なコマンドについては標準でそうなっている。ディレクトリが欲しいときはディレクトリのみが補完候補になり、特定の集合の要素が欲しければその集合が候補になる。

optparse.rbによるオプション宣言

さて、rubyistならばオプションつきのコマンドは標準添付のoptparse.rbを使って書いていると思う。もちろんRAAに行けば他のコマンドラインオプションのライブラリはあるけれど、標準添付なので今はoptparseについてのみ考える。たとえば、例として以下のRubyスクリプトop.rbを考える。これ自体はなにも処理しないが、気にしないでくれ。あくまでオプション宣言が主題だ。

#!/usr/bin/env ruby
require 'optparse'
ARGV.options {|o|
  o.on("--hoge=x", "option hoge") {|x| @hoge=x}
  o.on("-a", "option a"){ @a=1 }
  o.on("-b", "option b"){ @a=1 }
  o.on("-c", "option c"){ @a=1 }
  o.parse!
}

実にわかりやすいRubyらしい宣言だ^^

op.rbの補完関数

さて、op.rbの補完関数を書いてみよう。とりあえず ~/zsh/Complete 以下にユーザー定義の補完関数ファイルが置いてあるものとする。こんな感じになる。補完関数の詳しい書き方についてはhttp://www.ayu.ics.keio.ac.jp/members/mukai/translate/write_zsh_functions.htmlが詳しい。あくまでもこんな風に書くものと思ってくれ。

#compdef op.rb

typeset -A opt_args
local context state line

_arguments -s -S \
  "--hoge[option hoge]" \
  "-a[option a]" \
  "-b[option b]" \
  "-c[option c]" \
  '*:file:_files' && return 0

このファイルを ~/zsh/Complete/_op.rb というファイル名で保存してzshを再起動するかcompinitを実行すれば op.rb のオプション補完ができるようになった。万歳!

$ op.rb -[Tab]
option
--hoge  -- option hoge
-a      -- option a
-b      -- option b
-c      -- option c

DRY原則

話はここで終わりではない。いや、ここからが大切だ。開発が進んでいくにつれてオプションが増えたり変更されたりするものだ。zshで快適に補完するためにはRubyスクリプトだけでなく補完関数ファイルをも書き換えないといけないのだ!!オプションを追加して、さらに説明をも書かないといけない。なんと面倒臭い><それだけでなくて補完関数が最新版に対応できなくなることは目に見えてる。ここでDRY原則に則ってみると、スクリプトを更新したら自動で補完関数ファイルを更新できないかと考えないといけない。

そこで俺は補完関数ファイル自動更新スクリプトを作ってみた。ただ、今のところオプションの文脈依存補完はサポートしていない、課題にさせてくれ。オプション補完ができるだけでもだいぶ快適になる。以下からもってけ!

http://www.rubyist.net/~rubikitch/archive/zshcomplete.txt

zshcomplete.rbという名前で$LOAD_PATH($:)の通ったディレクトリに保存する。

これを使うために必要な.zshrcの設定は以下の通り…compinitの前のfpathの設定はしっかりとね。あとはふたつの関数を加えておこう。

mkdir -p ~/zsh/Completion
touch ~/zsh/Completion/dummy
fpath=(~/zsh/Completion $fpath)
autoload -U ~/zsh/Completion/*(:t)
autoload -U compinit
compinit

generate-complete-function/ruby/optparse () {
    local cmpl
    cmpl=~/zsh/Completion/_`basename $1`
    existp=`[ -f $cmpl ]; echo $?`
    ruby -rzshcomplete $1 > $cmpl
    if [ $existp = 0 ]; then
        reload-complete-functions
    else
        compinit
    fi
}
reload-complete-functions() {
    local f
    f=(~/zsh/Completion/*(.))
    unfunction $f:t 2> /dev/null
    autoload -U $f:t
}

使ってみる

設定後にzshcompleteを使ってみる。オプションが多いrcovもこの通り!

$ generate-complete-function/ruby/optparse =rcov
$ rcov -[Tab]
option
--aggregate                              -- Aggregate data from previous runsin FILE.
--annotate            -a                 -- Generate annotated source code.
--diff-cmd                               -- Use PROGNAME for --text-coverage-diff.(de
--exclude             -x                 -- Don't generate info for files matching ap
--exclude-only                           -- Skip info only for files matching thegive
--gcc                                    -- Dump uncovered line in GCC error format.
--help                -h                 -- Show extended help message
--include             -I                 -- Prepend PATHS to $: (colon separated list
--include-file        -i                 -- Generate info for files matching apattern
--no-callsites        --callsites        -- Show callsites in generated XHTML report.
--no-color            -n                 -- Create colorblind-safe output.
--no-comments         --comments         -- Mark all comments by default.(default: --
--no-html             --html             -- Generate HTML output.(default: --html)
--no-rcovrt                              -- Do not use the optimized C runtime.(will
--no-validator-links  --validator-links  -- Add link to W3C's validation services.(de
--no-xrefs            --xrefs            -- Generate fully cross-referenced report.(i
--only-uncovered                         -- Same as --threshold 100
--output              -o                 -- Destination directory.
--profile             -p                 -- Generate bogo-profiling info.
--rails                                  -- Skip config/, environment/ and vendor/.
--range               -r                 -- Color scale range for profiling info (dB)
--replace-progname                       -- Replace _rcov.rb when loading the .rb fil
--report-cov-bug                         -- Report coverage analysis bug for themetho
--save                                   -- Save coverage data to FILE,for later use
--sort                                   -- Sort files in the output by the specified
--sort-reverse                           -- Reverse files in the output.
--test-unit-only                         -- Only trace code executed in TestCases.
--text-counts                            -- Dump execution counts in plaintext.
--text-coverage                          -- Dump coverage info to stdout, usingANSI c
--text-coverage-diff  -D                 -- Compare code coverage with saved statein
--text-report         -T                 -- Dump detailed plain-text report to stdout
--text-summary        -t                 -- Dump plain-text summary to stdout.
--threshold                              -- Only list files with coverage < INT %.(de
--version                                -- Show version
-w                                       -- Turn warnings on (like ruby).

今後の展望

~/.ruby-zshcomplete により精巧な補完関数ファイル生成に必要な情報をDSLで書けるようにしたい。

追記

http://subtech.g.hatena.ne.jp/secondlife/20071003/1191394868 のコメントを反映させました。ありがとうございます。

generate-complete-function/ruby/optparseに補完関数ファイルの存在チェックをやらせました。