2004年8月16日月曜日

続・君ならどう書く?(Sledgeを使って作ってみました。)


君ならどう書く?(Sledgeを使って作ってみました。)というエントリーを書いたらmiyagawaさんからコメントが!「ソース!」ということなので、ソースを公開してみます。確かにソース公開しなきゃ何の意味もないですもんね(苦笑)



今回はsledge-setupで生成されるLLW::Pages、LLW::Config、LLW::Config::_common以外に、ロジック部分のLLW::Pages::Index、カレンダーを表すLLW::Calendar、スケジュールを表すLLW::Scheduleという3つのクラスを作りました。テンプレートはindex.html一枚で、トリガ用CGIはスケジュール表示、登録用のindex.cgiと削除用のdelete.cgiを作成しました。入力データのバリデーションは時間の都合上やってません(笑)それぞれのソースは以下の通りです。



LLW::Pages::Index



Pagesクラスを継承した今回の核となるクラスです。



package LLW::Pages::Index;
use strict;
use base qw/LLW::Pages/;
__PACKAGE__->tmpl_dirname('llw');
use LLW::Calendar;
use LLW::Schedule;
use Date::Simple;
# カレンダー、スケジュールの表示
sub dispatch_index {
my $self = shift;
my $year = int $self->r->param('year');
my $month = int $self->r->param('month');
my $day = int $self->r->param('day');
my $calendar = LLW::Calendar->new($year, $month) || LLW::Calendar->new;
my $date = Date::Simple->new($year, $month, $day) || Date::Simple->new;
my $schedules = LLW::Schedule->search(schedule_date=>$date,
{order_by=>'schedule_timestamp'} );
$self->tmpl->param( date=>$date, calendar => $calendar ,schedules => $schedules );
}
# スケジュールの登録
sub post_dispatch_index {
my $self = shift;
my $year = int $self->r->param('year');
my $month = int $self->r->param('month');
my $day = int $self->r->param('day');
my $title = $self->r->param('title');
my $content = $self->r->param('content');
my $date = Date::Simple->new($year,$month,$day);
eval {
my $schedule = LLW::Schedule->create({
schedule_title => $title,
schedule_content => $content,
schedule_date => $date
});
};
$self->tmpl->param(create_error=>1) if $@;
my $uri = sprintf 'index.cgi?year=%d&month=%d&day=%d', $year, $month, $day;
$self->redirect($uri);
}
# スケジュールの削除
sub dispatch_delete {
my $self = shift;
my $schedule = LLW::Schedule->retrieve(int $self->r->param('id'));
$schedule->delete if $schedule;
my $year = int $self->r->param('year');
my $month = int $self->r->param('month');
my $day = int $self->r->param('day');
my $uri = sprintf 'index.cgi?year=%d&month=%d&day=%d', $year, $month, $day;
$self->redirect($uri);
}
1;

LLW::Calendar


カレンダーを表すクラスです。拙作のDate::Simple::Monthというモジュールのサブクラスとして定義しています。Date::Japanese::Holidayをuseすることで、各日にちでis_holidayメソッドを利用可能にしています。



package LLW::Calendar;
use strict;
use base qw/Date::Simple::Month/;
use Date::Japanese::Holiday;
1;

LLW::Schedule


スケジュールを表したクラスです。Class::DBIのサブクラスになっています。今回はスケジュールを保存するためにmysqlを使いましたので、保存用のテーブルのスキーマも書いておきました。



package LLW::Schedule;
use strict;
use base qw/Class::DBI/;
use LLW::Config;
my $config = LLW::Config->instance();
__PACKAGE__->set_db('Main', @{$config->datasource}, {RaiseError=>1});
__PACKAGE__->columns(Primary=>'schedule_id');
__PACKAGE__->columns(Essential=>qw(schedule_date schedule_title schedule_content));
__PACKAGE__->columns(OTHERS=>qw(schedule_timestamp));
__PACKAGE__->has_a(schedule_date=>'Date::Simple');
1;
__END__
CREATE TABLE schedule (
schedule_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
schedule_date DATE NOT NULL,
schedule_title TEXT NOT NULL,
schedule_content TEXT,
schedule_timestamp TIMESTAMP
);

