Catalyst response->redirect and login trick

03 November 2006


say, u have a "return $c->res->redirect('/login') unless ($c->user_exists);" in one sub(means one url like "/forum/1/topic/new"). and u tried to get "/forum/1/topic/new" as $c->req->referer; but Catalyst is somehow different, u don't get "/forum/1/topic/new" as your HTTP_REFERER. Catalyst treats $c->res->redirect inside.

mm, maybe you will get this problem when you code in Catalyst.
Here comes my solution:
Root.pm
sub end : Private {
my ( $self, $c ) = @_;

# for login using!
if ($c->res->location and $c->res->location eq '/login') {
$c->res->location('/login?referer=/' . $c->req->path);
}
return if ($c->res->body || $c->res->redirect);
Logon.pm after $c->login OK, use
# redirect
my $referer = $c->req->param('referer');
if ($referer) {
$c->res->redirect($referer);
} else {
$c->res->redirect('/');
}
and in Template: try to put "<input type='hidden' name='referer' value='[% c.req.param('referer') %]' />" in your action='/login' form.

OK, maybe I forget the '$c->req->uri->query' or '$c->req->body_parameters', but you can improve it, right?

Have fun!

Catalyst TT WRAPPER trick

01 November 2006


Catalyst::View::TT has two different ways:
one is set a $c->stash->{template} then $c->forward($c->view)
the other is using $c->view('TT')->render
so if u want to have a hack on View::TT. better hack on 'sub render' since 'sub process' is calling 'sub render' indeed.

assuimg that we have a very common situation. we have a wrapper.html for whole site. but we don't want to apply the wrapper.html in email body templates and /admin pages.
according to this, we might have two solution.
one is to create two view modulues like TT.pm and NoWrapperTT.pm. config the WRAPPER.
the other is to do some trick on 'sub render' as what I did today.
sub render {
my $self = shift;
my ( $c, $template, $args ) = @_;

# view Catalyst::View::TT for more details
my $vars = {
(ref $args eq 'HASH' ? %$args : %{ $c->stash() }),
};

if ($vars->{no_wrapper}) {
$self->template->service->{WRAPPER} = [];
} else {
$self->template->service->{WRAPPER} = ['wrapper.html'];
}

$self->NEXT::render(@_);
}
so that u can use something like:
my $email_body= $c->view('TT')->render($c, 'email/example.html', {
no_wrapper => 1,
another_var => $another_var,
} );
or set $c->stash->{no_wrapper} = 1 in Admin.pm

have fun.
Refer: Code in GoogleCode

Session::State::URI redefine

14 October 2006


当我们在 Catalyst 里同时用 Session::State::URI 和 Session::State::Cookie 来保持最大的 session 使用可能性的时候,有件事情可能并不是太完美。因为 Session::State::URI 会重新改写输出的结果,把 html 里的 link, a, img, script 等里的链接后面加上一个 sessionid. 但是对于 img, css 和 script 来说,一般情况下都是没必要的(浏览器会缓存 css/js/img, 但是如果后面加了 sid, 它会重新获取一次,增加了服务器的负担)。所以这时候我们可以 redefine Session::State::URI 里的一个 sub 来去掉 img/css/script 的 url 链接改写。
package Catalyst::Plugin::Session::State::URI;

no warnings 'redefine';

sub _session_rewriting_html_tag_map {
return {
a => "href",
form => "action",
# link => "href",
# img => "src",
# script => "src",
};
}
将这段代码随便放到某个 Controller/Model/Plugin 的 pm 里都可以。因为在 Catalyst 里所有的模块都是一次性载入的。

have fun.

Mail::Mailer::smtp_auth

08 October 2006


http://search.cpan.org/~fayland/Mail-Mailer-smtp_auth-0.01/lib/Mail/Mailer/smtp_auth.pm

since we use Mail::Mailer and AUTH PLAIN smtp, so I create this module and upload it to CPAN. have fun.

use Net::SMTP_auth to auth the smtp server

07 October 2006


应该说 Net::SMTP 的 auth 并不是很好,它是根据 SMTP 服务器返回的 AUTH 支持参数来进行下一步的 auth 行动。但是有时候 SMTP 会返回很多个 auth 类型如 LOGIN CRAM-MD5 PLAIN,但是你可能需要用 PLAIN 来进行验证。这时候 Net::SMTP_auth 会派上用场。
Net::SMTP_auth @ISA = qw(Net::SMTP); 唯一不同的是它比 Net::SMTP 的 auth 上多了一个参数。
Net::SMTP 的可能是这样 $smtp->auth($username, $password);
但是 Net::SMTP_auth 多了一个参数指定 auth 类型。$smtp->auth('PLAIN', $username, $password);
如果非得要用 Net::SMTP 来进行 AUTH PLAIN 的话也是可以的:
use MIME::Base64;
# after $stmp = Net::SMTP->new
$smtp->command('AUTH PLAIN')->response();
my $userpass = encode_base64("\000$username\000$password");$userpass =~ s/\n//g;
$smtp->command($userpass)->response();

好象是这样子的。Have fun!

XML.ObjTree and force_array

26 September 2006


在以前的 blog 里提过 JSAN 的 XML.ObjTree. See javascript XML parser
今天发现一点新东西。所以要做一下修正。
最主要是 force_array 这个以前被我忽略了但非常有用的参数。
<xml><message>OK</message></xml>
<xml><message>OK</message><message>DEL</message></xml>

以前写起来比较麻烦,要用 if (tree.xml.message) { 来判断是不是一个。
现在发现有了 force_array, 就非常简单了。:)

JSAN.use('XML.ObjTree');
var xotree = new XML.ObjTree();
xotree.force_array = [ "message" ];
var tree = xotree.parseXML( response );
for (i = 0; i <>不管有多少个 messages, 一律强制使用 array
以前的我真是有点笨。

any way, JSAN 和 XML.ObjTree 都非常好用。:)

Catalyst Session State

21 September 2006


我们一般使用 cookie 来保存 sessionid, 如果 cookie 失效的话就用 url 来传递 sessionid.
这个在 Catalyst 里非常简单。我们能同时使用 State::URI 和 State::Cookie 模块。
use Catalyst qw/
-Debug
Dumper
Config::ConfigLoader
Static::Simple
Authentication
Authentication::Store::DBIC
Authentication::Credential::Password
Session
Session::Store::DBIC
Session::State::URI
Session::State::Cookie
/;
将 URI 放在 Cookie 的前面。Session::State::URI 会判断有没有 load State::Cookie 而且 cookie 有没有效。如果 cookie 有效就使用 cookie, 无效就使用 uri.

perfect solution!

unihan modules updated

10 September 2006


抽个空把三个用 Unihan 数据库的模块给更新了。
Unihan 的数据太大,大概有 24 M, http://www.unicode.org/Public/UNIDATA/Unihan.txt
太大了,然后用个 Perl 脚本把这里面的 kCantonese|kMandarin|kTotalStrokes 抽出来。分别对应到 Cantonese PinYin Stroke 模块。
Unihan 里面还有些是很有用的。比如汉字翻译等。不过我个人没怎么用到,所以也就没写。

have fun.

Code Complete 2

09 September 2006


从当当订购了《代码大全2》,正在那看。稍微看了一点点,感觉还是挺不错的。等我多看点或许会写点感想。
平日里真是太忙。很少有时间去学习新东西,可这样可能很快就会跟不上时代了。上班没时间看资料,下班又不想碰电脑。还是买本书在床头看看比较好。
我们用 Catalyst 编写的新网站可能在这个月就要推出来了。感觉很兴奋。虽然不是很满意所有的代码,但是还是非常兴奋。我在 Zorpia 工作挺开心的。就是太忙了,程序员几乎都很忙。想着把 Lingua::Han 系列的模块更新到 Unicode 5.0 都没时间。看看明天能不能抽个空更新一下。

tips tips

01 September 2006


as perfaq9, 我们可以通过如下代码得到本机的 IP 地址:
use Sys::Hostname;
use Socket;
my $host = hostname();
my $address = inet_ntoa( scalar gethostbyname( $host || 'localhost' ) );
print $address

PS, the previous post, the "single and find" one shows that ->single don't support order_by attribute, but ->find support it. :)

Want to get a session data by sessionid in Catalyst?
u can insert a seesion_id in TT by [% c.sessionid %]
code like:
my $sid = $c->req->param('sid');
$c->log->debug("sid is $sid");
my $session_data = $c->get_session_data("session:$sid");
return $c->res->body(Dumper($session_data));

don't forget the "seesion:" before $sid.

Good luck.