5分で始める dotcloud

xaicron
Yokohama.pm #7 (2011-05-13)

Agenda

dotcloud

これまでのあらまし

つまり

こういうことですね

miyagawa さんが使えるようになりました

dotcloud の始め方

$ easy_install dotcloud
$ dotcloud create nekokak
$ dotcloud deploy --type perl nekokak.www

とかやるといろいろあって、おもちゃ箱ができる。
かんたんですね。

dotcloud の使い方

詳しくはログイン後のチートシートを読んでね♡

dotcloud で NoPaste

とりあえず簡単なので NoPaste 的なアプリを動かしてみる

ソース

use strict;
use warnings;
use Plack::Request;
use Plack::Response;
use Text::Xslate;
use DBIx::Connector;
use Router::Simple;
use Encode qw(decode_utf8 encode_utf8);

my $router = Router::Simple->new;
$router->connect('/', {action => 'index'});
$router->connect('/create', {action => 'create'}, {method => 'POST'});
$router->connect('/{id:[1-9]\d*}', {action => 'content'});

my $conn = DBIx::Connector->new('dbi:SQLite:nopaste.db', '', '', {
    RaiseError     => 1,
    AutoCommit     => 1,
    PrintWarn      => 0,
    PrintError     => 0,
    sqlite_unicode => 1,
}) or die $DBI::errstr;

my $xslate = Text::Xslate->new(
    path   => ['tmpl'],
    syntax => 'TTerse',
);

sub {
    my ($found) = $conn->run(sub {
        shift->selectrow_array(
            'SELECT COUNT(tbl_name) FROM sqlite_master WHERE tbl_name = ?',
            undef,
            'paste',
        );
    });

    return if $found;

    $conn->txn(sub {
        my $dbh = shift;
        $dbh->do(<< 'SQL');
CREATE TABLE paste (
    id INTEGER PRIMARY KEY,
    title TEXT,
    content TEXT NOT NULL,
    created_on INTEGER
)
SQL
        $dbh->commit;
    });
}->();

sub render {
    my ($file, $args) = @_;
    my $html = encode_utf8 $xslate->render($file, $args);

    return [200, [
        'Content-Type'   => 'text/html; chatset=utf8',
        'Content-Length' => length($html),
    ], [$html]]; 
}

sub make_permalink {
    my ($req, $id) = @_;
    my $uri = $req->base;
    $uri->path($id);
    $uri->as_string;
}

sub {
    my $env = shift;
    my $req = Plack::Request->new($env);
    if (my $p = $router->match($env)) {
        if ($p->{action} eq 'index') {
            return render('index.xt');
        }
        elsif ($p->{action} eq 'content') {
            my $id = $p->{id};
            my $row = $conn->run(sub {
                my $dbh = shift;
                $dbh->selectrow_hashref(
                    'SELECT * FROM paste WHERE id = ?',
                    undef,
                    $id,
                );
            });

            return [404, [], ['Not Found']] unless $row;
            return render('content.xt', {
                %$row,
                link => make_permalink($req, $id),
            });
        }
        elsif ($p->{action} eq 'create') {
            my $title = decode_utf8 $req->param('title');
            my $content = decode_utf8 $req->param('content');

            unless (length $content) {
                return [400, [], ['Bad Request']];
            }

            my ($id) = $conn->txn(sub {
                my $dbh = shift;
                $dbh->do(
                    'INSERT INTO paste(title, content, created_on) VALUES(?, ?, ?)',
                    undef,
                    ($title, $content, time),
                );
                $dbh->commit;
                $dbh->sqlite_last_insert_rowid;
            });

            return render('content.xt', {
                title   => $title,
                content => $content,
                id      => $id,
                link    => make_permalink($req, $id),
            });
        }
    }
    else {
        return [404, [], ['Not Found']];
    }
};

簡単ですね。

ディレクトリ構成

.
├── Changes
├── MANIFEST.SKIP
├── META.yml
├── Makefile
├── Makefile.PL
├── README
├── app.psgi
├── inc
│   └── Module
│       ├── Install
│       │   ├── AuthorTests.pm
│       │   ├── Base.pm
│       │   ├── Can.pm
│       │   ├── Fetch.pm
│       │   ├── Makefile.pm
│       │   ├── Metadata.pm
│       │   ├── Repository.pm
│       │   ├── Win32.pm
│       │   └── WriteAll.pm
│       └── Install.pm
├── lib
│   └── nopaste.pm
├── t
│   └── 00_compile.t
├── tmpl
│   ├── content.xt
│   └── index.xt
└── xt
    ├── 01_podspell.t
    ├── 02_pod.t
    ├── 03_pod-coverage.t
    ├── 04_perlcritic.t
    ├── 05_script-shebang.t
    └── perlcriticrc

git で管理しましょう

さっきのような構成を *全部* git に commit

$ dotcloud push xaicron.paste {path}

とかやると魔法が起きてアプリがうごく!!!

http://paste.xaicron.dotcloud.com/

依存モジュールの解決

依存モジュールは Makefile.PL に書いとくとよしなにしてくれる。超らくちん。

use inc::Module::Install;
name 'nopaste';
all_from 'lib/nopaste.pm';

requires 'Plack';
requires 'DBD::SQLite';
requires 'DBIx::Connector';
requires 'Text::Xslate';
requires 'Encode';
requires 'Router::Simple';

test_requires 'Test::More', 0.98;

tests join q{ }, map { sprintf 't%s.t', '/*' x $_ } 1..3;
author_tests 'xt';

WriteAll;

依存モジュールの解決

注意点としては Module::Install 使ってる場合は inc も commit すること。

$ git add -f inc
$ git commit -m 'added inc'

忘れるとよよよ。

dotcloud いいですね

とっても簡単に PSGI アプリを動かせて便利ですね!

みんなも使いましょう!

おしまい

ご清聴ありがとうございました。