2009-02-01

Quillのインターフェース

ありがちですが、社会人1年目半ばくらいで、情報の管理について結構悩んだ記憶があります。自分で学んだこと、先輩から学んだこと、タスク、スクラップ、などなど、学生時代とは比べようもないほどの情報に迫られ、これをうまく管理する方法はないものかと。

で、確か最初にその方法として選んだのがExcelだった気がします。絵は描けるし、いろんなファイルを埋め込めるし、その気になればマクロでいい具合のインターフェースも作れて、今考えても普通に実用的だったなーと思います。ただ、やっぱりネックだったのが動きの重さ。ちょっと見たいだけなのにいぃ。ってときでも変わらないウエイトは懐かしい思い出です。あの動作の遅さ(PCの性能もあるんでしょう)が嫌で結局エクセルからは離れました。

次に使ったのは、今でも忘れない。HTML。直接タグ打ってました。Excelの経験を活かし「そこそこの表現力+軽量さ」を求めた先です。適度にファイルを分割することで、軽量さも保て、しばらく使っていました。やめた原因?保守に疲れたからです。:-)

そこからは、覚えている範囲ですが

  • Remember the mill -> もっと機能が欲しい
  • Basecamp -> 客先ネットワーク断絶の件
  • wiki(pukiwiki, tiddly wiki) -> 記法うぜえ、移行面倒、
  • changelog -> 変なルール設けて壊滅状態
  • テキスト <- 今ココ

てな感じで遷移してきました。ただ、今後はネットワークに繋がらないってのは無さそうな状況になったので、どこからでも見れた方が嬉しいよね!ってことで、タイトルにあげたQuillを使っています。テキストの全文検索をインクリメンタルでやってくれたり、ショートカットキーが他のメモ系サービスよりも充実しているのがナイスな感じです。

でも、不満な点もあって、文章の編集がやりづらいです。これはブラウザ上で使うウェブサービス全般に言えることで、入力インタフェースがブラウザに引っ張られるためにしょうがない部分もあるとは思うのですが、例えば、タブを使ったインデントが出来なかったり(フォーカスが移動する。コピペならいけるけど、それはそれで面倒。)、普段使っているOperaだと、一部のショートカットキーが正しく動作しなかったりです。(とは言え、この手のサービスでのOperaのサポート状況から見ると、Quillは優秀。ユーザー少ないもんね。。。)

じゃあどうすれば編集とか入力作業が快適になるかと考えてみたのですが、そもそもエンジニアは、エディタが一番書きやすいよな、ってところに戻り、Vimから入力できたら最高だなーと思いました。でもvimスクリプトとか全然わからんなーというのがあるので、ひとまずPerlからQuillを扱えるようにするためのモジュールを書いてみました。

ソース(github)

というわけで、これを使って何かナイスな情報管理につながる方法が見出せればなあ、と思います。何かやればフィードバックもここにアップしていきたいと思います。

※Perlでモジュールを書く場合の流儀(?)みたいのは、よくわかっていません。なので、podドキュメントとかtディレクトリとかその他もろもろ無くて、本当にモジュールと簡単な使用例のサンプルスクリプトだけしかありません。機会を見つけてアップすると思うので、ひとまずその辺はご勘弁ください。

Posted at 01:38 in | WriteBacks (0) | Edit

2008-12-31

XPath覚え書き

以下の内容くらいで以外に事足ります。

シンタックス 意味
// 先祖と子孫エレメントの連結
/ 親子を連結
* 全要素
tag[@xxx] tagの属性xxx
tag[n] n個目のtag(要素)
text() テキストの内容
.. 1階層上の要素

式の確認には、CPANモジュールのXML::XPathに付属のxpathを使います。xpathはコマンドとして使えるPerlスクリプトで、

xpath 入力ファイル xpath式
の形式で使用します。以下のHTMLで試してみます。

<body>
  <div class="hoge">
    abababa.
  </div>
  <div class="hoge">
    <p>
      <strong>
        <a href="http://www.google.com/">
          google
        </a>
      </strong>
    </p>
  </div>
