Perl 6チームからのクリスマスプレゼント
この連載でも何度か名前が出てきたPerl 6ですが、
もちろんこの話には
オードリー・
実際、
この年前任のアリソン・ドラえもんもといPerl使いの真骨頂。
残念ながらPerl 6そのものではありませんでしたが、
それが、
どこまで本当の言語らしく書けるか
Maypoleという既存の成功したフレームワークからユーザを引き継いだCatalystに比べると、
が、
2007年のYAPC::Asiaでも紹介されていましたが
その背景にある技術についてはまた次回以降取り上げますが、
環境を構築する
ご多分に洩れず、
> svn co http://svn.jifty.org/svn/jifty.org/jifty/trunk
Jiftyのインストールが済んだら、
> jifty app --name MyApp
新しくできたMyAppディレクトリのなかには、
ログインまわりはプラグインを使えば簡単
今回は自分だけではなく、
framework:
Database:
Version: 0.0.1
Plugins:
- LetMe: {}
- User: {}
- Authentication::Password:
login_by: username
内容については直感的にわかると思いますので特に説明しませんが、
続いて、
> jifty model --name User
lib/
use Jifty::Plugin::User::Mixin::Model::User;
use Jifty::Plugin::Authentication::Password::Mixin::Model::User;
sub check_read_rights { 1 }
この段階で一度サーバを起動してみましょう。
> jifty server --start
これで自動的にデータベースの設定が行われて、
モデルを追加しよう
続いて、
> jifty model --name Entry
Userモデルのほうはプラグインのほうで用意してくれた出来合いのスキーマを使いましたが、
use MyApp::Record schema {
column body =>
type is 'text';
column user_id =>
type is 'integer',
refers_to MyApp::Model::User;
column epoch =>
type is 'integer',
default is defer { time() };
};
一見裸のワードに見える単語がいくつも並んでいますが、
refers_
複雑なアクセスコントロールを実装する
このモデルは、
このような複雑なアクセスコントロールは、
sub current_user_can {
my ($self, $right, %args) = @_;
return 1 if $right eq 'read';
return 1 if $args{user_id} && $self->current_user->id == $args{user_id};
return 1 if $self->user_id && $self->current_user->id == $self->user_id;
return $self->SUPER::current_user_can($right, %args);
}
スキーマを更新しよう
モデルの処理としてはこれでひとまず完成ですが、
まずはlib/
sub { '0.0.2' }
続いてetc/
Database:
Version: 0.0.2
それが済んだら、
> jifty schema --setup
これで新しいモデルがデータベースに登録されました。もちろん生のSQLを書く必要はありません。
ディスパッチャもDSLで
モデルができたところで、
エディタでこのようなlib/
package MyApp::Dispatcher;
use strict;
use warnings;
use Jifty::Dispatcher -base;
under 'user/*' => [
run {
my ($name) = ($1);
my $user = MyApp::Model::User->load_by_cols(name => $name);
set user => $user;
},
on qr/(\d+)/ => run {
my ($epoch) = ($1);
my $user = get('user');
my $entry = MyApp::Model::Entry->load_by_cols(
user_id => $user->id,
epoch => $epoch,
);
set entry => $entry;
show '/entry';
},
on '' => show '/list',
];
1;
ここではhttp://
なお、
ビューもDSLで
続いてビューを作成しましょう。エディタでこのようなlib/
package MyApp::View;
use strict;
use warnings;
use Jifty::View::Declare -base;
use Time::Piece;
template 'list' => page {
my $user = get('user');
if (Jifty->web->current_user->id == $user->id) {
h1 { "Your tweets" }
show 'form';
}
else {
h1 { "Tweets by $name..." }
}
my $entries = MyApp::Model::EntryCollection->new;
$entries->order_by( column => 'epoch', order => 'DESC' );
$entries->rows_per_page(15);
$entries->limit( column => 'user_id', value => $user->id );
while (my $entry = $entries->next) {
show('_entry' => $entry);
}
};
template 'entry' => page {
my $entry = get('entry');
show('_entry' => $entry);
};
設定がメインのJiftyのDSLとは違って、
private template '_entry' => sub {
my ($self, $entry) = @_;
div { $entry->body }
p {
my $time = localtime($entry->epoch);
span { outs "by " . $entry->user->name .
" at " . $time->date . ' ' . $time->time }
span {
my $url = join '/', "/user",
$entry->user->name,
$entry->epoch;
hyperlink( url => $url, label => 'permalink' );
}
}
};
一覧と個別のエントリで共通に使える小さな部品はプライベートテンプレートとしてくくりだしておきました。$entry->user->nameの部分では外部キーによる参照を行っています。
private template 'form' => sub {
my $user = get('user');
my $name = $user->name;
my $action = new_action(
class => 'MyApp::Action::CreateEntry',
moniker => 'create_entry',
);
div { attr { class => 'Form' }
Jifty->web->form->start(submit_to => "/user/$name/post");
div {
render_param( $action => 'body', focus => 1 );
Jifty->web->return(
submit => $action,
label => 'Tweet',
as_button => 1,
);
};
Jifty->web->form->end;
}
};
1;
Jiftyのフォームはふつう対応するアクションと密接に結びついています。データベースのCRUDや検索はすべて専用のアクションを用意するのがJiftyの流儀です
Jiftyのアクション
ここではとりあえず作成用のアクションだけ用意しましょう。シェルから次のコマンドを実行してください。Entryモデルにひもづけられたアクションのひな形が用意されます。
> jifty action --name CreateEntry
アクションにはモデルの定義によく似たパラメータの定義を用意できます。monikerはこのアクションの識別名です
use Jifty::Action schema {
param body =>
label is '',
type is 'text',
display_length is 40,
max_length is 255,
sticky is 0;
};
sub moniker { 'create_entry' }
実際のアクションの中身はtake_
sub take_action {
my $self = shift;
my $entry = MyApp::Model::Entry->new;
$entry->create(
body => $self->argument_value('body'),
epoch => time,
user_id => $self->current_user->id,
);
Jifty->web->next_page("/home");
$self->report_success if not $self->result->failure;
return 1;
}
これでひとまず動くアプリケーションができました。サーバを起動して、
単純に比較するのは間違っていますが
なるべく本体は軽くしてCPANモジュールの触媒、
でも、
もちろんCatalystにはCatalystの長所がありますし、
国内ではあまり大きな採用実績はありませんが、