Zorpia team photo and me

17 April 2006




右往左数二 is me.

See also: my boss's journal

Catalyst and $c->res->redirect

17 April 2006


今天发现关于 $c->res->redirect 的一件事。post 在这 http://lists.rawmode.org/pipermail/catalyst/2006-April/006758.html
sub test : Global {
my ( $self, $c ) = @_;
unless ($c->user_exists) {
$c->res->redirect('/login');
# return 1;
}
$c->res->body($c->user->user_id);
}
注意这里的 $c->res->redirect, 它不是马上一碰到就执行的,而是全部执行完整个 test 子程序才执行的。
也就是说,上面的代码是错误的。当用户没有登陆了,虽然要执行 $c->res->redirect, 但是它还要去执行 $c->res->body($c->user->user_id); 而不是一碰到 redirect 就直接跳转。所以代码会在 $c->res->body($c->user->user_id); 这句出错。因为没登陆时是不能访问 $c->user->user_id 的。把 return 1; 的注释去掉就可以了。

写代码事要注意这件事。:)

JSAN and Form.Validator

15 April 2006


大致上是我第一次用 JSAN. JSAN 的意思是 "CPAN".replace(/CP/, "JS") 它的目标是成为类似 CPAN 一样的 JS 库。 CPAN 是每个 Perl hacker 的骄傲。不过 JSAN 目前的东西不是很多。

我用的是 Form.Validator, 检验 form 输入的东西。不过 JSAN 这个模块的命令似乎在 Win32 下跑不起来。只好一个个去下。
http://www.openjsan.org/doc/c/cw/cwest/JSAN/0.10/
http://www.openjsan.org/doc/b/be/beppu/Form/Validator/0.33/index.html