</body>

入力: xpath foo.html '//a' 意味: リンクタグを選択
選択結果

<a href="http://www.google.com/">
            google

          </a>

入力: xpath foo.html '//div//strong'
意味: divの子孫のstrongを選択
選択結果

<strong>
          <a href="http://www.google.com/">
            google
          </a>

        </strong>

入力: xpath foo.html '//a/@href'
意味: aタグのhref属性を選択
選択結果

href="http://www.google.com/"

入力: xpath foo.html '//div[2]'
意味: 2つ目のdivを選択
選択結果

<div class="hoge">
      <p>
        <strong>
          <a href="http://www.google.com/">
            google
          </a>
        </strong>
      </p>
    </div>

入力: xpath foo.html '//div[@class="hoge"][2]/p/strong'
意味: classがhogeの2番目のdivを選択し、さらにその子要素のpの子要素のstrongを選択
選択結果

<strong>
          <a href="http://www.google.com/">
            google
          </a>
        </strong>

入力: xpath foo.html '//div//a/text()'
意味: divの子孫aのテキストを選択 選択結果

google

入力: xpath foo.html '//div//a/..'
意味: divの子孫aの1階層上の要素を選択 選択結果

<strong>
          <a href="http://www.google.com/">
            google
          </a>
        </strong>

さらに色んな選択方法とかは ここを参照してください。また、XPathの考え方はこちらが参考になりました。

Posted at 23:52 in | WriteBacks (2) | Edit

2008-12-14

Sledgeのソースを追った記録。

社内の共通言語がPerlなところに最近入社したわけですが、殆どのサービスがSledgeで作られているので、理解する必要がありありなんですけど、Perlが危ういレベルなので解説記事を見てもちゃんと理解できませんでした。で、最近「続・初めてのPerl」を読み出してようやくOOPerlをやる上で必要な情報に触れ出したので、ちゃんとソースコードを追いかけてみました。頭に残るようソースを追った記録を残しておきたいと思います。

あ、自分のPerl歴とかを書いておくと、

  • 約1年前くらいに、man perlから情報を辿りながらちょっとしたテキストツールは作ったことがある。(dbのselectした結果をファイルに吐いて、正規表現で良い具合に整形して、何かとデータを付き合わせるみたいの)
  • ちゃんとした勉強を開始したのは12/1から。
  • 最初に初めてのPerlを読んだ。ただし、ファイル操作とかプロセス管理は斜め読み。
  • 今は続・初めてのPerl 改訂版を読んでる。リファレンスが出てきたところ。
  • オブジェクト指向はそこそこ理解しているつもり。

ぐらいです。なので、見る人もそういう覚悟でお願いします。題材ですが「Sledgeのインストールと設定方法」で追ってみます。

まず、スタートはhtdocs/index.cgiへのリクエストから。以下のスクリプトが実行されます。

#!/usr/local/bin/perl

use strict;
use Hello::Pages::Root;
Hello::Pages::Root->new->dispatch('index');

Hello::Pages::Root->new->dispatch('index'); ここが肝で、意味はHello::Pages::Rootってモジュールのnewメソッドを呼び出してインスタンス生成して、dispatchメソッドを呼び出す。 OOらしいコードです。そういうわけで次はHello::Pages::Rootモジュールを見ます。ファイルは、lib/Pages/Root.pm。

package Hello::Pages::Root;
 
use strict;
use base qw(Hello::Pages);
 
__PACKAGE__->tmpl_dirname('.');
 
sub dispatch_index {
    my $self = shift;
    $self->tmpl->param(string => 'Hello World!');
}
 
1;
で、ここにはnewメソッドはないわけだけど、「use base モジュール名」な構文でクラスの継承が可能。なので、次はHello::Pages(lib/Hello/Pages.pm)を見る。
package Hello::Pages;
use strict;
use Sledge::Pages::Compat;

use Sledge::Authorizer::Null;
use Sledge::Charset::Default;
use Sledge::SessionManager::Cookie;
use Sledge::Session::MySQL;
use Sledge::Template::TT;

