Flex⇔Perl連携 その2

前回の続き

仕様

簡単すぎるサンプルだとつまらないので、
そこそこ実践的(?)な仕様にしてみました。

  • JSONXML/AMF0のフォーマットを選択して情報を表示できるようなもの。
  • データベースからレコードを取得して、データグリッドに反映。
  • 簡単な絞り込みも実装。

クライアントサイド

http://github.com/toritori0318/Flex_Perl/tree/a61078e8ccd1d6bfe21326b2f37ae1d269a3043b/FlexToPerl_HTTPENGINE/client/src

○FlexToPerl.mxml

そんなに難しい事はしてません。
JSONXMLHTTPServiceで、AMF0はNetConnectionでデータを取ります。
AMF0のソースは、こちらのクライアントコードを参考にしてます。*1

○JSONClient.as/XMLClient.as/AmfClient.as

フローは基本一緒です。

  1. 指定したURLにリクエスト(検索条件のパラメータを渡す)
  2. 取得した結果をデータグリッドにバインド


AMFClientだけは、テスト的に「echo」「sum」「list」の3つのサーバメソッドを呼び出しています。
データグリッドに入力されるのは「listResult」メソッドです。

private function listResult(result:*):void {
    // Empテーブルのハッシュ
    var _result:Array = result as Array;
    var _dp:ArrayCollection = new ArrayCollection;
    for (var i:int =0; i<_result.length;i++){
        var _item:Object = _result[i];
        _dp.addItem(_item);
    }
    view.dg.dataProvider = _dp;
}

ここでは、サーバからリストのハッシュとして渡されてきたデータをArrayCollectionに変換し、
データグリッドにバインドしています。
ちなみに、渡ってくるデータ(resultに入ってくるデータ)はこんな感じです。

$VAR1 = {
          'sal' => '800',
          'emp_no' => '7369',
          'dept_id' => '2',
          'mgr_id' => '13',
          'hiredate' => '1980-12-17',
          'emp_name' => 'SMITH',
          'id' => '1',
          'version_no' => '1'
        };
$VAR2 = {
          'sal' => '1250',
          'emp_no' => '7654',
          'dept_id' => '3',
          'mgr_id' => '6',
          'hiredate' => '1981-09-28',
          'emp_name' => 'MARTIN',
          'id' => '5',
          'version_no' => '1'
        };
JSONの注意点

JSONをデコードするのにライブラリが必要です。
http://code.google.com/p/as3corelib/
こちらからダウンロードしてプロジェクトに配置して下さい。



サーバサイド

○テーブル作成

まず、最初にテーブルを作ってしまいましょう。
テーブルは以前S2Flex2で利用したサンプルSQLをそのまま使います。

○DBアクセス

次に、DBアクセスモジュールを作ります。
手軽にやりたいのでDBIx::Class::Schema::Loaderを使う事にします。
Postgres使ってます。

$ module-starter --module=AmfTestDBIx
$ cd AmfTestDBIx
$ perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:./lib -e 'make_schema_at("AmfTestDBIx::Schema", { constraint => qr/emp/, debug => 1 },[ "dbi:Pg:dbname=test","postgres" ])'

これだけでEMPテーブルにアクセスするモジュールを作成してくれます。*2


AmfTestDBIx/lib ディレクトリ構成はこんな感じ。

