catalyst.perl.kmtz.net RSSフィード

2007-07-25Catalystログイン このエントリーを含むブックマーク

Catalyst::Plugin::Authentication 0.10002 と Catalyst::Plugin::Session 0.17 にアップデートしたら、セッションIDをクッキーに保存したままブラウザを閉じて、しばらくたってからアクセスすると、$c->user_exists は1(真)を返すのに、$c->user が undefined になって取得できなくなる事象が頻発。一度ログアウトしてログインしなおさないと直らない。

再現条件すら確認中だから、本当にモジュールのせいか不明だけど致命的だ。。。

2007-05-19動的に文字コードを変更して出力する Catalyst::Plugin::Unicode::Encoding このエントリーを含むブックマーク

リクエスト毎に出力の文字コードを変換するプラグインCatalyst::Plugin::Unicode::Encoding を元に作成してみた。下の例だと、shiftjis と utf8 のみに対応するが、$c->config から読み込むようにも簡単に変更可能。

package Catalyst::Plugin::Private::Unicode::Encoding;

use strict;
use warnings;
use Encode 2.10 ();
use Encode::Guess qw(utf8 shiftjis);

our $VERSION = 0.01;
our $CHECK   = Encode::FB_CROAK | Encode::LEAVE_SRC;

sub finalize {
    my $c = shift;

    unless ( $c->res->body ) {
        return $c->NEXT::finalize;
    }

    unless ( $c->res->content_type =~ /^text|xml$|javascript$/ ) {
        return $c->NEXT::finalize;
    }

    unless ( Encode::is_utf8( $c->res->body ) ) {
        return $c->NEXT::finalize;
    }

    unless ( ref $c->stash->{'encoding'} ) {
        unless ( $c->stash->{'encoding'} =
            Encode::find_encoding( $c->stash->{'encoding'} ) )
        {
            $c->stash->{'encoding'} = Encode::find_encoding('utf8');
        }
        $c->log->debug( 'changed:' . $c->stash->{'encoding'}->mime_name );
    }
    $c->log->debug( 'out:' . $c->stash->{'encoding'}->mime_name );
    $c->res->body( $c->stash->{'encoding'}->encode( $c->res->body, $CHECK ) );

    $c->NEXT::finalize;
}

sub prepare_parameters {
    my $c = shift;

    $c->NEXT::prepare_parameters;

    my $str;
    for my $value ( values %{ $c->req->params } ) {
        $str .= $_ for ( ref($value) ? @{$value} : $value );
    }
    my $decoder;
    if ($str) {
        eval {
            $decoder =
              Encode::Guess->guess_encoding( $str, qw(utf8 shiftjis) );
        };
        $decoder = Encode::find_encoding('utf8') unless ($decoder);
        for my $value ( values %{ $c->req->params } ) {
            next
              if ( ref $value && ref $value ne 'ARRAY' );
            for ( ref($value) ? @{$value} : $value ) {
                $_ = $decoder->decode( $_, $CHECK );
            }
        }
    }
    else {
        $decoder = Encode::find_encoding('utf8');
    }
    $c->stash->{'encoding'} = $decoder;
    $c->log->debug( 'in:' . $c->stash->{'encoding'}->mime_name );
}

1;

__END__

コントローラの中で、

$c->stash->{'encoding'} = 'shiftjis';
# or
$c->stash->{'encoding'} = 'utf8';

のように指定するとその文字コードで出力する。テンプレートは全て utf8 で記述することを想定。ちなみに、使用される文字コードルールは下のような感じ。