use Hello::Config;

# メソッドの定義が以下に続く

ここにもnewメソッドは定義されてません。ソレハオカシイダロってことでちょっと調べてみたら、実はuse base以外にも継承方法があって、それぞれのクラスが持つISAって名前の配列に親クラス名をぶっこむ方法。で、Perlでは「use ModuleName」をしたときに、ロードされた側のモジュールにimportってメソッドがあった場合にuseのタイミングで実行されます。なので、useしているモジュールのどこかにimportメソッドがあって、さらにISAに親クラスを入れるコードがあろうものなら、継承になるようです。。。

まあ、名前的にSledge::Pages::Compatが一番怪しいので中を見たところ、
# perldoc -l Sledge
# で、Sledge.pmの場所がわかるので、その辺を探す。

package Sledge::Pages::Compat;
# $Id: Compat.pm,v 1.1.1.1 2003/02/13 06:59:36 miyagawa Exp $
#
# Tatsuhiko Miyagawa 
# Livin' On The EDGE, Co., Ltd..
#

use strict;
use constant MOD_PERL => defined $ENV{MOD_PERL};

sub import {
    my $base = MOD_PERL ? 'Sledge::Pages::Apache' : 'Sledge::Pages::CGI';
    eval qq{require $base};
    {
		my $pkg = caller;
		no strict 'refs';
		unshift @{"$pkg\::ISA"}, $base;
    }
}

1;

importメソッドの中にある「unshift @{"$pkg\::ISA"}, $base;」ってのがそれ。
# この継承方法嫌だなー
やってることはコードのとおり、実行環境がmod_perlかcgiかによって継承元を振り分け。callerは、このモジュールをuseしたモジュール名を返してくれるようです。僕はmod_perlで動かしていたので、Sledge::Pages::Apacheから継承。

package Sledge::Pages::Apache;

use strict;
use base qw(Sledge::Pages::Base);

use Apache;
use Apache::Request;

sub create_request {
    my($self, $r) = @_;
    my $req = Apache::Request->new($r || Apache->request);
    $req->param;		# do parse here
    return $req;
}
ここにもnewはないけど、Sledge::Pages::Baseを継承している。で、それを見ると・・・
sub new {
    my $class = shift;
    my $self = bless {}, $class;
    $self->init(@_);
    $self->invoke_hook('AFTER_INIT');
    return $self;
}

あった。これがHello::Pages::Root->newで使われているメソッド。ひとまず主要な流れがわかれば満足なので、invoke_hookはここでは無視。自分的なメインラインはinit。initを見る。newの真下にあります。

sub init {
    my($self, $r) = @_;
    $self->r($self->create_request($r));
    $self->authorizer($self->create_authorizer);
    $self->manager($self->create_manager);
    $self->charset($self->create_charset);
}

rメソッドです。rはなんだ?と調べてみると、Sledge::Pages::Baseの頭付近にこんなコードが。

__PACKAGE__->mk_accessors(
    'r',           # Apache::Request or Sledge::Request::CGI
    'session',     # Sledge::Session
    'manager',     # Sledge::SessionManager
    'authorizer',  # Sledge::Authorizer
    'charset',     # Sledge::Charset
    'tmpl',        # Sledge::Template
    'fillin_form', # Sledge::FillInForm
    'finished',    # flag whether request is finished
    'page',        # page name (arg to dispatch())
    'filters',     # filter subs
);

__PACKAGE__は自分自身。つまりSledge::Pages::Baseを意味し、mk_accessorsは親クラスにあたるClass::Accessorのメソッド。このメソッドを使うと。引数でわたした名前のアクセサメソッドをつくってくれるようです。例えばrの例だと、

r(値);
で、値をrに設定。
r();
で読み出し。

