Mojolicious wikiにあるORLiteの例をUnicode対応にする

Mojolicious::LiteでData::Modelを使ってみたという記事で、

MojoliciousのWikiにORLiteを使ったサンプルがあったのですが、残念ながらそのままでは日本語には対応していないので、…

という文を見かけたので、その Working with ORLite inside Mojolicious にあるサンプルを日本語でも使えるようにできないか、調べてみました。

Mojolicious::Liteにおける文字エンコーディング周りの動作を調べてみたところ、Mojolicious::Liteの内部では、文字列はあくまでPerlの内部文字列として扱われることを知りました。具体的に言うと:

なので、 $self->param('type') のようにして取得したデータは、その時点で内部文字列となっているし、また、データベースから取得したデータをテンプレートに埋め込むならば、内部文字列とした上で埋め込む必要がある、ということになります。

そうすると修正はデータベース寄りということになりそうです。調べると、ORLiteが使用するDBD::SQLiteには、入出力データを内部文字列として扱うための属性(sqlite_unicode)がありました。しかしORLiteではその属性を使用していないようです。

そこで、以下のように package Model のところで connect メソッドを上書きすることで、日本語文字列のポスト・表示にも対応することができました。

追記(2012年2月3日): ORLite がバージョン1.52以降で unicode オプションをサポートしたため、以下のソースも変更しました。

#!/usr/bin/env perl

package Model;
use strict;
use ORLite {
    file   => 'sample.db',
    create => sub {
        my $dbh = shift;
        $dbh->do(
            'CREATE TABLE motorcycles (
             id INTEGER PRIMARY KEY,
             type TEXT NOT NULL, 
             brand TEXT NOT NULL,
             color TEXT)'
        );

        # just use $dbh->do(...) if you need more
        # tables
        return 1;
    },
    # ORLite >= 1.52
    unicode => 1,
};

#{
#    # use unicode character (ORLite < 1.52)
#    my $connect = sub {
#        DBI->connect( $_[0]->dsn, undef, undef, {
#            PrintError => 0,
#            RaiseError => 1,
#            sqlite_unicode => 1,
#        } );
#    };
#    no warnings 'redefine';
#    *connect = $connect;
#}

package main;

use Mojolicious::Lite;
use utf8;

get '/' => sub {
    my $self = shift;
    $self->stash(motorbikes => [Model::Motorcycles->select('order by type')]);
} => 'index';

post '/' => sub {
    my $self = shift;
    Model::Motorcycles->create(
        type  => $self->param('type'),
        brand => $self->param('brand'),
        color => $self->param('color')
    );
    $self->redirect_to('/');
};

app->start;

__DATA__

@@ index.html.ep
<!DOCTYPE html>
<html>
  <head>
    <title>Motorcycles</title>
    <style>td { background-color:#dee; } </style>
  </head>
  <body>
    <h1>Motorcycles</h1>
    <table>
    % foreach my $cycle (@{$motorbikes} ) {
      <tr>
        <td><%= $cycle->type %></td>
        <td><%= $cycle->brand %></td>
        <td><%= $cycle->color %></td>
      </tr>
    % }
    </table>
     
    <p>Enter a new motorcycle here (バイクを登録して下さい)</p>
    <p>
      <%= form_for '/' => (method => 'post') => begin %>
% foreach (qw/type brand color/) {
        <%= uc($_) %>: <%= input_tag $_, 'type' => 'text' %><br />
% }
        <%= submit_button 'Submit motorcycle' %>
      <% end %>
    </p>
  </body>
</html>

スクリプト内部(__DATA__ 以下)のテンプレートで日本語文字列を使うような場合は、内部文字列として扱われるように use utf8; を追加しておくとよさそうです。