uri_forのパッチ
いまどきのperl使いな皆様におかれましては内部ではutf8フラグを立てたutf8 stringとして文字列を扱っていると思いますが、そういった場合は uri_for
は正しく動作します。
そうではなく、utf8 bytes を渡した場合、今の uri_for
では壊れたURIが帰って来るというバグがあります。単純なミスなのだけど。
かなり前からなんだけど誰も気がつかないのか必要ないのか一向に直らないので業を煮やしてメールでパッチ送ったところすぐに反映してくれた。ナイス。
パッチはこんなん。
canonical してないのは仕様らしい。(昔のはしてたよね?)
mst によれば
For RSS/atom feeds etc.
とのこと。どゆこと?
舌足らずすぎた。
Model::DBIC:
connect_info:
- dbi:mysql:table
- root
- on_connect_do:
- SET NAMES utf8
cursor_class: DBIx::Class::Cursor::Cached
cache_file: __path_to(tmp/query_cache)__
さっきはこんなconfigで使った場合のコードです。
DBIx::Class::Cursor::Cachedつかってみた
すばらしすぎる。もっと早く使えばよかったとおもった。
Catalyst::Model::DBIC::Schema
で使う場合はこんな感じでOK。
sub new {
my $self = shift->NEXT::new(@_);
my $cache = Cache::FastMmap->new( share_file => $self->{cache_file} );
$self->schema->default_resultset_attributes({
cache_object => $cache,
});
$self;
}
そんで、あとはsearchのattrとして { cache_for => 300 } とかかいてやればそのクエリは300秒キャッシュされる。
実際にはsearch時ではなくてallとかnextのときにそのクエリをキャッシュしてる。
ちなみにdefault_resultset_attributes
にcache_for
を含めるとすべてのクエリがキャッシュされる。
CatalystアプリのスタータースクリプトをCodeReposに
あげた。
もともと使ってるのはpmsetupを改造したので雛形つくるだけだったんだけど、毎回DBとか使うわけでもないので、ついでにそこを選べるように改造した。
ほぼ僕専用ルールにのっとった雛形を作成しますので他の方はまったく使えないと思いますが、参考にはなるかもしれません。
他の方のも見てみたいのでぜひともcodereposで共有しましょう!
あ、Catalyst自分規約カンファレンス、とかおもしろいかもしれない。
IP認証とBasic認証を同時に使うCatalystコードサンプル
Catalystは Authentication::Credential::*
プラグインを複数使うことにより、複数の認証方式に対応できる。
IP認証などのような、Credプラグインが提供されていないものも即席でCredインタフェースにそったモジュールを作ることで簡単に認証方法を追加することができる。
MyApp.pm はこんな感じ
package MyApp;
use strict;
use warnings;
use Catalyst::Runtime '5.70';
use Catalyst qw/
ConfigLoader
Authentication
Authentication::Credential::HTTP
+MyApp::Credential::IP
Authentication::Store::DBIC
/;
our $VERSION = '0.01';
__PACKAGE__->setup;
Cred::HTTP
が Basic 認証用のモジュール。+MyApp::Credential::IP
がIP認証用のオリジナルモジュール。
この MyApp::Credential::IP
のコードはこんな感じ。
package MyApp::Credential::IP;
use strict;
use warnings;
use base qw/Catalyst::Plugin::Authentication::Credential::Password/;
sub authenticate_from_address {
my $c = shift;
my $ip = $c->model('DBIC::UserIp')->find({ ip => $c->req->address }, { prefetch => 'user' })
or return;
my $user = $c->get_user( $ip->user->username )
or return;
$c->set_authenticated($user);
1;
}
1;
こんな短いコードで認証増やせるの。なかなか便利。
で、実際に利用するのは以下のように、Root Controller の auto アクションに
sub auto :Private {
my ($self, $c) = @_;
# basic auth
$c->authenticate_http and return 1;
# ip auth
$c->authenticate_from_address and return 1;
# return basic auth response when no auth success
$c->authorization_required_response( realm => 'Require Authentication' );
return;
}
などとする。
全体に認証かけたいときはルートコントローラの auto に、コントローラ個別に指定したいときは、上記コードを埋め込んだ認証用の親コントローラクラスつくって認証必要なコントローラはそれを継承する。
パスごとにやりたいときは Authentication::ACL
ってのがあるけど僕は使っていない。
X-Sendfile の逆がほしい
ファイルアップロードしたときに、サーバーが自動でどっかファイルに保存しつつ受信して、fastcgi にはファイルを渡さずファイル名を渡す。
結構でかいファイルだと Webサーバー <-> FastCGI 間のファイルデータのやり取りに時間かかってすげー無駄だ。
lighty が実装しねーかなぁ。
perlbal でできたりするのかな。
Catalyst で Log::Dispatch を使うの法
同僚から聞かれたので、僕のやり方を書いておく。
すでにCPANに Catalyst::Plugin::Log::Dispatch
というのがあるのだけど、なんかいけてないのでそれは使わず、以下のようにしている。
まず MyApp::Logger
とかいうのをかく
package MyApp::Logger;
use strict;
use warnings;
use Log::Dispatch::Config;
use Log::Dispatch::Configurator::YAML;
use Catalyst::Utils;
use NEXT;
{
package MyApp::Logger::Backend;
use base qw/Log::Dispatch::Config/;
}
sub setup {
my $c = shift;
my $class = ref $c || $c;
$c->log->_flush if $c->log->can('_flush');
my $config = $c->config->{log}{config}
|| $c->path_to( Catalyst::Utils::appprefix($class) . '_log.yml' );
MyApp::Logger::Backend->configure_and_watch(
Log::Dispatch::Configurator::YAML->new($config) );
$class->log( MyApp::Logger::Backend->instance );
$c->NEXT::setup(@_);
}
1;
んでこれをプラグインとしてロード。
use Catalyst qw/+MyApp::Logger/;
ログの設定ファイルを書く(デフォルトmyapp_log.yml)
---
dispatchers:
- file
- screen
file:
class: Log::Dispatch::File
min_level: debug
filename: /path/to/log
mode: append
format: '[%d] [%p] %m at %F line %L%n'
screen:
class: Log::Dispatch::Screen
min_level: debug
stderr: 1
format: '[%p] %m%n'
以上。
MyApp::Logger::Backend
とかしてるのは mod_perl 対策。まーつかわんのだけど。
これ、さっきヘルパーにした。
これ使えば
./script/myapp_create.pl Log::Dispatch
すると Logger クラスとサンプルの yml ファイルを生成してくれる。
Catalyst + 外部認証API
今作ってるサービス。
use Catalyst qw/
...
Authentication
Authentication::Credential::Password
Authentication::Credential::OpenID
Authentication::Credential::TypeKey
Authentication::Credential::Hatena
Authentication::Credential::JugemKey
+MyApp::UserStore
...
/;
MyApp::UserStore
は A::Store::DBIC
のちょっとしたサブクラスで、Cred::Password
のときは auto_create_user
しないようにしただけのもの。
最近の Store::DBIC
の auto_create_user
機能つかうと外部認証使うサービス簡単に作れる。
Cred::Password
使わないなら (独自ユーザーを使わないなら) 、Store::DBIC
そのまま使えばいける。
すばらしいなー。
リリースしたらちゃんとした記事を書きたい。
学生さんのcatアプリ見せてもらった。なんかすごいがんばってた。
Chainedアクションがないころのなのか、/wiki/{pagename}/edit
とかそれ系のコントローラを Regex コントローラでがんばってたりしてた。
Plugin::Images
とかつかってた。しらんかった。なんかあれだなーと思ったら作者見て納得。nothingmuch のか。
Plugin::Static::Simple
とかは使わないほうがいいよ。静的ファイルはサーバー直でやんないと無駄。ロードするだけですらッ!
それで思い出したけど、lighttpdの設定、最近 mod_alias
も mod_setenv
も mod_rewrite
もつかわない書式を思いついて使ってる。
$HTTP["url"] =~ "^/(?!favicon\.ico$|static/|js/|css/|images?/)" {
fastcgi.server = (
"" => (
( "socket" => "/path/to/myapp.socket",
"check-local" => "disable", ),
),
)
}
こんなの。
とにかく、技術はどうあれ、作ってみる、形にするってことはやっぱり重要だなと思った。口だけなら何でもいえる。
Catalyst::Plugin::Firebug
とか言うのを書いた。
ロードしておくとDebugモードのときに出力htmlに自動でFirebug liteのコードが埋め込まれるというやつ。
でも激しくDISられたので、ActionClassにしてリリースしなおす予定。うう><