lib
|-- AmfTestDBIx
|   |-- Schema
|   |   `-- Emp.pm
|   `-- Schema.pm
`-- AmfTestDBIx.pm


次にコネクション情報を AmfTestDBIx/Schema.pm に記述します。

__PACKAGE__->connection(
    'dbi:Pg:dbname=test;host=localhost',
    'postgres',
    '',
    { AutoCommit => 0 } );


ResultSetをハッシュで扱いたいので、AmfTestDBIx/Schema/Emp.pm にプラグイン(AsFdat)を追加します。

__PACKAGE__->load_components(
          "AsFdat",
          "PK::Auto",
          "Core",
);


DBアクセスはこんな感じでOK。

○サーバスクリプト

次に簡易サーバですが、前回の simple_server_mod.pl を元にして書いてみました。
amf_dbix_all_server.pl

#!/usr/bin/perl
 
use strict;
use warnings;
 
use HTTP::Engine;
use Data::AMF::Packet;
 
use List::Util ();
use FindBin::libs;
use Path::Class;
use URI::Escape;
use Data::Dumper;
 
 
use lib '/home/toritori0318/AmfTestDBIx/lib';
use AmfTestDBIx::Schema;
use DBIx::Class::AsFdat;
 
sub getEmp {
    my $prm = shift;
    $prm = +{ map { ( $_, '%' . $prm->{$_} . '%' ) } keys %$prm };
    my $schema = AmfTestDBIx::Schema->connect;
    my @rs = map { $_->as_fdat } $schema->resultset('Emp')->search({ 'emp_name' => { like => $$prm{emp_name} }});
    return \@rs;
}
 
&main;
exit;
 
sub main {
    my $engine = HTTP::Engine->new(
        interface => {
            module => 'ServerSimple',
            args => {
                host => 'localhost',
                port => '3000',
            },
            request_handler => \&handler,
        },
    );
    $engine->run;
}
 
sub handler {
    my $req = shift;
    my $qq = cnv_hash( $req->uri->query )
      if $req->uri->query;
 
      if ( $req->uri->path eq '/gateway' ) {
        my $fh = $req->body;
        my $body = do { local $/; <$fh> };
 
        my $request = Data::AMF::Packet->deserialize($body);
 
        my @result;
        for my $message ( @{ $request->messages } ) {
            my $method = __PACKAGE__->can( $message->target_uri );
 
            if ($method) {
                my $result = $method->( $message->value );
 
                push @result, $message->result($result);
            }
        }
 
        my $response = Data::AMF::Packet->new(
            version => $request->version,
            headers => [],
            messages => \@result,
        );
 
        return res( 'application/x-amf', $response->serialize );
    }
    elsif ( $req->uri->path eq '/json' ) {
        use JSON;
        return res( 'application/xhtml+xml', to_json( &getEmp( $qq ) ) );
    }
    elsif ( $req->uri->path eq '/xml' ) {
        use XML::Simple;
        my $xml = new XML::Simple;
        my $body = '<?xml version="1.0" encoding="utf-8" ?>\n';
        my $emp = &getEmp($qq);

        if(@$emp > 0){
            my $emp_rec = {record => $emp};
            $body .= XMLout( $emp_rec, RootName=>'Result', NoAttr => 1 );
        }else{
            $body .= "<Result><record></record></Result>";
        }
        return res( 'application/xhtml+xml', $body );
    }
    else {
        return res( 'application/x-shockwave-flash',
            scalar file('./examples/simple_flash_remoting.swf')->slurp );
    }
}
 
sub res {
    my ( $content, $body ) = @_;
    HTTP::Engine::Response->new(
        content_type => $content,
        body => $body
    );
}
 
sub echo {
    return $_[0];
}
 
sub sum {
    return List::Util::sum( @{ $_[0] } );
}
 
sub dump {
    use YAML;
    warn Dump $_[0];
}
 
sub list {
    my $prm = shift;
    my $hash = $$prm[0];
 
    #my %hash = %{ $$prm[0] };
    return getEmp($hash);
}
 
sub cnv_hash {
    my $qs = shift;
    my %ret = ();
    my @pairs = split( /&/, $qs );
    foreach my $pair (@pairs) {
        my ( $name, $value ) = split( /=/, $pair );
        $ret{uri_unescape($name)} = uri_unescape($value);
    }
    return \%ret;
}
1;


まず handler ですが、JSONXMLの場合はリクエストURLでフォーマットを判断し、レスポンスを返します。
AMFの場合はクライアントで指定されたメソッドを実行し、その結果をAMFフォーマットにしてレスポンスを返しています。
レスポンスデータについては、先ほど作成したDBアクセスモジュールを呼び出し、
その結果を各々のフォーマットに変換しています。

動かす

まず、前回と同じようにサーバ側でamf_dbix_all_server.plを実行します。

]$ perl amf_dbix_all_server.pl
HTTP::Engine::Interface::ServerSimple : You can connect to your server at http://localhost:3000/


あとはFlexから検索実行するとこんな感じできちんとデータ取れてます!


いい感じ。


今回使用したスクリプトgithubに置きました。*3
http://github.com/toritori0318/Flex_Perl/tree/master


今回触った感じだと、JSONがかなりとっつきやすい。
ちょっとしたもの*4はこれでやるのが吉っぽいなぁ。
AMF0は他の方の記事を見るとそんなに利点なさそうなので
JSONでいいんじゃね?と思ってしまった。


余裕があったらベンチマークも出してみたいです。


次回ですが、今回のFlexアプリケーションをCatalystと連携させてみたいと思います。
(その前に番外編があるかも)

*1:というかほぼそのまま

*2:DBIxワンライナーについてはこちら

*3:変なコードがあったら遠慮なくdisって下さい><

*4:ちょっとしてなくても!?