2008-08-14 [長年日記]
_ NIGHT ZOO 行ってきた

いしかわ動物園で NIGHT ZOO という企画をやっていたので行ってきた。
今回は三脚という新たな武器を手に入れたので喜び勇んで使ってみた。しかしまともに撮れた写真は案外少なかった。そもそもこのデジカメは暗い写真をあまりまともに撮れないんだよなー。
冷静になって考えると、夜行性の動物ってどれだけいるんだろうか。やたら人はいたが、夜ならではの醍醐味はどこにあったのかなぁ。まぁそんなに暑くないっていうのはメリットだし、なんとなくお祭り気分で子どもたちはテンション上がってたけど。
2008-08-11 [長年日記]
_ periodic_forwarder を書いた
cron シリーズ、何個目だ?
以前から cron の管理が面倒くさいことは書いているのですが、periodic, run-parts を利用することで厳密な時間設定の必要ないスクリプトの管理は比較的楽になりました。しかし、すべてのスクリプトがシステムワイドな cron ディレクトリにあると都合の悪い場合もあります。
そこでこんなものを書いてみました。
/lang/ruby/misc/periodic_forwarder CodeRepos::Share
どういうものかと言うと、
/etc/periodic/
|-- daily/
| `-- periodic_forwarder* -> periodic /BASE_DIR/foo/FILLING_PATH/daily/
|-- monthly/
| `-- periodic_forwarder* -> periodic /BASE_DIR/foo/FILLING_PATH/monthly/
`-- weekly/
`-- periodic_forwarder* -> periodic /BASE_DIR/foo/FILLING_PATH/weekly/
こんな感じで、システムワイドな cron ディレクトリにこのスクリプトを置いておく(実際には link で ok)と、任意のディレクトリにこれを forward して、対応する period のスクリプト群を periodic, run-parts によって実行してくれるというものです。
※ 上の例は *BSD フレーバーなシステムの話ですが、Linux の /etc/cron.*/ 方式にも対応しています。
どういう場合に便利かというと、例えば
/var/www/HOST1/ /var/www/HOST2/ /var/www/HOST3/ /var/www/HOST4/
みたいに virtual host が設定されているとして、これらの HOST ごとに異なる仕事を定期的に実行させたいときなどに威力を発揮します。
/var/www/HOST1/cron/ `-- daily/ /var/www/HOST2/cron/ `-- weekly/ /var/www/HOST3/cron/ `-- daily/ /var/www/HOST4/cron/ `-- monthly/
こんな感じで定期実行するスクリプトを置くディレクトリを用意したら、
BASE_DIR = /var/www FILLING_PATH = cron
となるように設定してこのスクリプトをセットしておきます。
これで、ある HOST のための cron ジョブはちゃんとその HOST 用のリポジトリの中に収めておくことができます。
設定をスクリプトと分離する処理はちゃんと書いてない*1ので、ちょっと中身を書き換えてもらわないといけないですけど。すいません。一応起動時のオプションで設定を与えることはできるようにしてあるので、お試しで動かすことはできると思います。
とりあえず自分はこれで目的が達成できそうなので、今は結構ホクホクしています。
しかしこういう、
- オプションを指定できる
- ディレクトリを自前でなめる
ものをシステム管理的によく書くのですが、どうも自分はこの二つが同時に入っているコードのテストを書くのが苦手です。Rake とか使ってコードを置くディレクトリからテスト用のコードを生成するとかすればいいんだろうけど、面倒くさくなってベタで書いちゃうからテストを公開できなくなっちゃうんですよねぇ。うーん。まだ全然へたくそだ。
*1 いや実際には書いたんだけど、shell スクリプトと設定を共有するという特殊な方法を考えているので現在リポジトリにはその部分は上げてません。
2008-08-10 [長年日記]
_ shell スクリプトと Ruby スクリプトで設定を共有する
あらためて書くほどたいした話じゃないです。
細々した仕事をするスクリプトの多くは基本的に sh スクリプトが多いと思うのですが、やはり sh スクリプトでは多少荷が重い仕事もあります。awk や sed などを使うのはまぁ常套手段ではありますが、あんまり激しくコマンドを呼び出すとそれだけ負荷が高くなっていきます。そういう場合、自分は Ruby で書き直してしまうのですが、すでに稼働中の sh スクリプトを含めて毎回同じような設定を書いていることに気がつきました。わずか数行の代入文とは言え、なんかこれ無駄な感じがします。
sh スクリプトの変数代入は基本的に以下のように書きます。
VAR=VAL
自分の場合、sh スクリプトの変数は常に大文字で書くようにしているのですが、よく見たらコレ、
Ruby の定数定義と同じじゃん
と気づきました。しかも Ruby はバッククォートを使った別プロセスの結果の取得も sh スクリプトと同じように動きます。つまり、特別なフォーマット(XML やら YAML やら)を用意しなくても、特別なパーサを用意しなくても、同じ設定をそのまま sh でも Ruby でも読み込むことができるということです。
具体的にやることは以下のように、