下回来。目录的结果大致为:
JSAN.js
Form/Validator.js
Form/Validator/*
根据 http://www.openjsan.org/doc/b/be/beppu/Form/Validator/0.33/lib/Form/Validator.html 这里的说法写 HTML 文件。大致为:
<script src='/js/JSAN.js'></script>
<form name="stuff" ...
<script>
JSAN.use('Form.Validator');
JSAN.use('Form.Validator.Report.AlertAll');
fv = new Form.Validator(document.stuff);
fv.reporter('AlertAll'); // show a pop-up with all errors
fv.set('title', 'notBlank', "Title is required.");
fv.set('text', fv.makeValueMin(5), "Text must be 5 or greater.");
</script>

have fun.

Ajax.Updater and evalScripts: true

11 April 2006


OK, 我昨天说 Catalyst 的 redirect 和 Ajax 的那个不合拍。后来得到那边的提示。用这样的代码解决了。
Controller:
sub vote : Global {
my ( $self, $c ) = @_;

unless ($c->user_exists) {
#$c->res->redirect('/login');
$c->res->body(<<HTML);
<script>
top.location = '/login';
</script>
HTML
return 1;
}
TT files:
var myAjax = new Ajax.Updater('vote_result', url, {method: 'get', parameters: pars, evalScripts: true});


这里多了个 evalScripts: true 这样可以在 div 里运行返回的 javascript.
Yup, It's OK now. 但是我想 prototype.js 应该能自动处理 redirect 这样的 Location header.

work and ask

10 April 2006


不工作的时候总是没有问题,可一工作,总是会碰到这样那样的问题。碰到问题解决问题,才算是学会点东西。

写 Catalyst 的时候用 Ajax, 然后某一动作需要先确定用户登陆。但是 Ajax 只是更新某一个 div, 所以在 Controller 里写 $c->res->redirect('/login') unless ($c->user_exists); 是没有用的。redirect 只对整个 page 有效,而对 Ajax 的 div 是没有效果的。
大致的代码如下:
Controller:
sub vote : Global {
my ( $self, $c ) = @_;

$c->res->redirect('/login') unless ($c->user_exists);
and TT files:
var url = '/vote';
var myAjax = new Ajax.Updater('vote_result', url, {method: 'get',
parameters: pars});

<div id='vote_result'></div>
目前没有解决方案。mailling list 的问题在这:http://lists.rawmode.org/pipermail/catalyst/2006-April/thread.html#6563

另两个是 DBIx::Class 的问题。

第一个是 debug 问题。我想在 Catalyst 开启 DBIx::Class 的 debug, 这样我用 perl server.pl 的时候能看到所有的 SQL 语句。这个其实很简单。只要设置
$ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} = 1;
将这个写在 Root.pm 的 sub auto 里就可以了。

第二个是 update_or_create 的问题。因为 update_or_create 很好用。我们 new 或 edit 的时候就能采用同一的代码。new 是 create 而 edit 是 update, 但是我想知道什么时候是 create 什么时候是 update, 因为我要针对不同的 create 还是 update 写一点不同的代码。不过解决方案未知。也在问中。mailing list 的帖子在:http://lists.rawmode.org/pipermail/dbix-class/2006-April/thread.html#1192

Template Plugin HtmlToText

05 April 2006


It's a TT plugin for format html to text, a plugin interface to HTML::FormatText.

将 HTML 用 HTML::FormatText 转为 Text 文本。

Template::Plugin::HtmlToText is a easy plugin, all code is here:
package Template::Plugin::HtmlToText;

use strict;
use vars qw( @ISA $VERSION );
use base qw( Template::Plugin );
use Template::Plugin;

$VERSION = '0.01';

sub new {
my ($class, $context, $arg) = @_;
$context->define_filter('html2text', [ \&html2text => 1 ]);
return \&tt_wrap;
}

sub html2text {
my ($context, $args) = @_;
return sub {
my $html = shift;
return $html unless ($html =~ m#(<|>)#s);

require HTML::TreeBuilder;
my $tree = HTML::TreeBuilder->new->parse($html);
require HTML::FormatText;
my $formatter = HTML::FormatText->new(%{$args});
my $text = $formatter->format($tree);
return $text;
}
}

1;

Date Manip and Template Plugin FillInForm

04 April 2006


今天用 Date::Manip 的时候碰到 Date::Manip unable to determine TimeZone 这样的错误。
后来通过 perldoc Date::Manip 设置了环境变量 TZ 为 +0800 就可以了。

另一个是用 Template::Plugin::FillInForm 的时候。
很多时候用户输入错误后,我们会跳转到用户输入的界面,然后填充用户输入的字段。一般来说对于 input/text 这样可以设置 value='[% c.req.param('name') %], 但是对于 radio checkbox select 这样就比较复杂了。而 HTML::FillInForm 用 HTML::Parser 分析字段并填充,很方便。用法很简单:
[% USE FillInForm %]
[% FILTER fillinform fdat => journal_date || c.req.params %]
<form>
<input type='radio'
[% END %]

啥都不用写,就前面一句 [% USE 后面一句 [% END %]
但是这种情况在以 DBIx::Class 情况下会失效。因为 FillInForm 用 $journal->{title} 来填充,而 DBIx::Class 是用 $journal->title 来获取的。这在 TT 下没关系,因为都可以用 [% journal.text %] 来获取的。但是对于 FillInForm Plugin 就会失效。我目前的折中方法是多添加一句:
[% USE FillInForm %]
[% IF journal;
journal_date = {
title => journal.title,
};
END; %]
[% FILTER fillinform fdat => journal_date || c.req.params %]

而不是用这段失效的:
[% USE FillInForm %]
[% FILTER fillinform fdat => journal || c.req.params %]

Authentication and DBIC

23 March 2006


因为正在搞的项目使用了多个数据源(说过好多次了。:) 所以只能在运行时指定 Catalyst::Plugin::Authentication::Store::DBIC 所采用的表所在的数据源。将这个 config 放到 Root.pm 下总是会报错。怎么改也改不好,大致原因是 Catalyst 一运行就载入所有的插件 Plugins, 而载入插件时插件就会读取它自己的 config ,所以在 __PACKAGE__->setup; 后修改插件的 config 是不起作用的。而我们获取表所在的数据源需要知道 $c->req->address 类似东西,所以一定会是在 setup 以后。
最后迫不得已到 Catalyst mailing list 发了个 [Catalyst] Catalyst::Plugin::Authentication::Store::DBIC and config in auto
邮件组里的人倒没给我解决,可能是时差关系。因为这是 $work, 所以就只好自己看源码了。最后的报错总是 Can't call method "search" on an undefined value at,研究了个把小时,总算搞定了。

写一个 Module 专门用于 Authentication:
package MyApp::Model::UserAuth;

use strict;
use warnings;
use base 'Catalyst::Model';

sub search {
my $self = shift;
my $user = MyApp->config->{dbic}{geo}->resultset('User')->search(@_);
return $user;
}

1;
这里的 search 方法是专门给 DBIC::User 用的。因为这是一个 Model, 所以可以得到存到 config 里的数据源所在的表的。最后设置 yml 文件。
authentication:
dbic:
user_class: "MyApp::Model::UserAuth"
user_field: "username"
password_field: "password"

:) I like $$$$$.

MySQL 首字母大写

21 March 2006


MySQL 中有个将所有字母大写的函数,UPPER
但是没有将首字母大写的函数,今天要用到,所以写了一个。
mysql > update test set text = concat(upper(left(text, 1)), mid(text, 2, length(text) - 1));

test 是 table 表,text 为表的字段 field

concat 是用来连接字符串的。
upper 是将所有的字母大写
left(text, 1) 是取出 text 的第一个字符
mid(text, 2, length(text) - 1) 是取出从第二个字符到最后一个。

good luck

Update

OK, 这东西对 lin dao 这样的两个字段是不试用,它只能改成 Lin dao. 如果要改为 Lin Dao 的话那就比较复杂了。还好我们可以用 Perl 快速的写一段代码:
#!/usr/bin/perl

use strict;
use DBI;

my $dbh = DBI->connect("DBI:mysql:fayland:localhost",
'root', 'pass', { RaiseError => 1, PrintError => 1 }) or die $DBI::errstr;

my $sth = $dbh->prepare(
qq{SELECT * FROM test}
);
$sth->execute() or die $DBI::errstr;

my $records = $sth->fetchall_arrayref({});

$sth = $dbh->prepare(
qq{UPDATE test SET text = ? WHERE text = ?}
);

foreach my $record (@$records) {
my $old = $record->{text};
$record->{text} =~ s/(\w+)/\u\L$1/g;
$sth->execute($record->{text}, $old) or die $DBI::errstr;
}

print 'ok';

Marriage wassail of my sister

20 March 2006


I need to engage in my one and only sister's marriage wassail this weekend. It means that I cann't join the PerlChina Beijing Conference even I'm willing to.

It's just a coincidence I cann't refuse. Any way, wish you guys have a good party.
Wish my sister happy.