トップ 最新 追記

2008-12-02 [長年日記]

_ RotationMaker を作った

最近は iCal づいています。

/lang/ruby/misc/rotation-maker – CodeRepos::Share – Trac

これは何か?

README より抜粋

  • 例えば毎週月曜に何かやります
  • メンバーが 5人いて担当は 2人ずつとします
  • いつ誰が担当するのか先々の予定を算出したい
  • ハッピーマンデーがあるので、その場合は火曜にスライド

みたいなときに便利かもしれないツールです。

使い方

オプションを与えずに起動するとなんとなく分かります。たぶん。

$ ruby rotation-maker.rb
Usage: rotation-maker [options]
    -x, --exclude DATE
        --exclude-from-ical ICS
        --exclude-from-file FILE
        --exmethod [METHOD]
    -s, --dtstart DATE
    -e, --dtend DATE
    -i, --interval INTERVAL
    -d, --date EVENT DATE
        --date-from-ical ICS
        --date-from-file FILE
    -m, --member MEMBER
        --member-from-file FILE
    -a, --atonce NUMBER
    -y, --yaml FILE

最低限必要なものは

  • date
  • member

です。-d や -m を複数書いてもいいですが、それぞれ1行1アイテム形式のファイルから読み込ませることもできます。

また、date ではなく -s START, -e END, -i INTERVAL でくり返しの予定を記述することもできます。INTERVAL は基本的に整数ですが、activesupport をインストールしてあれば week, month, year も指定できます。

exclude で除外する日付を指定します。デフォルトでは除外日に予定が当たったらその日はスキップします。exmethod オプションに backload を与えていたら次の日にずらします。次の日も除外日だったら除外日でなくなるまでスライドします。*1

あ、今気づいたけど slide オプションは YAML からしか与えられないじゃないか。後で直します。

YAML で設定を書く場合はこんな感じで書きます。

dtstart: '2008-12-01'
dtend: '2009-03-31'
interval: week
atonce: 2
exmethod: backload
members:
  - abc
  - def
  - ghi
  - jkl
  - mno
  - pqr
  - stu
excludes:
  # PATH or URI
  ical: http://www.google.com/calendar/ical/japanese%40holiday.calendar.google.com/public/basic.ics

出力部分は自分で書いてください^^;

例えばこんな感じのものを用意すると iCal 形式で予定を吐き出せます。

#! /usr/bin/env ruby
# -*- coding: utf-8 -*-

require File.dirname( __FILE__ ) + '/rotation-maker'
require 'erb'
require 'nkf'

def main
  rot = RotationMaker.new.run
  puts NKF::nkf( '-w -Lw', ERB.new( template, nil, '-' ).result( binding ) )
end

def serialized_time( time )
  return time.gsub( /-/, '' )
end

def template
  return <<EOD
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Asia/Tokyo
X-LIC-LOCATION:Asia/Tokyo
BEGIN:STANDARD
TZOFFSETFROM:+0900
TZOFFSETTO:+0900
TZNAME:JST
DTSTART:19700101T000000
END:STANDARD
END:VTIMEZONE
<%- rot.each do |r| -%>
BEGIN:VEVENT
SUMMARY:社内勉強会
DESCRIPTION:発表 <%= r['member'].join( ', ' ) %>
DTSTART;TZID=Asia/Tokyo:<%= serialized_time( r['date'] ) %>T180000
DTEND;TZID=Asia/Tokyo:<%= serialized_time( r['date'] ) %>T190000
END:VEVENT
<%- end -%>
END:VCALENDAR
EOD
end

main

結果はこんな感じ。

ハッピーマンデーを除けてスケジューリングできた様子

あ。あれ? Google Calendar にある Japanese Holidays の成人の日の情報が

DTSTART;VALUE=DATE:20090112
DTEND;VALUE=DATE:20090113
DTSTAMP:20081203T115909Z

になってますね。見た目は 12日だけに見えるんだけど。えーまぁ、これはデータの問題ということで。

問題というか

iCal の Rrule *2って、くり返しの終了が指定されていない場合、無限に続いてしまいますよね? これをどう扱ったらいいのかが全然分かっていません。今回、除外日を設定できるようにしたため、除外日を Array にして Array#include? でチェックしているのですが、Array で無限なんて扱えないですよね。

実際には Vpim::Icalendar がイベントの日時情報を DateTime ではなく Time で扱っているため、2037年問題に引っかかって有限の配列を取得することができているのですが、本当に無限のものを扱いたい場合は全部を計算で扱う Array 以外の何かが必要になるんじゃないかと思っています。とにかく動くものを作りたかったのでその辺突っ込んで調べてませんが。

またこのため Time の ArgumentError を rescue してそのまま知らん顔するという暴挙に出ています。こんなんじゃいかんよなぁ。limit を設けるようにした方がいいのか、Icalendar に戻した方がいいのか。Icalendar は DateTime で扱っているので少なくとも 2037年問題には引っかからなそうなのですが。Icalendar にこの include?( date ) みたいな便利メソッドがあれば Array に展開することなく処理を続けられるのかな。ただ、実際にはどこかで区切らないと処理が終わらないのでどっちみち limit は要るのか。

Icalendar は Icalendar で to_ical がマルチバイト文字列をぶった切る問題があるしなぁ。ふーむ悩ましい。

何かご存知の方はツッコミください。

TODO とか

  • slide オプションをコマンドラインからも与えられるようにする
  • slide オプションで予定の前倒しもできるようにする
  • 土日を除外するのはもっと簡単にできてもいいような気がする

げ。

*atonce をコマンドラインから与えられるようにする><

参考

2008-12-04 追記

前のエントリのツールも一緒に checkout できるようにしました。手軽に cat できるなら月水金カレンダーとかでも割とすぐ作れるので、こっちで頑張りすぎる必要ないかなぁという気がしてきました。

Tags: iCal Ruby

*1 年末年始分、丸ごとスライドさせることもできます。

*2 例えば毎週月曜といった形で設定されたイベント


2008-12-04 [長年日記]

_ AutoPagerize をイントラアプリで

AutoPagerize を常用するようになってから初めて CodeRepos の Trac を見に行って気づいた。

何もせずに changeset を遡れる!

