あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

MT アプリケーションを Sinatra like に記述できる MT::App::Lite 作った

Movable Type には、MT::App という Web アプリケーションを記述するための基底クラスがありますが、結構慣れが必要なのと癖があることもあって、もっと手軽に Sinatra like にアプリケーションを記述できる、フレームワーク的なものが欲しいなーと常々思っていました。

とりあえずいろいろやりたいことは残っていますが、動くところまでできたので、紹介します。

現状では、PSGI でのみ動作します。

名前

概要

以下のような感じでMTアプリケーションを記述します。

package MyLiteApp;
use strict;

use MT::App::Lite;

setup Renderer => 'Xslate';

get '/' => sub {
  my $app = shift;
  $app->render('index.tt', {
    blog => MT->model('blog')->load(1),
    entries => [MT->model('entry')->load({blog_id => 1})],
  });
};

get '/entry/:id' => sub {
  my $app = shift;
  $app->render('entry.tt', {
    blog => MT->model('blog')->load(1),
    entry => [MT->model('entry')->load($app->param('id'))],
  });
};

1;

__DATA__

@@ index.tt
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title><: $blog.name :></title>
</head>
<body>
  <ul>
  : for $entries -> $entry {
    <li><a href="<: $entry.permalink :>"><: $entry.title :></a></li>
  : }
  </ul>
</body>
</html>

@@ entry.tt
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title><: $entry.title :> | <: $blog.name :></title>
</head>
<body>
  <h1><: $entry.title :></h1>
  <div>
    <: $entry.text | mark_raw :>
  </div>
</body>
</html>

1ファイル完結型です。(正確にはプラグインの config.yaml が必要)
多分、フォームとか作るのに便利だと思います。

仕様

MT アプリケーションのハンドラ内で、Router::Simple::Sinatraish でエクスポートされる、get や post などの関数を使って、ルートを記述します。

そのファイル中の __END__ セクションに、テンプレートを書きます。
__END__ セクションに記述したテンプレートは、Data::Section::Simple によって、MT::App::Lite に渡されます。

テンプレートは、Text::Xslate を標準のテンプレートエンジンにしています。申し訳程度に MTML 用のテンプレートエンジンも用意しましたが、ここでわざわざ使う必要はない気がします。

MT::App::Lite::Renderer::Foo という形で、render メソッドと render_string メソッドを用意すれば、任意のテンプレートエンジンを利用することは可能です。

また、MT::App::Lite を use すると、自動的に MT::App を継承することになるので、MT::App っぽい書き方もできるのかもしれません。

基本的には、MT::App に Web アプリケーションを記述するための機能はひと通り揃っているので、便利っぽいモジュールを糊付けしたような実装です。

MT アプリケーションのプラグインを作成する

このモジュールを使った、MTアプリケーションのプラグインの作り方です。

最小限の config.yaml と、ハンドラを用意します。

config.yaml
name: MyLiteApp
id:   myliteapp

applications:
  lite_app:
    handler: MyLiteApp
    script: sub { 'app' }
    cgi_path: sub { '/' }
MyLiteApp (handler)
package MyLiteApp;
use strict;

use MT::App::Lite;

setup Renderer => 'Xslate';

get '/' => sub {
  my $app = shift;
  $app->render('index.tt', {
    blog => MT->model('blog')->load(1),
    entries => [MT->model('entry')->load({blog_id => 1})],
  });
};

get '/entry/:id' => sub {
  my $app = shift;
  $app->render('entry.tt', {
    blog => MT->model('blog')->load(1),
    entry => [MT->model('entry')->load($app->param('id'))],
  });
};

1;

__DATA__

@@ index.tt
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title><: $blog.name :></title>
</head>
<body>
  <ul>
  : for $entries -> $entry {
    <li><a href="<: $entry.permalink :>"><: $entry.title :></a></li>
  : }
  </ul>
</body>
</html>

@@ entry.tt
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title><: $entry.title :> | <: $blog.name :></title>
</head>
<body>
  <h1><: $entry.title :></h1>
  <div>
    <: $entry.text | mark_raw :>
  </div>
</body>
</html>

これで、http://yourdomain/app/ や、http://yourdomain/app/entry/:id にアクセスするとテンプレートの内容が描画されます。

まとめ

本当は、Movable Type Advent Calendar 2013 の自分担当日のネタとして作ろうと考えていたんですけど、実装する暇がなく、今日に至ります。

便利な気がするので、メンテしていこうと思います。まだいろいろと実装したいことや、実装すべきことがあります。

現時点ではちょっとした Viewer やフォームを作るのには便利な気がしています。