index.html



テンプレートです。実はここに一番時間をかけていたりします(笑)



<html>
<head>
<title>llw scheduler</title>
<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<style type="text/css">
<!--
.weekday { color: #999999; }
.holiday { color: red; }
.today {background-color: yellow;}
.content { color: #999999; }
-->
</style>
</head>
<body>
<div id="calendar">
<table border="1" cellpadding="1" cellspacing="0">
<caption>[% calendar.year %]年[% calendar.month %]月</caption>
<tr>
<th>日</th>
<th>月</th>
<th>火</th>
<th>水</th>
<th>木</th>
<th>金</th>
<th>土</th>
</tr>
[% FOREACH d = calendar.wraparound_dates %]
[% IF loop.count % 7 == 1 %]<tr>[% END %]
<td>
[% IF d.month == calendar.month %]
<a class="[% IF d.is_holiday %]holiday[% ELSIF d == today %]today[% ELSE %]weekday[% END %]"
href="index.cgi?year=[% d.year %]&month=[% d.month %]&day=[% d.day %]">[% d.day %]</a>
[% ELSE %] [% END %]
</td>
[% IF loop.count % 7 == 0 %]</tr>[% END %]
[% END %]
</table>
<a href="index.cgi?year=[% calendar.prev_month.year %]&month=[% calendar.prev_month.month %]&day=[% date.day %]">前月</a>
<a href="index.cgi">今月</a>
<a href="index.cgi?year=[% calendar.next_month.year %]&month=[% calendar.next_month.month %]&day=[% date.day %]">来月</a>
</div>
<h3>[% date.year %]年[% date.month %]月[% date.day %]日の予定</h3>
<form action="index.cgi" method="post" name="" id="register_form">
タイトル<br>
<input name="title" type="text" id="title">
<br>
内容
<br>
<textarea name="content" id="content"></textarea>
<br>
<input type="submit" value="予定を追加">
<input name="year" type="hidden" id="year" value="[% date.year %]">
<input name="month" type="hidden" id="month" value="[% date.month %]">
<input name="day" type="hidden" id="day" value="[% date.day %]">
</form>
[% IF create_error %]データの登録に失敗しました。<br>[% END %]
<div id="schedule">
[% WHILE (s=schedules.next) %]
<div class="title">・[% s.schedule_title|html %] [<a href="delete.cgi?id=[% s.id %]&year=[% date.year %]&month=[% date.month %]&day=[% date.day %]">削除</a>]</div>
<div class="content">[% s.schedule_content|html|html_line_break %]</div>
[% END %]
</div>
</body>
</html>

index.cgi、delete.cgi


トリガ用CGIです



#! /usr/local/bin/perl
use strict;
use LLW::Pages::Index;
LLW::Pages::Index->new->dispatch('index');


#! /usr/local/bin/perl
use strict;
use LLW::Pages::Index;
LLW::Pages::Index->new->dispatch('delete');

MIME::Lite::TT


MIME::Lite::TTMIME::Lite::TT::JapaneseというPerlモジュールを作ってみました。


僕がPerlでメールを送信するプログラムを書くときはMIME::Liteをよく使うんですが、そのとき、メールの本文はテンプレートにしておいて、Template::Toolkitで読み込むなんてことをよくやります。メールを送信するという機能はWebアプリを作る際には必ずといっていいほどついてくるものなので、毎回、同じようなコードを書かなくてはなりません。それがめんどくさくなって少しでも簡単にかけるようにとこのモジュールを書いてみました。MIME::Lite::TT::Japaneseの方は日本語のエンコード部分がちょっとあやしいので、使って頂いてフィードバックもらえたらうれしいです。

2004年8月9日月曜日

君ならどう書く?(Sledgeを使って作ってみました。)

Lightweight Language Weekendの「君ならどう書く」のお題はこんな感じ。

  • カレンダーを表示すること

  • その月だけでなく、前後の月にも移動できること

  • カレンダーの日にちを指定して予定を入力/表示できること

  • あとの拡張は自由


WebアプリケーションといえばSledge。ということでSledgeとCPANモジュールをごりごり使ってさくっと作ってみました。


http://hori-uchi.com/llw


所要時間は1時間30分くらい。やはり典型的なWebアプリの開発にはSledgeは恐ろしく便利だと実感しました。



Lightweight Language Weekendに行ってきました

Lightweight Language Weekendというカンファレンスに参加しました。カンファレンスという堅苦しい雰囲気はなく、どのセッションも笑いが絶えない和やかな雰囲気で進行していき、参加していてとても楽しかったです。
特に、Lightning Talkでの早川さんのLL侍は会場中大爆笑で、僕も大笑いでした。たぶんLLWで一番印象に残ったのは?と聞かれたら、ほとんどののひとがLL侍というんじゃないでしょうか(笑)


まじめなところで行くと、Language Updateでは、今まで知らなかった言語をその動向とともに知ることができたのが非常に参考になったし、LLを仕事にするにはどうしたらいいかということに関して、いろいろな方の意見が聞けたのも非常に有益でした。僕はWebアプリケーションを作るのが主な仕事なので、Python、Zopeというのは一度さわってみたいなーと思いました。また、LLを生かせる場所ということについては、ASP型のサービスのシステム構築というのが一つの答えかなと感じました。はてなダイアリーの例を出すとPerlで書いているからこそ少人数でも次から次へと素早く機能を拡張していけるのだろうと思います。LLを仕事にのセッション中の会場ではLLは仕事にしにくいみたいな空気があったように思うんですが、Webアプリケーションということについて言えば、中小規模の会社のWebサイト構築なんかは高額なJAVAより安価に作れるLLを使った方が喜ばれる(というかお客さんにとっては動けば言語なんて何でもいいんですよね。。)と思うんですよね。儲かる儲からないは別ですが(笑)


こんな感じで、どのセッションもすごくおもしろかったんですが、一つだけ不満が。最後のセッション、その場でどう書くはお題がWebアプリケーションということもあって個人的にすごく楽しみにしていたんですが、参加者のみなさんのほとんどがWebアプリケーションなんて作ったことないよーとか久々に作るって方ばかり(笑)
僕としてはZopeだろうがCPANだろうが飛び道具何でもありで、こんなにも簡単にWebアプリが作れますというのを見たかったです。ただ、飛び入り参加の○○さん(名前は伏せてとおっしゃっていた気がするので○○さんとしました)のPerl標準関数のみを使って70行程度で書いたコードは流石だなと感心しました。



2004年8月5日木曜日

SledgeでのURLエンコード


GETリクエストの引数に日本語を渡したい場合、文字列をURLエンコードしなければなりませんが、Sledgeで、出力ページをEUC以外にしていると、URIエンコードにひと工夫必要になります。

出力ページがEUCの場合は、Template::Toolkitに標準でついているURIフィルターを使って


<a href="search?q=[% r.param('query')| uri %]">
検索結果のリンク</a>

こんな風に簡単にURLエンコードできますが、EUC以外の場合は単純に上のようにしても、文字化けが起こってしまいます。これは、Sldgeは内部で扱う文字コードをEUCに統一していて、外部からの入力データは受け取り時にEUCに変換され、コンテンツのアウトプット時にテンプレートの値が埋め込まれた完全なEUCのHTMLを作成してからそれを指定の文字コードに変換して出力しているためです。

このため、上記のような方法でURLエンコードを行うと、指定の文字コードに変換される前に、つまり、文字コードがEUCの状態でURLエンコードが行われてしまうため、おかしなことになってしまうのです。




この問題を回避するには、URLエンコードを行う前に指定の文字コードへ変換する必要があります。


<a href="search?q=[% r.param('query')| 文字コードの変換 | uri %]">
検索結果のリンク</a>


最初は自分で文字をエンコードするためのプラグインを書いて使っていましたが、CPANを探してみるとすでにいい感じものがありました。



Template::ToolkitでJcodeを利用可能にするためのプラグインです。時代はEncodeだと言われそうですが、utf8を扱わないのであれば、これで十分ですね。使い方はこんな感じです。


[% USE Jcode %]
<a href="search?q=[% r.param('query').jcode('euc').sjis | uri %]">
検索結果のリンク</a>