AutoPagerize のメリットは理解していたつもりだったけど、今回がいちばん感動した。なんでかなと思ったんだけど、普段 AutoPagerize の対象になるページは

  • ニュースサイトの分割記事(最近これ多すぎ)
  • 検索結果一覧

くらいで、実はこの二つは AutoPagerize がなくてもある程度の分量の情報を一目で確認できる。

しかし changeset の場合は極端な話、ほとんど情報量のない changeset が途中で混ざっていたりする*1ので、一つずつめくらなければいけない状態が結構ストレスだったようだ。自覚はなかったけど、AutoPagerize が利いたときの驚きのでかさがそれを示している。なるほど、これが「ゼロクリックにする革命」なわけだ。

ということで早速イントラの Trac でも AutoPagerize が利くようにしたいなと思ったんだけど、ここで二つ問題が。

  • イントラアプリの SITEINFO を Wedata に上げるわけにはいかない
  • かといって localhost の AutoPagerize を書き換える方法では恩恵に預かれるのは自分一人。喜びを分かち合うことができない。

そこで今回はイントラ内に HTTP で JSON を提供してくれる場所を用意することにした。これなら最初の一回は AutoPagerize の書き換えが必要だけど、以降は SITEINFO を追加していくとどんどん対応アプリを増やすことができる。

イントラの HTTP で JSON を serve

うちの場合、Subversion を WebDAV で運用しているので、Web サーバ周りで特にやることはない。具体的な方法は

JSON を Subversion リポジトリに突っ込んでその URL を AutoPagerize に教えてやるだけ

コード上では

var SITEINFO_IMPORT_URLS = [
   'http://wedata.net/databases/AutoPagerize/items.json',
]

にイントラの JSON の URL を書き足してやればオーケー。

JSON ファイルを作る

SITEINFO を作るにはまず最初

var SITEINFO = [
  {
     url: ,
     nextLink,
     pageElement,
     exampleUrl
  }
]

を書き換えていく。この過程は Wedata に上げる場合と一緒。

Wedata に上げる場合はこのままできあがったものをコピペするだけでよい。AutoPagerize では JSON を受け取ってパースするが、先ほどコピペしたデータを Wedata 側で JSON にして提供してくれるので Wedata に上げる場合は単なるコピペでよいのだ。

しかしイントラ JSON の場合は

自分で Wedata 形式に合わせて JSON にエンコードしてやる必要がある

ここだけが違う。

まず Wedata からどういうフォーマットでデータが来ているのか確認。この間 作ったもので見てみるとこんな感じ。

{
  "name": "i\u30bf\u30a6\u30f3\u30da\u30fc\u30b8 (Lite)",
  "updated_at": "2008-11-29T21:33:09+09:00",
  "database_resource_url": "http:\/\/wedata.net\/databases\/AutoPagerize",
  "created_by": "wtnabe",
  "resource_url": "http:\/\/wedata.net\/items\/25850",
  "data": {
    "pageElement": "\/\/table[descendant::hr][position() > 1]",
    "url": "^http:\/\/(?:www\\.)?itp\\.ne\\.jp\/servlet\/jp\\.ne\\.itp ...",
    "nextLink": "\/\/a[text() = \"\u6b21\u3000\u30da\u30fc\u30b8\"]",
    "exampleUrl": "http:\/\/itp.ne.jp\/servlet\/jp.ne.itp.sear.SKWSVmai ..."
  },
  "created_at": "2008-11-28T11:18:17+09:00"
}

ということは

{
  "data": {
    "pageElement": "",
    "url": "",
    "nextLink": "",
    "exampleUrl": ""
  }
}

があればいいんだな。全体としては

[
  {
    "data": {
      "pageElement": "",
      "url": "",
      "nextLink": "",
      "exampleUrl": ""
    }
  },
  {
    "data": {
      "pageElement": "",
      "url": "",
      "nextLink": "",
      "exampleUrl": ""
    }
  },
  ...
]

こんな感じになる。

JSON は意外なほどきっちりしていて、key も文字列でなければいけないとか文字列は " で囲む必要があったりするんだけど、そういうのはいちいち自分で気にするのはばからしいので、完成したら適当なツールを使ってエンコードするとよい。

自分の場合は AutoPagerize 内に直接書いた SITEINFO を別ファイルに書き出して、Ruby なり PHP なりでエンコードして、それを先ほどの JSON ファイルの方にコピペしている。

イントラ AutoPagerize、これは便利。

ちなみに Trac の SITEINFO は 0.10 系と 0.11 系で異なる。CodeRepos はこの段階で 0.11 系でイントラのものは 0.10 系だったので丸ごとコピペしただけでは動かなかった。

Tags: Firefox

*1 typo を直したとか


2008-12-05 [長年日記]

_ php-shell が便利すぎてヤバい件

PHP には php -a というインタラクティブモードが一応あるんだけど、これがビックリするくらい使えない。いちばんビックリなのは

readline モジュールを読み込んでいないと <?php ?> を省略できない

ってこと。何それ? PHP を書くよって最初に明示してるのに <?php を省略できないとは。

これにビックリしてインタラクティブシェルを探してみたら、いくつか見つかった。中でもこれはすごい。

~jk php shell

  • PHP 5 以降
  • pear コマンドでインストールでき、irb のように実行ファイルとしてインストールされる
  • ビルド時の --with-readline ではなく、readline.so を追加する形で利用できる
  • つまり補完が効く
  • 関数の使い方をその場で調べられる

どうです。なかなかすごいっしょ。

例えばこんな風に使える。

$ php-shell.sh
PHP-Shell - Version 0.3.1, with readline() support
(c) 2006, Jan Kneschke <jan@kneschke.de>

>> use '?' to open the inline help

