第8章 Mojolicious ベースにリファクタリングをする(その3)

前回はテンプレートの切り離しまですすみました。 テンプレートは切り離す仕組みが最初から Mojo にありましたので簡単でしたが コントローラーについてはすこし考えなければいけないところもあります。 コントローラーを作る前にアプリケーションが最初に読み込みを行なっているファイルが bulletin.pl であり、今まではその中に全てを書いていたのですが bulletin.pl を読み込むだけのファイルとして変更しなくてはいけないことと Perl の場合は cpan 形式という言葉があって特別な理由がない場合は cpan 形式で 作っておくと間違いはないです。 最終的には bulletin.pl は script/beginning_mojo となります。 順番にみてゆきましょう。

8.3 コントローラー

アプリケーション内容の記述部分のためのファイル作成からはじめてみます。

8.3.1 アプリケーションファイル

% touch lib/BeginningMojo.pm

lib/BeginningMojo.pm

アプリケーション内容の記述部分を再定義

package BeginningMojo;
use Mojo::Base 'Mojolicious';
use Mojo::Util qw(trim);
use BeginningMojo::Model;

sub startup {
    my $self = shift;

    $self->helper( model => sub { BeginningMojo::Model->new(); } );
    $self->helper( teng  => sub { BeginningMojo::Model->new()->teng; } );

    my $r = $self->routes;

    $r->get(
        '/' => sub {
            my $c = shift;
            $c->render( template => 'index' );
        }
    );

    $r->get(
        '/list' => sub {
            my $c           = shift;
            my $to_template = $c->model->bulletin->to_template_list;
            $c->stash($to_template);
            $c->render( template => 'list' );
        }
    );

    $r->get(
        '/create' => sub {
            my $c = shift;
            $c->render( template => 'create' );
        }
    );

    $r->post(
        '/store' => sub {
            my $c      = shift;
            my $params = $c->req->params->to_hash;

            # 値が空の場合は登録しない
            my $comment = $params->{comment};
            my $trimmed = '';
            if ($comment) {
                $trimmed = trim($comment);
            }
            if ( !$trimmed ) {
                $c->flash( msg => "正しく値を入力してください" );
                $c->redirect_to('/create');
                return;
            }

            my $model = $c->model->bulletin->req_params($params);
            $model->store;
            $c->redirect_to('/list');
        }
    );

    $r->post(
        '/remove' => sub {
            my $c      = shift;
            my $params = $c->req->params->to_hash;
            my $model  = $c->model->bulletin->req_params($params);
            my $msg    = '';
            if ( my $remove = $model->remove ) {
                $msg = $remove->{msg};
            }
            $c->flash( msg => $msg );
            $c->redirect_to('/list');
        }
    );
}

1;

bulletin.pl

今までの実行ファイルは実行コマンドのみになった

#!/usr/bin/env perl
use strict;
use warnings;
use lib './lib';
use Mojolicious::Commands;
Mojolicious::Commands->start_app('BeginningMojo');

アプリの起動方法が変わったためテストコードも変更

t/bulletin.t

# ...
# web アプリを実行からテスト用DB準備まで
my $t = Test::Mojo->with_roles('+Basic')->new('BeginningMojo');
# ...

t/remove.t

# ...
# web アプリを実行からテスト用DB準備まで
my $t = Test::Mojo->with_roles('+Basic')->new('BeginningMojo');
# ...

テストコードを実行確認

% docker-compose exec web carton exec -- prove

8.3.2 コントローラーファイル

コントローラーを新設する

% mkdir lib/BeginningMojo/Controller
% touch lib/BeginningMojo/Controller/Bulletin.pm

lib/BeginningMojo/Controller/Bulletin.pm

コントローラー内容の記述部分を再定義

package BeginningMojo::Controller::Bulletin;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::Util qw(trim);

sub index {
    my $self = shift;
    $self->render( template => 'index' );
}

sub list {
    my $self        = shift;
    my $to_template = $self->model->bulletin->to_template_list;
    $self->stash($to_template);
    $self->render( template => 'list' );
}

sub create {
    my $self = shift;
    $self->render( template => 'create' );
}

sub store {
    my $self   = shift;
    my $params = $self->req->params->to_hash;

    # 値が空の場合は登録しない
    my $comment = $params->{comment};
    my $trimmed = '';
    if ($comment) {
        $trimmed = trim($comment);
    }
    if ( !$trimmed ) {
        $self->flash( msg => "正しく値を入力してください" );
        $self->redirect_to('/create');
        return;
    }

    my $model = $self->model->bulletin->req_params($params);
    $model->store;
    $self->redirect_to('/list');
}

sub remove {
    my $self   = shift;
    my $params = $self->req->params->to_hash;
    my $model  = $self->model->bulletin->req_params($params);
    my $msg    = '';
    if ( my $remove = $model->remove ) {
        $msg = $remove->{msg};
    }
    $self->flash( msg => $msg );
    $self->redirect_to('/list');
}

1;

lib/BeginningMojo.pm

アプリケーション内容はルーティングのみに

package BeginningMojo;
use Mojo::Base 'Mojolicious';
use BeginningMojo::Model;

sub startup {
    my $self = shift;

    $self->helper( model => sub { Bulletin::Model->new(); } );
    $self->helper( teng  => sub { Bulletin::Model->new()->teng; } );

    my $r = $self->routes;
    $r->get('/')->to('bulletin#index');
    $r->get('/list')->to('bulletin#list');
    $r->get('/create')->to('bulletin#create');
    $r->post('/store')->to('bulletin#store');
    $r->post('/remove')->to('bulletin#remove');
}

1;

テストコードを実行確認

% docker-compose exec web carton exec -- prove

8.3.3 実行ファイル

実行スクリプトファイルを再定義する cpan 形式へ

% mkdir script
% mv bulletin.pl script/beginning_mojo

lib/Test/Mojo/Role/Basic.pm

# ... アプリの読み込みが簡略化された

sub init {
    my $self = shift;

    # テストの時は強制的にモードを変更
    $ENV{MOJO_MODE} = 'testing';
    $self->init_db;
    return;
}
# ...

アプリの実行コマンドが変更されたことに注意

% carton exec -- morbo script/beginning_mojo

docker で起動しているファイルも書き換えておきたい

bin/compose-cmd.bash

#!/usr/bin/env bash
carton install && \
if [ -f ./db/bulletin.sql ] && [ -f ./db/bulletin.db ]; then
    carton exec -- morbo script/beginning_mojo
elif [ -s ./db/bulletin.sql ]; then
    sqlite3 ./db/bulletin.db < ./db/bulletin.sql
    carton exec -- morbo script/beginning_mojo
else
    echo "not exist bulletin.sql !!"
fi

テストコードを実行確認

% docker-compose exec web carton exec -- prove

8.3.4 まとめ

8.3.5 次回

今回はコントローラー部分の切り離しをしました。

次回は切り離しを行なった最後の全体的な調整をやってみたいと思います。