- 変数定義の部分を別ファイルに切り出し
- sh スクリプトでは . で 1 のファイルを読み込む
- Ruby スクリプトでは load で 1 のファイルを読み込む
だけです。
Ruby の方で定数はちょっと扱いにくいなぁと思ったら Object.const_defined?(), Object.const_get() を使って変数に取り込むことも可能です。
最初から小文字で変数を書いて Ruby でもローカル変数として取り込めばいいじゃんという意見もあるかもしれませんけど、Ruby スクリプト側は恐らくコードと設定の分離を意識して書きたいだろうから、ローカル変数にいきなり取り込むよりいったん定数で取り込んでおいて、それを必要に応じて使えるようにしておいた方が気持ちいいような気がします。で、この処理を module に追い出してしまえば毎回書く必要もありません。うん、いいアイディアだ。
2008-07-27 [長年日記]
_ Yapra の Feed::Custom で attribute を扱えるようにする
※ 取り込まれたのでもうこのパッチは不要です。
えー。github にアカウントは作るところまではいったのですが面倒になったのでとりあえずここに貼ります。
使い方はこんな感じ。
- module: Feed::Custom
config:
url: URL
extract_xpath:
capture: '//div[@class="FOO"]//ul'
split: '/li'
link: # <- ココ
first_node: '//a[@class="BAR"][1]'
attr: :href
# 従来の書き方
title: '//a[@class="BAR"]/text()'
extract_xpath 内に書けるものは
- capture
- url の中で処理を開始したい node を特定する xpath
- split
- capture した elemens を each で回す際に分割で利用する xpath
- これ以外
- feed item 内に生成したい「要素」の名前を key に、「内容」となる HTML 文字列を取得したい node を特定する xpath を value にとる Hash
です。前回の話の中でチラッと言っていたのは、value は item の「内容」になるはずなのに「HTML 文字列」が取れてもあんまり嬉しくないなぁという話でした。HTML の「内容」は xpath で一発で取れるけど「属性値」も取りたいということです。
で、はたと思いついて、value をさらに first_node と attr を key に持つ Hash にしてみました。
コードはこんな感じ。ベッタベタです。いじったのは Feed::Custom ではなく MechanizeBase の方になります。
@@ -11,7 +11,14 @@ class Yapra::Plugin::MechanizeBase < Yapra::Plugin::Base
def extract_attribute_from element, item
if plugin_config['extract_xpath']
plugin_config['extract_xpath'].each do |k, v|
- value = element.search(v).to_html.toutf8
+ value = nil
+ case v.class.to_s
+ when "String"
+ value = element.search(v).to_html.toutf8
+ when "Hash"
+ ele = element.at( v['first_node'] )
+ value = ( ele.nil? ) ? nil : ele.get_attribute( v['attr'] )
+ end
set_attribute_to item, k, value
end
end
Hpricot には search() と at() があって、search() で返ってくるのは Hpricot::Elements, at() で返ってくるのは Hpricot::Elem*1 でして、at() が呼べれば属性値は get_attribute() ですぐ取れます。(引数は、サンプルの YAML では symbol で書いてますが文字列でもいいです。)
最初このためにメソッドと同じ「at」を key として使おうと思ったのですが、attr と区別しにくいので first_node にしてみました。こういう要求は際限がなさそうだけど、この程度で収まるなら at() の挙動もはっきり分かるし、悪くないかな?*2
あ、toutf8 を呼んでないな。んー。あれでもこれページを取得したときに toutf8 した方がいいような? 内容が短くなってからの方が判別難しくなっちゃいませんか?
cf.
p.s.
先日ぼやいていた YAML は grep まで含めて動きました。いやー便利便利。
_ Yuanying [このパッチってもらっちゃっても良いのでしょうか?? あと、toutf8の件ですが、ページ取得したときにtout..]
_ wtnabe [こんなデキの悪い子でよければもらってやってください。 そのうちちゃんと pull request できるように..]