>> get_<TAB>
get_browser(                 get_headers(
get_cfg_var(                 get_html_translation_table(
get_class(                   get_include_path(
get_class_methods(           get_included_files(
get_class_vars(              get_loaded_extensions(
get_current_user(            get_magic_quotes_gpc(
get_declared_classes(        get_magic_quotes_runtime(
get_declared_interfaces(     get_meta_tags(
get_defined_constants(       get_object_vars(
get_defined_functions(       get_parent_class(
get_defined_vars(            get_required_files(
get_extension_funcs(         get_resource_type(
>> ? get_class()
'/**
* Retrieves the class name
*
* @params [object object]
* @return string
*/
'

どうですか。

※ ただ、PHP では readline モジュール自体がポピュラーでないのか、手元の環境ではシステム標準のパッケージだけで readline が有効な PHP を構築できたのは CentOS だけだった。Debian も MacPorts, Fink もダメだった。FreeBSD ではたぶんイケるけど試してない。

おまけ1(2月4日に気づいた)

>> ?
'Inline Help:
  >> quit
    leaves the shell
  >> ?
    show this help
  >> ? license
    show license of the shell
  >> :set <var>
    set a shell variable
  >> ? <var>
    show the DocComment a Class, Method or Function
    e.g.: ? fopen(), ? PHP_Shell, ? $__shell
  >> p <var>
    print the variable verbosly
  >> r <filename>
    load a php-script and execute each line
'

なんと、p も定義されている。irb 使いならこれを使わずに何を使うのだという話ですよ! まぁ中身はただの var_export() なんだけど。

おまけ2(同じく2月4日に気づいた)

こういう使い方がなかなか便利かも。

php -c INIFILE -q /PATH/TO/php-shell-cmd.php

デフォルトの php-shell.sh を起動するだけだと、例えば案件独自の設定などを反映させることができない。そこでこの方法。これならライブラリの読み込みもスムーズに行える。php-shell は組み込みの関数だけではなくて PHP で書かれたライブラリについても ? で phpdoc コメントを表示させることができるので、真面目に書かれたライブラリを使っているとものすごく便利に使える。

オレ? オレはもちろん大真面目に作っているから超便利に使っているよ!

Tags: PHP

2008-12-06 [長年日記]

_ CGI を rackup してみた

Ruby は自分の大好きな言語だが、実は長く運用する Web アプリを Ruby で書いたことはない。cgi.rb の評判はずいぶん前から芳しくないし、決定打となるフレームワークの不在が長く続いたこと、すでに PHP を使っていたことが大きな理由だった。

Rails が登場した。勉強した。「うーん、なんか DBMS とか要らないんだけど、どうしたらいいのよ?」と思っているうちに世間ではすっかり定着、代わりに自分の中では興味は薄れていった。そうこうしているうちに Rails の問題点もちょこちょこ指摘されるようになり、prototype.js とともに先駆者ゆえの苦難を味わっているなぁと感じている今日この頃。

Merb だなんだと言われていた中、Rack が登場した。これだ!と思った。こういうシンプルなやつが欲しかったんだよ! しかしそれから特に何の理由もないまま一年半の月日が流れた。なんかこんなんばっかだな。RSS の登場により情報の収集は速くなったけど、知識にするための時間、手を動かして「つかむ」までの時間は、自分という人間の性能が変わらない*1のでちっとも短くならない。

そんなオレ語りはどうでもよく、今回はようやく自分で Rack を使ってみましたよというお話。しかも、あんまり見ない、WEBrick と CGI の二本立て。試したのは gem で入れた rack 0.4.0

Rack の使い方は二段階ある

超簡単なチュートリアルを読んでもリファレンスを見てもこの違いを明確に意識することができなくて苦労した。Rack の機能には以下の二段階がある。

  1. Handler によってアプリケーションサーバの違いを抽象化する
  2. rackup によって便利なミドルウェアを使う

この違いは

  • ruby から直接 Rack::Handler::FOO.run( App.new )
  • rackup から run( App.new )

の違いからくる。要するに

ミドルウェアを use したければ rackup しろ

ってこと。

逆に言うと rackup しなくても Request, Response の抽象化は行えるので基本的なことはできる。

ではそれぞれの方法をもう少し詳しく見てみる。

※ ブコメいただきました。ミドルウェアの中にアプリケーションを突っ込んで、ミドルウェアを Handler に渡すという方法もあるようです。なんかちょっと不思議な感じもしますが、そもそも内部ではミドルウェアでアプリケーションを再帰的に wrap する構造のようです。

lib/rack/lobster.rb at master from chneukirchen's rack ― GitHub

ruby から直接 Handler を呼ぶ方法

WEBrick の例

komagata さんの記事から拝借。

ウノウラボ Unoh Labs: RackでWebアプリのWebサーバー依存を無くす

#!/usr/bin/env ruby
require 'rubygems'
require 'rack'

class HelloRack
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
  end
end

Rack::Handler::WEBrick.run HelloRack.new, :Port => 3000

この hello-rack.rb を

ruby hello-rack.rb

で起こし、これにブラウザからアクセスすると

Hello, Rack

と表示される。うむ。

CGI の例

同じことは CGI でも当然できる。

#!/usr/bin/env ruby
require 'rubygems'
require 'rack'

class HelloRack
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
  end
end

Rack::Handler::CGI.run HelloRack.new

こんな感じで Handler を CGI にしてやれば ok. これを

  • Web サーバ配下の CGI の実行可能なディレクトリに置いて
  • 実行権限を付けて

ブラウザからアクセスしてやるとさっきと同じように

Hello, Rack

と表示される。めでたしめでたし。

これで cgi.rb を使わなくても Ruby で CGI を書けるようになったよ!

CGIAlt があるとか言わないでね。)

気をつけなきゃいけないこと

以下は自分のためのメモ

  • CGI の場合は Web サーバがアプリケーションを勝手に起こしてくれるけど、実行権限の付与が必要
  • WEBrick(など)の場合は自分でアプリを起こしてやらないといけない

自分の場合は生 CGI を触る機会が減っているので実行権限の付与をしょっちゅう忘れる。

でも Rack の本当の威力を味わうなら rackup

rackup は

rackup -s webrick [config file]

などとして server(handler) を指定してアプリケーションサーバを起動する。rackup が行われると、中で

Rack::Builder

がアプリケーションサーバの環境を作ってくれ、自分の書いたアプリケーションはこの中で実行される。

ここで初めて use が使えるようになる。

もうちょっと細かく見てみよう。

use は Rack::Builder の instance_method

Rack でミドルウェアを追加する use は Rack::Builder の instance_method となっている。

irb(main):001:0> require 'rack'
=> true
irb(main):002:0> Rack::Builder.methods.grep( /use/ )
=> []
irb(main):003:0> Rack::Builder.instance_methods.grep( /use/ )
=> ["use"]

つまり

require 'rack'

しただけでは use は使えない。

rackup の流れ

rackup を読んでみると、アプリケーションは rackup の中で以下のように実行されている。(詳細は端折ってある)

Rack::Builder.new {
  run inner_app
}

つまりそういうこと。Rack の便利機能を使いたければ rackup すべし。

では実際に rackup してみる。

WEBrick の例
直接起動で use の使えない様子

さきほどの hello-rack.rb に use を加える。

#!/usr/bin/env ruby
require 'rubygems'
require 'rack'

use Rack::ShowExceptions

class HelloRack
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
  end
end

Rack::Handler.WEBrick.run HelloRack.new, :Port => 3000

すると、当然ながら

$ ruby hello-rack.rb
hello-rack.rb:6: undefined method `use' for main:Object (NoMethodError)

怒られる。use なんて使えない。

rackup してみる
#! /usr/bin/env ruby
require 'rubygems'
require 'rack'

use Rack::ShowExceptions

class HelloRack
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
    cho_www
  end
end

run HelloRack.new

ここで注目してほしいのは、

  • cho_www という syntax error をアプリケーションの中に仕込んであるところ
  • run の呼び方を変えたところ

どうも

rackup するときは Handler は外から(rackup の -s オプションで)指定してあげた方がなんかうまくいく感じ。

これを hello-rack.ru の名前で保存し、

rackup -s webrick -p 3000 hello-rack.ru

WEBrick を rackup してブラウザ上で Trace Back

で実行。

ご覧のようにブラウザ上に Exception を表示できる。画像では端折ってあるが TraceBack も表示できる。当然のことながら cho_www を削除すれば普通に動く。

CGI の例

直接起こして use してダメなのはさっき WEBrick で見たので省略。

rackup する

ru ファイルはさっき用意した hello-rack.ru をそのまま利用。CGI の方は以下のようにする。

#! /bin/sh

/opt/local/bin/rackup hello-rack.ru

CGI でも use Rack::ShowExceptions が使えた!

パスが /opt/local/bin なのは MacPorts で入れた Ruby で gem を使ってインストールしているから。rackup のパスは適宜調整すること。

これを hello-rack.cgi の名前で保存、ごにょごにょしてブラウザからアクセスしてみる。画像の中の URL の違いに注目。確かに CGI で動いている。

ただし、当然のことながら CGI で rackup するのは負荷的には不利。

従来の流れ
  1. shebang から Ruby を起動
  2. 自分自身を読み込ませる
rackup 時の流れ
  1. shebang で sh や他のインタプリタから
  2. rackup コマンドを呼び出し
  3. そこから ruby が呼ばれ
  4. アプリケーションがロードされる

という長い道のりを要するようになってしまう。でもメリットでかいし。

わざわざ別プロセスの rackup から起動しなくてもいんじゃね?と思いたいのは分かる。自分も思った。でも結局 rackup と同じとまではいかないまでも似たような流れで Rack::Builder を呼んであげなきゃいけないので、Rack に頼るうまみが薄れちゃうよね?と思うのでそういうことはしないことにした。

まとめ

何はともあれ、Rack の基本的な使い方は分かった。map とかは分かってないけど、これで

  1. WEBrick で開発
  2. とりあえず CGI で deploy
  3. 負荷的にまずいので FastCGI にしましょうか

といった柔軟な運用が可能になった。

よしよし。

参考

Tags: Web Rack Ruby

*1 というか加齢とともに落ちる一方だぜ! オレは10代の若者じゃねぇ!


2008-12-10 [長年日記]

_ RedHat 系 Linux 用新しめの PHP + MySQL のあるリポジトリ

RepoView: Les RPM de Remi

使ったことはないけど、新しい PHP を使いたいけど野良ビルドも自力バッページングもいや、っていう場合にもしかして使えるのかな?

でも自分で使いたいモジュールがパッケージになってなかったらやっぱ自分でやらないとダメな気がするな。

Tags: PHP

2008-12-12 [長年日記]

_ SimmpleTest のサイトが引っ越してた

しかも古い方は放置だったんで変化に気づかなかった。おいおい。

SimpleTest - Unit Testing for PHP

1.0.1 が出ました。PHP 4.2 から 5.3 までカバーするテストツールですよ、とでかでか書かれているんだけど、左の方にこっそりこのリリースが PHP 4 互換で最後だよと書かれている。つまり、

PHP 4 から 5 の移行に活かせるのは SimpleTest 1.0.1 までだよ

ということ。

※ サンプルを見ると SimpleTest の方にも BDD の影響が出ている気がする。ただ PHP な世界のテストツールでは新しく出てきた PHPSpec なんかも同じなんだけど関数名を Behavior にするアプローチが多い。でもこれ絶対ダメだと思うんだよな。関数名に使える文字には制限が多すぎる。ただでさえ Ruby と違って ? とか使えないんだから。お前ら RSpec の上っ面だけ真似して喜んでるんじゃねえ。

手前味噌な話だけど、自分が預かっている環境で PHP 4 のものはもうない。これは 1年前散々苦労した成果だ。その際、

  • SimpleTest によるユニットテスト
  • お手製のチェッカ(要は片っ端からスクリプトを起こしまくって致命的なエラーを吐き出させるもの)

が大活躍してくれた。

実は SimpleTest の前に PHPUnit をちょっとだけ使っていたこともあったんだけど、その後すぐ SimpleTest に乗り換えた。理由は

  1. PHPUnit は PHP 4, 5 の両対応ではなかった
  2. SimpleTest の方がテスト結果の表示が標準で派手めで、ちょっと楽しそうだったので導入の心理的ハードルを下げてくれた

の二つが大きかった。自分一人だったら 1 だけで十分に移行理由になるんだけど、他の人にも使ってもらおうと思うと 2 の理由も同じくらいに大きなものになってくる。

インストールは今までずっと pearified.com の 1.0 を使っていたんだけど、なんか pearified もこのまま放置プレイな匂いがぷんぷんするので、自前で pear パッケージングしようかなと思い始めている。まぁ、開発環境だけならパッケージングする必要もないっちゃないし、最近流行りのフレームワークは pear 使わないのも多いんだけどね。でも pear パッケージにしないんならせめてリポジトリでちゃんとタグは打っといてほしいわ。svn co は実質 trunk のみとかいやすぎる。

Tags: PHP

2008-12-16 [長年日記]

_ Routing のお勉強

Rack に興味を持ったついでに以前からチェックだけしておいた

Horde/Routes: Elegant URL Handling

を最近読んでいた。この話を twitter と #openpear と #coderepos に振ったら反応があったのは coderepos だけだった。そこで

を知った。話はそのまま RouterCon 方面へ行って完全にネタとして昇華することができた。合唱。

ま、それはともかく、

  • PHP の世界はフレームワークに多少不満があっても我慢して使うし、Router に興味ある人はあんまりいないんじゃ?
  • なんだかんだでみんな routes.py を参考にしている

という話は参考になったし、なかなか興味深かった。いちばん共感を覚えたのは

11:08:15 <tokuhirom__> うわー
11:08:31 <tokuhirom__> せっかくの routes.py のカッコイイDSLが
11:08:35 <tokuhirom__> array() によって台無しだー

いやまったく :-) ホント PHP はキツいっす。array() のヒドさは犯罪級です。便利なのは分かったから、もう一つ踏み込んで array に分解してくれるレイヤーを挟まないと DSL としては機能しないよな。まぁ現実的には時間とかモロモロの兼ね合いで array() むき出しって自分でもよくやるんだけどさ。

あとこの辺

11:40:35 <kazuho> ルーターの定義ってなんすか?
11:41:14 <tokuhirom__> なんだろう
11:41:27 <kazuho> HTTP::Router あたり読めばわかります?
11:41:39 <tokuhirom__> Web Application において、コントローラー and/or
アクションと URL とのマッピングを定義するもの
11:41:41 <tokuhirom__> かな。
11:41:48 <kazuho> 双方向?
11:42:00 <tokuhirom__> URL => Controller/Action
11:42:05 <tokuhirom__> ですね。
11:42:12 <tokuhirom__> 逆方向は optional な要素ではないかと。
11:42:38 <kazuho> あざーす。じゃあ、それも論点ですかね。あまり需要ないのかなー

実際 View を書くときには誰かが逆方向の部分を担当しないと意図した Routing にマッチしない URL を簡単に生成できて、アプリケーションとして管理しきれなくなってしまう。Rails 的には Helper がこの部分を担当することにしてあるのかな? 少なくとも Rails 系の CakePHP では View の Helper で対応する方針になってるみたい。フルスタックのフレームワークではまぁどこで処理しても問題ないやね。

話は戻って Horde/Routes ともう一つ似たライブラリである Net_URL_Mapper では、Horde/Routes だけが逆方向の URL の組み立て機能も持っており、単体の routing ライブラリとして扱いやすいのではないかと思う。速度差とかは計ってないのでこれだけで一概に Horde/Routes の方が優秀とは言い切れないけど、単体の router, mapper は個人的には逆方向の変換も持っていてくれる方が安心感がある。

他に Zend Framework も見てみたけど、この routing は Rails 風のようで微妙に違う。また Zend Framework は疎結合を意識しており、Router と Dispatcher を別々に利用できそうに見えるんだけど、実際処理を追いかけて行くと PHP 5 から入ったタイプヒンティングが邪魔しており、duck type 的に別なライブラリを突っ込むのはちょっと難しそうな感じ。変なところで Java 的な考え方に振っちゃうのはちょっと勘弁してほしい。PHP って開発者の目指す方向とユーザーの目指す方向が合ってないんじゃないか。

Tags: Web

2008-12-18 [長年日記]

_ Capistrano は思ったよりシンプルで思ったよりすごい

システム管理者のみなさん、こんにちは。今日は Rails アプリの deploy ツールとして有名な(らしい)Capistrano についてです。紹介? いえいえ。紹介はすでに有名な人たちによってなされています。ワタシがしたいのは検討。こいつはどこにどのように使えそうか。

Capistrano: Home

システム管理の話なのになんで Puppet じゃないの?と思うかもしれません。それは、以前 Puppet の OSX 対応があまりよくなかったことと、また自分の環境が PPC Mac だったため、仮想マシンを使って他の OS を動かすのも現実的でなく、面倒になってしまっていたからです。

で、巡り巡って Capistrano って実は deploy 以外にも結構使えそうじゃない?と思えましたよというお話。想定しているバージョンは Capistrano 2.5.3 です。

なお、例によって嘘書いてる可能性があります。識者のツッコミお待ちしております。

Capistrano の動作に必要なのは Ruby と ssh だけ

  • Puppet は daemon をいくつか用意してやる必要があります。
    • https を使ってくれるので fw friendly ですが、それにしたっていろいろ準備が必要です。
  • Capistrano の準備で大事なのは以下の2点だけ。
    • Capistrano*1 をセットアップする1台のマシン
    • 管理される方のマシンには sshd と作業用の ssh アカウント

しかも Puppet と違い C/S 方式ではないため、Capistrano のセットアップが必要なマシンは1台だけです。

なんか、ちょっとお手軽な感じがしませんか。

Capistrano 自体は Windows でも動く

依存している gem パッケージは以下の通り。

$ gem dependency capistrano
Gem capistrano-2.5.3
  net-ssh (>= 2.0.0, runtime)
  net-sftp (>= 2.0.0, runtime)
  net-scp (>= 1.0.0, runtime)
  net-ssh-gateway (>= 1.0.0, runtime)
  highline (>= 0, runtime)
  echoe (>= 0, runtime)

あと echoe がこんな感じ。*2

$ gem dependency echoe
Gem echoe-3.0.2
  rake (>= 0, runtime)
  rubyforge (>= 1.0.0, runtime)
  highline (>= 0, runtime)

ext なライブラリはないので Capistrano 自体は Windows でも動かせます。*3自分で実際に確認はしてないですけど、動かしている例は探せばすぐ見つかります。

ただ管理対象は ssh でなんでもできる Un*x 系システムじゃないと Capistrano の利用に旨味はないでしょう。

要するにできることは ssh HOST COMMAND

ssh は interactive な login shell を提供するだけではなく、

ssh HOST COMMAND

の形で

HOST に接続して COMMAND に書かれた処理を実行して終了する

という使い方もできます。大雑把に言うと Capistrano はこの機能をより便利に使うために

  • 接続先の管理と接続の自動化
  • 接続先と作業内容のセットをタスクで管理
  • タスクの実行

を提供してくれるものです。そして

そのために Rake によく似た DSL を使う

ようになっています。

deploy ツールとして紹介されることの多い Capistrano ですが、それは deploy 用のタスクがあらかじめ定義されていて、変数をセットするだけで便利に使えるようにしてあるからであって、

タスクを用意すれば ssh でできる仕事はなんでもできますし、remote の仕事だけでなく local の仕事もできます。

たぶん。

ただし、Puppet と違い、接続先のサーバ OS の抽象化などは行ってくれないため、基本的にその部分は生で相手しないといけません。逆に Puppet のように独自文法の習得を要求しません。

ということは従来の方法からの移行コストが小さい

まともな管理者なら、サーバ上のたいていの作業は sh や Perl、Ruby などのスクリプトである程度自動化されていると思います。これが cron ジョブとして管理できるなら手作業はずいぶん減っているはずです。ただ、

完全に定期実行になっていない作業

も中にはあるでしょう。その場合、しぶしぶ個々のサーバにログインしてスクリプトを実行していませんか? 恥ずかしながら自分はそうです。しかし Capistrano を導入すれば

最後の「ログイン -> 実行」の部分を自動化、管理できます。

そうです。個々のサーバの管理方法を大幅に変える必要はありません。「ログイン -> 実行」という、最後に残った手作業部分を Capistrano のタスクとして管理できるようになるだけなのです。しかもサーバ側は基本的にいじる必要がありません。

これが複数のサーバ上の作業なら目に見えて楽になりますし、サーバの数がそれほど多くなくても

一定のワークフローを保証しやすくなります。

つまり

管理作業の共有、引き継ぎを楽にしてくれます。

もちろん独自タスクの管理のためには Ruby と Rake への理解を要求するので、楽になる一方ではありません。しかも Rake も Capistrano もそんなにドキュメントが充実しているわけでもありません。しかし、スクリプトを書いて自動化して、手作業によるミスを減らせるようにしてあるはずなのに、なんとなく属人的な作業が残っちゃう部分があって、なんか気持ち悪いなぁと感じているような場合には Capistrano の導入は十分に検討の価値があるんじゃないでしょうか。

超簡単な例

えーと概念の話からインストールの方法をすっ飛ばしていきなり実例に入ります。*4

カレントディレクトリに以下のような内容で Capfile という名前のファイルを作ってください。ここでログイン先のサーバのユーザー名、パスワードは一致しているものとします。

role :server, 'server1, 'server2'

set :user, 'user'
set :password, 'password'

desc "reporting disk usage with 'df -h'"
task :check_disk, :roles => :server do
  run 'df -h'
end

cap check_disk

と実行すると

 * executing `check_disk'
 * executing "df -h"
   servers: ["server1", "server2"]
   [server1] executing command
   [server2] executing command
** [out :: server1] Filesystem            Size  Used Avail Use% Mounted on
** [out :: server1] /dev/xvda1             32G   24G  7.2G  77% /
** [out :: server1] tmpfs                 201M     0  201M   0% /dev/shm
** [out :: server2] Filesystem            Size  Used Avail Use% Mounted on
** [out :: server2] /dev/mapper/XXX        17G   14G  2.1G  88% /
** [out :: server2] /dev/xvda1             99M   27M   67M  29% /boot
** [out :: server2] tmpfs                 181M     0  181M   0% /dev/shm
   command finished

みたいな結果が得られます。つまり、server1, server2 にログインして実際に df -h した結果をそのまま、Capistrano をインストールしたマシン上で確認できたということです。

どうです、なかなか面白くないですか。

情報収集が楽になるはず

これはちょっといきすぎてるかな?と思わなくもないのですが、cron ジョブの実行を Capistrano マシンに集中させることも可能ですね。こうすると

  • 個々のサーバで crond が不要
  • cron ジョブのレポートメールを Capistrano マシン上に集約可能
  • cron ジョブの様子を一カ所で確認できるので、スケジュールを考えやすくなる

というメリットがあります。もちろんデメリットや不安材料もあって、

  • ssh login のコスト
  • 長い時間掛かる仕事をさせたときの ssh connection はどうなる?

なんかが気になるところです。まだ見落としてることもあるかもしれません。

cron のレポートの受け取りが面倒くさい

ただ、cron はレポートのメールの受け取りが面倒だなと以前から感じていたので、この問題を解決しやすくなるとしたらとても嬉しいです。というのも、cron のレポートは基本的にローカルのアカウントにメールされるのですが、

どうやって読むの?

という問題がつきまといます。今思いつくのは

  1. 個々のサーバにログインして読む
  2. 個々のサーバに POP サーバ立てる
  3. forward する
  4. mail -> html -> feed 変換して feed reader で読む

くらいですかね。数が増えてくれば当然 1, 2 はナシだと思うんですが、

forward する場合、forward 先のサーバには SMTP サーバと SMTP の通る穴が必要

になります。これがまた悩ましい。メールサーバが LAN 内にあるなら LAN 内のレポートは文句なし LAN 内のメールサーバで処理すればいいです。でもインターネット向けに公開しているサーバのメールはどうしましょう? LAN 内のサーバ向けに forward ? fw で閉じてませんか? じゃあインターネット上の SMTP サーバに転送? OBP25 とかに引っかかりませんか? それにインターネット上に置いた SMTP サーバはあっという間に spam やエラーメールの嵐に飲み込まれてしまいます。すると今度は spam filter だ anti virus だ…。ふー。違うんだよ、オレはそんなことしたいんじゃないの。要するに

forward も意外に面倒なのです。

もう一つ、メールったってこんなの返信の必要もないし、みんな feed reader で読みたいなぁと以前から思っていました。実際、mail -> html -> feed 変換をしている人は見かけます。HTML にして URI が付けば内容の共有も楽です。*5しかしどうしても転送の部分がネックになります。各サーバで feed 吐いてそれpla で収集してもいいですけど、なんかそれもどうなんだと。

でも Capistrano からタスクを実行すれば、自動的に、少なくとも stdout や stderr の内容は取得できます。結果をファイルに落とすタイプでもファイルの転送が可能*6なのでそれを取得してくればオッケーです。

てことは、レポートの収集の部分は自動的にクリアしてしまいます。おう、なんかこれ面白そうじゃん。

できそうなことの一覧

クラスの一覧から一部を抜粋。

Capistrano::Configuration
Capistrano::Configuration::Actions
Capistrano::Configuration::Actions::FileTransfer
Capistrano::Configuration::Actions::Inspect
Capistrano::Configuration::Actions::Invocation
Capistrano::Configuration::Actions::Invocation::ClassMethods
Capistrano::Configuration::Callbacks
Capistrano::Configuration::Connections
Capistrano::Configuration::Execution
Capistrano::Configuration::Loading
Capistrano::Configuration::Loading::ClassMethods
Capistrano::Configuration::Namespaces
Capistrano::Configuration::Namespaces::Namespace
Capistrano::Configuration::Roles
Capistrano::Configuration::Servers
Capistrano::Configuration::Variables
Capistrano::Deploy
Capistrano::Deploy::Dependencies
Capistrano::Deploy::LocalDependency
Capistrano::Deploy::RemoteDependency
Capistrano::Deploy::SCM
Capistrano::Deploy::SCM::Accurev
Capistrano::Deploy::SCM::Accurev::InternalRevision
Capistrano::Deploy::SCM::Base
Capistrano::Deploy::SCM::Base::LocalProxy
Capistrano::Deploy::SCM::Bzr
Capistrano::Deploy::SCM::Cvs
Capistrano::Deploy::SCM::Darcs
Capistrano::Deploy::SCM::Git
Capistrano::Deploy::SCM::Mercurial
Capistrano::Deploy::SCM::None
Capistrano::Deploy::SCM::Perforce
Capistrano::Deploy::SCM::Subversion
Capistrano::Deploy::Strategy
Capistrano::Deploy::Strategy::Base
Capistrano::Deploy::Strategy::Checkout
Capistrano::Deploy::Strategy::Copy
Capistrano::Deploy::Strategy::Export
Capistrano::Deploy::Strategy::Remote
Capistrano::Deploy::Strategy::RemoteCache

いやーこうして見ると Deploy の充実っぷりがやはりすごいですね。Mercurial や Bzr なんて文字も見えるので、これらを使った deploy もできるんでしょう。

使えそうなリソース

Capistrano 自体が github でホスティングされているためか、github で検索するといちばんたくさん引っかかるようです。

あとはシステム管理系のレシピが載ってそうなところとか。まだあんまちゃんと見てないですけど。

最後に。

みんな、実際に試してオラにやり方を教えてくれ!

*1 当然、Ruby も必要

*2 これ recursive に取れないのかな? もしかして使ってる gem が古い?

*3 公開鍵認証を利用するための鍵作成には Putty などの ssh クライアントがたぶん必要です。

*4 実際、rubygems さえ動いていればインストールはそれくらい簡単です。

*5 例えば管理用のメールを複数人で受信していて、何か気になるアラートがあったとします。この情報を共有したいわけですが、メールを一意に特定するのは Message-ID です。しかし Message-ID の確認や検索の容易なメーラーは寡聞にして知りません。

*6 やり方はまだ分かってません!

本日のツッコミ(全1件) [ツッコミを入れる]

_ Johann [大変分かりやすいです。参考にさせていただきます。]


2008-12-24 [長年日記]

_ OOo 3 が要求するバージョン

  • MacOSX 10.4 以降
    • 2.x の時代は NeoOffice を使う必要あり
  • Windows 2000 以降
    • 2.x の時代は 98 以降でも動く

2008-12-27 [長年日記]

_ colordiff で色とりどりな世界へ

実は今まで diff はすべてモノクロで見ていた。Emacs の vc-diff 以外では。どうにかならんかなぁと思ってはいたがこんなもんかなとも思って長いこと過ごしていた。

しかし

coderepos の commit 数ナンバー1の人が github を練習したメモ - TokuLog 改めB日記

を読んで git では設定次第で color 出力できることが分かった。だったら他のツールでもそういう方法は必ずあるはずだと思ってググったらいきなり

ColorDiff

が出てきた。そこら中でパッケージとして利用できるみたいなので使い始めるのは十分簡単。中身は Perl スクリプトで、diff の出力を解析してエスケープシーケンスで色づけしてくれる。

使い方は

diff [options] FILE1 FILE2 | colordiff

みたいなのでもいいし、

colordiff [options] FILE1 FILE2

でもいい。基本的に diff の代わりとして使えるように配慮されているようだ。手っ取り早くは

svn diff | colordiff

みたいにしても使える。調子に乗って

.subversion/config

 diff-cmd = colordiff

にしてみたけど、これは失敗。というのも Emacs の vc-diff がエスケープシーケンスをそのまま吐き出して意味不明になってしまうから。ということは

svn diff | colordiff
hg diff | colordiff
git diff

で color 出力できるって感じか。

また、普段ページャとして less を使っているんだけど、less は標準ではエスケープシーケンスによる色づけには対応していない。man を読むと

-R or --RAW-CONTROL-CHARS
       Like -r, but only ANSI "color" escape sequences  are  output  in
       "raw" form.  Unlike -r, the screen appearance is maintained cor-
       rectly  in  most  cases.   ANSI  "color"  escape  sequences  are
       sequences of the form:

ということは、-r か -R でエスケープシーケンスをそのまま terminal に解釈させられそう。

最終的には .zshrc で

export LESS=R
alias diff='colordiff -u'

しといた。簡単な色づけだけど、分かりやすさが段違いだ。こりゃ便利。

あと直接関係ないけど

alias ls='ls --color=auto'

alias ls='ls --color'

にした。(BSD では export CLICOLOR_FORCE=1 )これで ls の出力を less に渡しても色付きで見ることができる。

_ ぐりもんアップデート時は load 順に注意

まぁ別に LDRize に限ったことじゃないんだろうけど。JavaScript には require の仕組みがないことが原因、ということなのかな?

とにかく

  1. minibuffer
  2. AutoPagerize
  3. LDRize

の順番を守ること。どれかアップデートすると新しくなったものが最後に読み込まれるようになってしまうので、その都度修正が必要。

ちょっとメンドイけどしょうがないのかな。

Tags: Firefox

2008-12-28 [長年日記]

_ Webby で静的サイト作成

Rake の練習課題として以前調べておいた

Welcome! - Rote

を試そうかと思っていたんだけど、ちょっと調べたら

Webby :: Webby

を見つけた。こっちの方がいいかもしんない。

rote は

  • Textile などで書かれたコンテンツファイル
  • メタデータを記述する rb ファイル

が分離していたんだけど、webby ではこれが一つのファイルにまとまっている。フォーマットはこんな感じ。

---
RFC822 風のメタデータ
---
Textile などで書かれたコンテンツ

コンテンツの処理は filter で指定する。filter には Textile などの書式の他に erb も指定できるので、コンテンツの中に Ruby で扱える変数や処理を自由に埋め込むことができる。これは便利だ。

使い方

Rails のように webby-gen コマンドでサイトのひな形を作る。このとき指定できるのは 0.9.3 で

  • blog
  • presenation
  • tumblog
  • website

の4種類。それぞれ適した task が利用できるようになる。

blog

試しに blog を作ると task に

blog:post

ができて、これを呼ぶと呼んだ当日の年月のディレクトリを

YYYY/MM

で掘る。blog:post 時に path の入力を求められるが、これを入れて

YYYY/MM/path

なエントリになる。Type Pad 風というか海外の blog ツールで最もポピュラーな URL かな。

website

これが website になると

create:page

になる。

deploy

Capistrano ではないけれど rsync などを使った deploy も行える。ただし ftp では行えない。うーん、もったいない。ftp に対応すれば使えるシーンはたくさんありそうなのに。ま、output したものを sitecopy などで mirroring すればいいっちゃいいんだけど。

autobuild

面白い機能として最後に autobuild を紹介しておく。このタスクを実行するとファイルの更新を監視して自動的に HTML の出力を行ってくれ、なおかつ WEBrick で serve してくれる。

正直、このツールに興味を持つ人は自分で Web サーバくらい立てられる気もするが、そうでない人でもこの autobuild を利用すればサイトの検証を行いやすい。

Tags: Web Rake Ruby

2008-12-30 [長年日記]

_ ri ってメソッドも引けたのか

$ ri String
---------------------------------------------------------- Class: String
     A +String+ object holds and manipulates an arbitrary sequence of
     bytes, typically representing characters. String objects may be
     created using +String::new+ or as literals.

 ...

しか見たことなかったけど、

$ ri String.sub
------------------------------------------------------------- String#sub
     str.sub(pattern, replacement)         => new_str
     str.sub(pattern) {|match| block }     => new_str
------------------------------------------------------------------------

...

こんな感じ。

最近は irb でメソッド検索することが多かったんだけど、これだけでは引数については何も分からない。ri で引くとちゃんとその辺の説明も分かるので便利。

なるほど。

あとはもう少し早く表示してくれると嬉しいんだけど、それは贅沢だろうか。

Tags: Ruby

_ 環境の判別って真面目にやると面倒くさい

年の瀬の大掃除ということで(書いたのは違うけど実際に実行していたのは年末だったのです)、.emacs, .zshrc などを整理している。

  1. .emacs, .zshrc で環境を判別し、FreeBSD, Linux, MacOSX で一つの設定ファイルを利用する
  2. この一つのファイルをバージョン管理する

ということを試みている。

またドットファイルをバージョン管理すると言ってもホームディレクトリを丸ごとバージョン管理するわけにはいかない(現実的じゃない)ので、dot-files/ ディレクトリを掘ってここにドットファイルを置き、rake を使ってホームディレクトリ以下にリンクを張るようにしてみた。

要は rake task 作りの練習である。実はあんだけ Capistrano を持ち上げておいて現段階で Rakefile をほとんど書けないのであった。

Tags: Zsh Emacs

2008-12-31 [長年日記]

_ 2008年振り返り

を、小正月に書くライフハック。

2008年の頭はその前の年からの PHP 4 -> 5, PostgreSQL 7 -> 8 の移行、サーバリプレイス作業に掛かりっきりでそれ以外よく分からなくなってた。

その成果がこの辺り。

ロクな監視ができていなかった古いシステムをどうにか移行させるためにあれこれ工夫してた時期。今も本当の意味で現代的な管理はできていないんだろうけど、以前よりはだいぶマシなのも間違いない。今後は最近ハマってる Rake ( Capistrano ) でより確実な作業に昇華できるようにしていく予定。

次、監視繋がりというかもっと feed を便利に、ということで Yapra を使い始めた。勢い余って Yahoo! Pipes も始めてみた。また Yapra 繋がりで、長いつきあいの Ruby をようやく Web 方面に活用し始めた。

feed を利用するだけでなく、リソースから API を用意したり feed を吐いたりもしてた。要は今になって HTTP Header を勉強し直したということですな。

また rest-client や Yapra の影響もあって git と github を始めた。

でも正直、git のメモが多いのは git がよく分からない証拠。実際、git を始める前から Mercurial を使っているが、Mercurial のメモは実は一切ない。これは少なくとも Subversion からの移行については、あくまで個人的な感想だけど圧倒的に Mercurial の方が楽だから。

他、

辺りが細かく熱かったところ。また RackRoutingjQuery なども含めて

2008年は全体的に Web と Ruby で充実してた年だった(なのに Rails はノータッチ)

と言っていいんじゃないかと思う。あとまぁ Mac も OSX 10.5 に移行して、以前よりいろんなことが快適になったかな。

2009年も基本的にはこの路線で頑張る感じで考えている。とりあえずは Rake と監視能力の強化かな。これまではあまりネットウォッチャーのスキルに興味なかったんだけど、そっち方面を強くしていきたいかなと思っている。

あとは紙を含めて雑多な情報の扱いをもう少しうまく回していきたい。あんまり有名すぎて言うのもはばかられるけど GTD とかライフハックとかももう少し意識した方がいいのかも。

身体も労りつつ鍛えなおさにゃいかんかなーなんてことも気になっている。もういいおっさんだしな。最初から無理の利く身体ではないが、無理せず、いい落としどころを見つける目も養っていかなきゃな。ガムシャラなだけではもうダメなんだ。そこはもう諦めついたよ、さすがに。

Tags: 日々