initメソッドに戻って、rの部分を見ると「$self->r($self->create_request($r))」。 create_requestはさっき見たSledge::Pages::Apacheの中で定義してありましたねー。でまあ、やっていることはリクエストを抽象化したオブジェクトのセットです。$r経由でリクエストパラメータにアクセスとかできる感じですね。以降のコードも同様に、認証(authorizer)、セッション管理(manager)、文字コード(charset)の抽象オブジェクトをセット。最後にこのインスタンスを返しているので、「Hello::Pages::Root->new」で返ってくるインスタンスで色んな値を参照できるし、色んなメソッドが実行できるよ!ってところですね。

newの次はdispatch。これもSledge::Pages::Baseに定義されていました。

sub dispatch {
    my($self, $page) = @_;
    return if $self->finished; # already redirected?

    local *Sledge::Registrar::context = sub { $self };
    Sledge::Exception->do_trace(1) if $self->debug_level;
    eval {
        $self->init_dispatch($page);
        $self->invoke_hook('BEFORE_DISPATCH') unless $self->finished;
        if ($self->is_post_request && ! $self->finished) {
            my $postmeth = 'post_dispatch_' . $page;
            $self->$postmeth() if $self->can($postmeth);
        }
        unless ($self->finished) {
            my $method = 'dispatch_' . $page;
            $self->$method();
            $self->invoke_hook('AFTER_DISPATCH');
        }
        $self->output_content unless $self->finished;
    };
    $self->handle_exception($@) if $@;
    $self->_destroy_me;
}

postとgetでルートを分けているみたいですね。今回通るルートだと、init_dispatchを実行した後dispatch_indexを実行なので、init_dispatchから確認したいと思います。

sub init_dispatch {
    my($self, $page) = @_;
    $self->page($page);
    $self->construct_session unless defined $self->session;
    $self->authorizer->authorize($self);
    $self->charset->convert_param($self);
    $self->load_template($page);
    $self->load_fillin_form if $self->is_post_request;
}

と言っても名前の示すとおりです。$self->page($page)は、「Hello::Pages::Root->new->dispatch('index');」で渡した"index"という名前を保存。construct_sessionはセッション情報が無い場合にセッションIDとかを生成、authorizerは認証機構に使うクラスのようです。charsetはリクエストパラメータの文字コード変換。どの文字コードを使用するかは、Hello::Pages(lib/Hello/Pages.pm)に定義されている以下のメソッドで決まります。

sub create_charset {
    my $self = shift;
    return Sledge::Charset::Default->new($self);
}

Sledge::Charset::Defaultはeuc-jpですが、Sledgeのインストールしてあるディレクトリ配下のCharsetという名前のディレクトリ以下を見ると、Shift_JIS.pm、UTF8.pmとかも用意してあるので、このメソッドの最後の行を、return Sledge::Charset::UTF8->new($self);としてあげればUTF-8に変換してくれるようです。

init_dispatchに戻って続きを見ると、load_template($page)です。ここでview/index.htmlをテンプレートとして使うために$selfに読み込ませてます。最後の$self->load_fillin_formですが、postの場合なので流します。

次がいよいよ最後のdispatch_indexです。lib/Hello/Pages/Root.pm内にあります。今回の例ではこんなのでした。

sub dispatch_index {
    my $self = shift;
    $self->tmpl->param(string => 'Hello World!');
}

view/index.htmlのテンプレートを埋めるだけですね。ここで生成されたHTMLをdispatchメソッドの最後の$self->output_contentによってクライアントに返して終了となります。


と、レスポンスまでのコードを追ってみましたとさ。

Posted at 08:20 in | WriteBacks (0) | Edit

2008-10-23

github

少し前にユーザ登録してそのままだったので使ってみました。どうでもいいですけど、油断するとすぐ「じっとはぶ」って呼んでます。

自分のページ
Posted at 01:50 in | WriteBacks (0) | Edit

2008-09-26

志人

全然CDを買ってなくてTHA BLUE HERBとMSCばっか聞いてたので、気になっていた降神の志人の「Heaven's 恋文」をAmazonで注文。今日届いた。BlueHerb/MSCもそうだけど、何となく何かをやる気が触発される。インストも含めて全体的に好みの音だったので他の作品も買ってみよっかな。

Posted at 20:58 in | WriteBacks (0) | Edit