1.リクエスト時にフォーム等でパラメータが渡されている場合は、パラメータ文字コード自動判別して utf8 に変換してからコントローラに渡す。(コントローラでは従来どおり $c->req->param でアクセス

2.レスポンスの内容を utf8 から指定の文字コードに変換して出力する。文字コード$c->stash->{'encoding'} で指定されていればそれを使い、なければ1の処理に使った文字コードを使用し、1の際にパラメータがなく判別されなかった場合には utf8 が使用される。

2007-05-14Catalyst::Plugin::Unicode::Encoding で動的に文字コードを指定したい このエントリーを含むブックマーク

携帯キャリア判定を行って出力の文字エンコードを変更するなんてことはよくあるんじゃないかと思うけど、イマイチやり方がわからない。

最初はテンプレートは全て utf8 なので、Catalyst::Plugin::Unicode::Encoding でできるのかなと思った。このプラグインだと、myapp.yml の中に encoding を設定しておくと変換して出力してくれる。だが、コントローラ携帯キャリア判定をして、そこで動的に文字コードを指定しようと思うとできなさげ。

実際に Catalyst::Plugin::Unicode::Encoding のソースコードを読むと、sub encoding の中で ref($c) で処理が分岐していて、MyApp->setup から sub setup 経由で呼ばれた場合と、Catalyst::Plugin::Unicode::Encoding->encoding と直接呼ばれた場合の両方に対応しようという意図が見える(気がする)。

前者後者$c が別物なのでわかりにくいが、前者ではエンコード情報Catalyst->{'encoding'} に格納し、後者では Catalyst::Plugin::Unicode::Encoding::_encoding に格納しているように読める。しかし、sub finalize の中では Catalyst->{'encoding'} しか見ていない。だから、Catalyst::Plugin::Unicode::Encoding->encoding で設定しても無視されるようだ。

ためしに、sub finalize の中で

$c->log->debug(__PACKAGE__->_encoding->mime_name);

$c->log->debug($c->encoding->mime_name);

とやると、前者Shift_JIS と表示しても、後者UTF-8 だったりする。

そこで、下記のように変えてみた

     unless ( Encode::is_utf8( $c->response->body ) ) {
         return $c->NEXT::finalize;
     }
-    $c->response->body( $c->encoding->encode( $c->response->body, $CHECK ) );
+    my $encoding = __PACKAGE__->_encoding || $c->encoding;
+    $c->response->body( $encoding->encode( $c->response->body, $CHECK ) );
+    __PACKAGE__->_encoding('');

     $c->NEXT::finalize;
 }

これでうまくいったように一瞬思えた。

しかし、Catalyst::Plugin::Unicode::Encoding は sub prepare_parameters でも設定された文字コードとして、HTTPで渡されてたパラメータを utf8 フラグ付きの文字列に変換している。この部分は finalize と違ってコントローラが受け取る前に処理してしまうので、キャリア判定後なんてことは原理的に不可能に思える。そうなると文字コード推測するようにするしかないのかな?

というわけで、同じような要件をクリアされた方がいらっしゃったら解決方法もしくはアドバイスをいただきたく。

全然勘違いなこと言っているよという指摘でもありがたいです。

ちなみに、上記 diff の最後の __PACKAGE__->_encoding(''); は mod_perl 環境下なので、Catalyst::Plugin::Unicode::Encoding->encoding を一度どれかのリクエストの際に呼び出すと、以降のリクエストでは、同様に上書きするまでデフォルトの設定ではなく Catalyst::Plugin::Unicode::Encoding->encoding の設定が共有されてしまうのを防ぐため。ただ、場合によっては初期化前に別のリクエストが finalize フェーズを通過する場合も考えられるので、そもそもクラス変数を使うことが間違い?(意図が違う?)

(追記5/19)とりあえず書いてみた。

http://catalyst.g.hatena.ne.jp/yosty/20070519

StretchStretch2011/08/03 01:17Never seen a betetr post! ICOCBW

