keisyuのブログ

最近めっきりエンジニアリングしなくなった厄年のおじさんがIT技術をリハビリするブログ。

Amon2を使ったアプリ開発

前回は amon2 を触ってみましたが ( Amon2 (PSGI フレームワーク) - keisyuのブログ ) これで少しアプリっぽいものを作って勉強してみようと思います。作るものは掲示板みたいなものを作って見ようと思います。

まずは amon2-setup.pl で flavor はBasicでスタートすることにします。

$ amon2-setup.pl --flavor=Basic SimpleBBS 
$ carton install

今回はplackupを使って起動して開発することにします。-R オプションに監視ディレクトリを指定して起動するとコードの変更時にサーバを停止することなく自動でリロードされるようで便利のようです。

$ carton exec -- plackup -R ./lib  -Ilib script/simplebbs-server

さて作るもの(方針)はこんな感じにします

  • 超簡易な掲示板
  • DBはMySQLを使う
  • flavor:BasicでバンドルされているORM(Teng)は使わずにdbhで操作する
  • Templeteのsyntaxは TT2 compatible が慣れているのでそれを使う( ひな形のは捨てる )。
  • その他開発に便利そうなやつは勉強がてら触ってみる。

DB(TABLE)の定義

投稿が保存される単一テーブルにします sql/mysql.sql を以下にします

CREATE TABLE IF NOT EXISTS entries (
    id      INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    body    VARCHAR(255),
    created_at  TIMESTAMP
) CHARSET=utf8;

MySQLにDatabaseを定義してテーブルを作成します

$ sudo mysqladmin create SimpleBBS
$ cat sql/mysql.sql | mysql -u root SimpleBBS 

dbh (MySQL) を使う準備

cpanfile に以下を追記します

requires 'Amon2::Plugin::DBI';
requires 'DBD::mysql';

そしてインストールします

$ carton install

config/development.pl はデフォルトでsqliteに接続するひな形になっているのでこれを以下に変更します

+{
    'DBI' => [
        "dbi:mysql:dbname=SimpleBBS", 'root', '',
        +{
            mysql_enable_utf8 => 1,
        }
    ],
};

開発

とりあえず、テンプレートを触るのはもうちょっと後にして、DBに直接エントリーを数個入れてそれがちゃんと取り出せるか確認してみます。

mysql> insert into entries (body) values ('Hello!'),('Amon2'),('World!!');          
mysql> select * from entries;
+----+---------+---------------------+
| id | body    | created_at          |
+----+---------+---------------------+
|  1 | Hello!  | 2014-02-16 18:25:58 |
|  2 | Amon2   | 2014-02-16 18:25:58 |
|  3 | World!! | 2014-02-16 18:25:58 |
+----+---------+---------------------+

lib/SimpleBBS/Web.pm の中にこのアプリで利用するプラグインをロードする箇所があるのでそこにDBIを追加します

__PACKAGE__->load_plugins(
...
    'DBI',
);

lib/SimpleBBS/Web/Dispatcher.pm に各URLのエントリーポイント毎の処理を記述する箇所がありますので '/' の部分で先ほどの entries テーブルをSELECTして標準出力に表示してみることにします。

...
use Data::Dumper;
...
any '/' => sub {
    my ($c) = @_;
    my $entries = $c->dbh->selectall_arrayref(q[
            SELECT * FROM entries ORDER BY id DESC
        ], { Slice => {} });
    print Dumper $entries;
    ...

これでWebページにブラウザでアクセスするとサーバが起動しているシェルに

$VAR1 = [
          {
            'created_at' => '2014-02-16 18:25:58',
            'body' => 'World!!',
            'id' => '3'
          },
          {
            'created_at' => '2014-02-16 18:25:58',
            'body' => 'Amon2',
            'id' => '2'
          },
          {
            'created_at' => '2014-02-16 18:25:58',
            'body' => 'Hello!',
            'id' => '1'
          }
        ];

と表示されました。ちゃんとDBI経由でMySQLからデータが取得できているようです。ちょっとMVCっぽく各entryの要素をModel化します。
lib/SimpleBBS/Model/Entry.pm を以下で定義します。

package SimpleBBS::Model::Entry;

use Class::Accessor::Lite (
    new => 1,
    ro => [ qw(
        id body created_at
    )], 
);

1;

これをつかて先ほどのDispacher.pmを若干修正します

...
use SimpleBBS::Model::Entry;

any '/' => sub {
    my ($c) = @_;
    my @entries = map {
        SimpleBBS::Model::Entry->new($_)
    } @{$c->dbh->selectall_arrayref(q[
            SELECT * FROM entries ORDER BY id DESC
        ], { Slice => {} })};
    print Dumper @entries;
    ....

これで出力されるログは

$VAR1 = bless( {
                 'created_at' => '2014-02-16 18:25:58',
                 'body' => 'World!!',
                 'id' => '3'
               }, 'SimpleBBS::Model::Entry' );
$VAR2 = bless( {
                 'body' => 'Amon2',
                 'created_at' => '2014-02-16 18:25:58',
                 'id' => '2'
               }, 'SimpleBBS::Model::Entry' );
$VAR3 = bless( {
                 'body' => 'Hello!',
                 'created_at' => '2014-02-16 18:25:58',
                 'id' => '1'
               }, 'SimpleBBS::Model::Entry' );

となりました。今回のサンプルではここまでModel化する必要は全く無いですがね...