lzewlcgtlzewlcgt2011/08/04 23:24S6pjC3 , [url=http://lyrveoxfeked.com/]lyrveoxfeked[/url], [link=http://wfddcwssxuwv.com/]wfddcwssxuwv[/link], http://ykmomoagsbuq.com/

nhptvwinhptvwi2011/08/05 18:37funD8f <a href="http://mvcxofnjkpzh.com/">mvcxofnjkpzh</a>

gvufwnigvufwni2011/08/07 00:29olf4UE , [url=http://jqbtuuxezjly.com/]jqbtuuxezjly[/url], [link=http://dtztkmqcthhu.com/]dtztkmqcthhu[/link], http://gizqexqurazy.com/

2007-04-18Class::DBI::Sweet と Class::DBI::utf8 このエントリーを含むブックマーク

Catalyst::Model::CDBI::Sweet を使っていて、Class::DBI::utf8 を併用したら、Class::DBI::utf8 が Class::DBI::Search::Basic の _search_for を使うせいか、SQL::Abstract の -or 等を利用した複雑なクエリが書けなくなっている気がする。(詳細を確認しきれていないけど)

Catalyst - Caught exception in Calendar::Controller::Service->events "-or is not a column of Calendar::Model::CDBI::Events at /usr/lib/perl5/site_perl/5.8.5/Class/DBI/Search/Basic.pm line 115

というわけであとで調べるそろそろ本気でDBIC。

whiygayzrwhiygayzr2011/03/24 10:09ve5RJI <a href="http://lbceslszyebu.com/">lbceslszyebu</a>, [url=http://lxpekcmxgiqw.com/]lxpekcmxgiqw[/url], [link=http://pqeosrotzqdh.com/]pqeosrotzqdh[/link], http://jrsxllezqdvi.com/

2007-04-17Catalyst::Controller::FormBuilder このエントリーを含むブックマーク

Catalyst::View::TT + Catalyst::Plugin::FillInForm + Catalyst::Plugin::FormValidator::Simple から、Catalyst::Controller::FormBuilder に乗り換えを検討してみたけど、下あたりを見てとりあえず延期w

http://catalyst.g.hatena.ne.jp/tokuhirom/20061129/1164765429

http://catalyst.g.hatena.ne.jp/ikasam_a/20061130/1164842382

http://blog.hide-k.net/archives/2006/11/catalystplugins.php

2007-04-14C::P::Authentication::Store::DBIC::User で型グロブを使ったメソッドへのアクセ このエントリーを含むブックマーク

Catalyst::Plugin::Authentication::Store::DBIC を利用して認証していて、ユーザレコードが下みたいな感じだった場合、

{ 
  id   =>  1,
  name =>  'username',
  password => 'password',
  age  => 27,
  type =>  'a'
}

$user->age や $user->type とはできるが、$user->id ではなく $user->obj->id としないといけない。

ほげーと思って、Catalyst::Plugin::Authentication::Store::DBIC::Userソースを確認したところ、

sub new {
    my ( $class, $id, $config ) = @_;

    my $query = @{$config->{auth}{user_field}} > 1
        ? { -or => [ map { { $_ => $id } } @{$config->{auth}{user_field}} ] }
        : { $config->{auth}{user_field}[0] => $id };

    my $user_obj = $config->{auth}{user_class}->search($query)->first;
    return unless $user_obj;

    bless {
        id     => $id,
        config => $config,
        obj    => $user_obj,
    }, $class;
}

*user = \&obj;

ということは、id と config と obj だけが、obj を経由しなくても $user_obj のメソッドにアクセスできるってことかな。型グロブに代入しても上書きはされないのかな?このあたりの理解がたりないなー。ラクダ本。。。

JeanJean2011/08/02 13:25I really couldn't ask for more from this aritcle.

ppwgrdvppwgrdv2011/08/02 20:47blOlYF <a href="http://yfcioqrhngji.com/">yfcioqrhngji</a>

ymcmypymcmyp2011/08/03 21:13wX7xpc , [url=http://wbiilywelrnf.com/]wbiilywelrnf[/url], [link=http://fhcsquxbzxtt.com/]fhcsquxbzxtt[/link], http://ntsptsmbeytc.com/

kfzzdustyykfzzdustyy2011/08/05 18:39JCthrg <a href="http://vwysuzxeygwy.com/">vwysuzxeygwy</a>

oujzgdroujzgdr2011/08/05 22:53e9RxlN , [url=http://azjvsobzalqn.com/]azjvsobzalqn[/url], [link=http://yslyzowyirlj.com/]yslyzowyirlj[/link], http://wyxupddsemag.com/

2007-04-10Catalyst::Plugin::DumperとCatalyst::Log::Log4perlの併用 このエントリーを含むブックマーク

Catalyst::Plugin::Dumper$c->log->dumper で Data::Dumper::dumper を呼び出せるだけのシンプルモジュールだけど、使いたいときに毎度 use しなくていいので開発中はだいたい組み込んでる。

でも、Catalyst::Plugin::Dumper は dumper メソッドを Catalyst::Log::dumper とハードコードしているので、Catalyst::Log::Log4perl を併用すると下記のようなエラーをはいてしまう。きっと Catalyst::Plugin::Log::Dispatch とか $c->log を置き換えるプラグインでも同じようなエラーになるはず。

Caught exception in MyApp::Controller::Member->main "Can't locate object method "dumper" via package "Catalyst::Log::Log4perl"

とりあえず、Catalyst::Log::dumper を Catalyst::Log::Log4perl::dumper に書き換えて使うと動作するが、もう少し汎用性を持たせるために 0.02 にパッチを書いてみた。

*** Dumper.pm   2006-01-16 20:46:36.000000000 +0900
--- Dumper.pm.new       2007-04-10 23:54:52.000000000 +0900
***************
*** 10,26 ****
  use Data::Dumper;
  $Data::Dumper::Sortkeys = 1;

! sub Catalyst::Log::dumper {
!   my ($self, $var, $label) = @_;

!   if (defined $label) {
!       # temporarily change the Varname that D::D uses
!       local $Data::Dumper::Varname = $label;
!       $self->debug( Dumper($var) );
!   }
!   else {
!       $self->debug( Dumper($var) );
!   }
  }

  1; # Magic true value required at end of module
--- 10,33 ----
  use Data::Dumper;
  $Data::Dumper::Sortkeys = 1;

! sub setup {
!     my $class = shift;

!     $class->NEXT::setup();
!     eval( sprintf( <<'    EOL', ref $class->log ) );
!         sub %s::dumper {
!             my ( $self, $var, $label ) = @_;
!
!             if ( defined $label ) {
!                 # temporarily change the Varname that D::D uses
!                 local $Data::Dumper::Varname = $label;
!                 $self->debug( Dumper($var) );
!             }
!             else {
!                 $self->debug( Dumper($var) );
!             }
!         }
!     EOL
  }

  1; # Magic true value required at end of module

ついでに、つたない英語bug と patch を報告。

http://rt.cpan.org//Ticket/Display.html?id=26233

Catalyst::Log::Log4perl を使う場合には Catalyst->log() を Catalyst->setup() の前に呼ぶ必要がある。(そうじゃなくてもこの順番だけど)

他に、Catalyst::Plugin::Log::Dispatch を使う場合には use Catalyst qw/Log::Dispatch Dumper/; のように Dumper を後に記述する必要がある。

eval と sprintf なんて綺麗じゃない気もするけど...

JavaScript みたいに $class.prototype.dumper = function() {} ってプロトタイプベースで書く方法ってあるのかな?

ikasam_aikasam_a2007/04/19 00:44使ったこと無いですけど Object::Prototype がそのイメージですかね.

こまつこまつ2007/04/21 10:31おおお、さすがdanさん。情報ありがとうございます。

MitchellMitchell2011/08/02 20:30Hey, that post leaves me feleing foolish. Kudos to you!

owvohwowvohw2011/08/03 21:24SVUtqv , [url=http://fsxigcehrrwm.com/]fsxigcehrrwm[/url], [link=http://wmktfngjrayj.com/]wmktfngjrayj[/link], http://yznqduvxjsgt.com/

khpkitkhpkit2011/08/05 18:02I7JKa9 <a href="http://bzvelfjarswh.com/">bzvelfjarswh</a>

zpxzrirgnyrzpxzrirgnyr2011/08/05 22:48YSjouY , [url=http://dddepywpxmfh.com/]dddepywpxmfh[/url], [link=http://ryvrrkklhmmw.com/]ryvrrkklhmmw[/link], http://ochjlmiicvsp.com/

2007-04-07Catalyst::Plugin::Email::Japanese 0.07 このエントリーを含むブックマーク

Catalyst::Plugin::Email::Japanese を使っていると、経験上60%くらいの高確率テンプレートファイルが見つからなくて Exception を吐いてしまう現象に困っていた。エラーページをよく見ると Catalyst::Plugin::Email::Japanese の config で INCLUDE_PATH が 空になっている。

モジュール不具合なんじゃないかなと思いつつ追えていなかったけど、fix されました。

Catalyst::Plugin::Email::Japanese - Changes

http://search.cpan.org/src/TYPESTER/Catalyst-Plugin-Email-Japanese-0.07/Changes

Revision history for Perl module Catalyst::Plugin::Email::Japanese

0.07 2007-04-07T17:51:34+09:00
     - fixed a bug deleting original INCLUDE_PATH config

というわけで快調。