昼休みの時間にCatalystでMixiを作ってみよう

昼休みにMixiみたいなSNSCatalystで作る

動作環境
perl 5.8.8
MySQL
Catalyst-Runtime 5.7001 + Catalyst-Devel 1.0
Template-Toolkit + DBIx-Class
CPANモジュールたくさn。
mixiっぽいって事でlixiと命名・・

# catalyst.pl lixi
created "lixi"
created "lixi/script"
created "lixi/lib"
created "lixi/root"
created "lixi/root/static"
created "lixi/root/static/images"
created "lixi/t"
created "lixi/lib/lixi"
created "lixi/lib/lixi/Model"
created "lixi/lib/lixi/View"
created "lixi/lib/lixi/Controller"
created "lixi/lixi.yml"
created "lixi/lib/lixi.pm"
created "lixi/lib/lixi/Controller/Root.pm"
created "lixi/README"
created "lixi/Changes"
created "lixi/t/01app.t"
created "lixi/t/02pod.t"
created "lixi/t/03podcoverage.t"
created "lixi/root/static/images/catalyst_logo.png"
created "lixi/root/static/images/btn_120x50_built.png"
created "lixi/root/static/images/btn_120x50_built_shadow.png"
created "lixi/root/static/images/btn_120x50_powered.png"
created "lixi/root/static/images/btn_120x50_powered_shadow.png"
created "lixi/root/static/images/btn_88x31_built.png"
created "lixi/root/static/images/btn_88x31_built_shadow.png"
created "lixi/root/static/images/btn_88x31_powered.png"
created "lixi/root/static/images/btn_88x31_powered_shadow.png"
created "lixi/root/favicon.ico"
created "lixi/Makefile.PL"
created "lixi/script/lixi_cgi.pl"
created "lixi/script/lixi_fastcgi.pl"
created "lixi/script/lixi_server.pl"
created "lixi/script/lixi_test.pl"
created "lixi/script/lixi_create.pl"

(おもったけどデフォルトページ用のstatic関係いらんのだけど、書き出さないオプションあるんでしょうか)

#cd lixi
viewの作成
# script/lixi_create.pl view TT TT
 exists "/home/test/public_html/lixi/script/../lib/lixi/View"
 exists "/home/test/public_html/lixi/script/../t"
created "/home/test/public_html/lixi/script/../lib/lixi/View/TT.pm"
created "/home/test/public_html/lixi/script/../t/view_TT.t"

lixi_dbにはuserdataとfriendlistの二つのテーブルがある。

userdata
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| uid          | int(11)      |      | PRI | NULL    | auto_increment |
| account      | varchar(32)  |      |     |         |                |
| password     | varchar(255) |      |     |         |                |
| display_name | varchar(255) |      |     |         |                |
| profile      | text         |      |     |         |                |
| email        | varchar(255) |      |     |         |                |
+--------------+--------------+------+-----+---------+----------------+

friendlist
+-------+---------+------+-----+---------+----------------+
| Field | Type    | Null | Key | Default | Extra          |
+-------+---------+------+-----+---------+----------------+
| id    | int(11) |      | PRI | NULL    | auto_increment |
| uid   | int(11) |      |     | 0       |                |
| fid   | int(11) |      |     | 0       |                |
+-------+---------+------+-----+---------+----------------+

こんなかんじ。

ヘルパーでModel作成
# script/lixi_create.pl model DBIC DBIC DBI:mysql:lixi_db lixi password
 exists "/home/test/public_html/lixi/script/../lib/lixi/Model"
 exists "/home/test/public_html/lixi/script/../t"
created "/home/test/public_html/lixi/script/../lib/lixi/Model/DBIC.pm"
created "/home/test/public_html/lixi/script/../lib/lixi/Model/DBIC"
created "/home/test/public_html/lixi/script/../lib/lixi/Model/DBIC/Friendlist.pm"
created "/home/test/public_html/lixi/script/../lib/lixi/Model/DBIC/Userdata.pm"
 exists "/home/test/public_html/lixi/script/../t"
created "/home/test/public_html/lixi/script/../t/model_DBIC-Friendlist.t"
 exists "/home/test/public_html/lixi/script/../t"
created "/home/test/public_html/lixi/script/../t/model_DBIC-Userdata.t"

これでModelのスクリプト作成
指定したDBの中身を読み込んでくれる賢いやつ。

テストサーバー起動
# script/lixi_server.pl
[debug] Debug messages enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
| Catalyst::Plugin::ConfigLoader  0.06                                       |
| Catalyst::Plugin::Static::Simple  0.14                                     |
'----------------------------------------------------------------------------'

[debug] Loaded dispatcher "Catalyst::Dispatcher"
[debug] Loaded engine "Catalyst::Engine::HTTP"
[debug] Found home "/home/test/public_html/lixi"
[debug] Loaded tables "friendlist userdata"
[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class                                                           | Type     |
+-----------------------------------------------------------------+----------+
| lixi::Controller::Root                                          | instance |
| lixi::Model::DBIC                                               | instance |
| lixi::Model::DBIC::Friendlist                                   | class    |
| lixi::Model::DBIC::Userdata                                     | class    |
| lixi::Model::DBIC::_db                                          | class    |
| lixi::View::TT                                                  | instance |
'-----------------------------------------------------------------+----------'

[debug] Loaded Private actions:
.----------------------+--------------------------------------+--------------.
| Private              | Class                                | Method       |
+----------------------+--------------------------------------+--------------+
| /default             | lixi::Controller::Root               | default      |
'----------------------+--------------------------------------+--------------'

[info] lixi powered by Catalyst 5.7001
You can connect to your server at http://localhost:3000

こんな感じでテストサーバーを動かすとデバッグを表示してくれます。

  • rをつけて起動するとソースをいじる度にサーバーを再起動してくれます。

それではhttp://localhost:3000/にアクセスしてみよう。(URLは自分の環境に変えるべし)

アクセスするとCatalystデフォルトページがひょうじされる。

このページはlixi/lib/lixi/Controller/Root.pmにある

sub default : Private {
    my ( $self, $c ) = @_;

    # Hello World
    $c->response->body( $c->welcome_message );
}

が表示してくれていますが、昼休みがおわっちゃうので
さっさとlixi用に変えてみます。

lixi.pm
package lixi;

use strict;
use warnings;

use Catalyst qw/-Debug
    ConfigLoader
    Static::Simple
    Charsets::Japanese/;

our $VERSION = '0.01';

__PACKAGE__->config(
                    name => 'lixi',
                    charsets =>  'Shift_JIS',
                    );
__PACKAGE__->setup;


1;

use Catalyst qw/-Debug ConfigLoader Static::Simple Charsets::Japanese/;
ここには使うCatlyst::Pluginのモジュールを
Carsets::JapanesでConfigにかいてあるShift_JISに変換してくれる。


とりあえずやっぱりログインするためのページが必要かな。
Mixiみたいなログインページをトップページに表示してみる。

トップページや、他のコントローラー以外のURLだった場合の表示が
Root.pmにある default : Privateだ。
Root.pmにおかないでlixi.pmにおいても動作するけど、Root.pmにかいておこう。

lixi/lib/lixi/Controller/Root.pm
package lixi::Controller::Root;

use strict;
use warnings;
use base 'Catalyst::Controller';

__PACKAGE__->config->{namespace} = '';

# デフォルトの処理
sub default : Private {
    my ( $self, $c ) = @_;

    # テンプレート設定
    $c->stash->{template} = 'login.tt';

}

# デフォルトの最後
sub end : Private { 
    my ( $self, $c ) = @_;
    # Viewに処理をなげる
    $c->forward('lixi::View::TT');
}

1;

login.ttはlixi/root/においてあって、特に指定しない場合にはlixi/rootからファイルを探す。

login.tt
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="ja">

	<head>
		<meta http-equiv="content-type" content="text/html;charset=shift_jis">
		<title>welcom lixi!</title>
	</head>

	<body bgcolor="#ffffff">
                [% error_msg %]
		<form id="FormName" action="/login" method="post" name="FormName">
			あかうんとめい <input type="text" name="account" size="24">
			<p>ぱすわーど <input type="password" name="password" size="24"></p>
			<p><input type="submit" name="" value="login"></p>
		</form>
		<p></p>
	</body>

</html>

これでログインページを表示するようになった。
ログインチェックは/loginとする。
Root.pmに login : Globalとかいてもいいんだけど、分けた方があとから見通しがいいので、ヘルパーにつくってもらう。 ついでにlogoutもつくっておく。

# script/lixi_create.pl controller login
 exists "/home/test/public_html/lixi/script/../lib/lixi/Controller"
 exists "/home/test/public_html/lixi/script/../t"
created "/home/test/public_html/lixi/script/../lib/lixi/Controller/login.pm"
created "/home/test/public_html/lixi/script/../t/controller_login.t"
# script/lixi_create.pl controller logout
 exists "/home/test/public_html/lixi/script/../lib/lixi/Controller"
 exists "/home/test/public_html/lixi/script/../t"
created "/home/test/public_html/lixi/script/../lib/lixi/Controller/logout.pm"
created "/home/test/public_html/lixi/script/../t/controller_logout.t"

メモメモ Template-ToolkitからCatalystにアクセスするに

A number of other template variables are also added:

    c      A reference to the context object, $c
    base   The URL base, from $c->req->base()
    name   The application name, from $c->config->{ name }

上記のが使える

ログイン後のテンプレートを用意

top.tt

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html lang="ja">

	<head>
		<meta http-equiv="content-type" content="text/html;charset=shift_jis">
		<title>ようこそlixiへ!</title>
	</head>

	<body bgcolor="#ffffff">
		<p>ようこそlixiへ!</p>
		<p>[% display_name %]</p>
		<p></p>
	</body>

</html>


lixi.pmとRoot.pmとlogin.pmでちゃんとログインできるように書き直します。

lixi.pm

package lixi;

use strict;
use warnings;

use Catalyst qw/-Debug 
                ConfigLoader 
                Static::Simple 
                Charsets::Japanese 
                Authentication 
                Authentication::Store::DBIC 
                Authentication::Credential::Password
                Session 
                Session::Store::FastMmap 
                Session::State::Cookie 
/;

our $VERSION = '0.01';

__PACKAGE__->config(
  name => 'lixi',
  charsets =>  'Shift_JIS',
  authentication => {
    dbic => {
      user_class =>  'lixi::Model::DBIC::Userdata',
      user_field => 'account',
      password_field => 'password',
    },
  },
);
__PACKAGE__->setup;

1;

追加修正した部分は認証系のCatalystPluginを利用するようにしたこと。
Authentication
Authentication::Store::DBIC
Authentication::Credential::Password
Session
Session::Store::FastMmap
Session::State::Cookie
DIBCの認証とセッションモジュール

Configには
authentication => {
dbic => {
user_class => 'lixi::Model::DBIC::Userdata',
user_field => 'account',
password_field => 'password',
},
},
をついか
Authentication::Store::DBIC でuser_classとuser_field,password_filedを設定する。
user_field => 'account'でユーザー名をMySQL::Userdataのaccountと結びつけるおまじない。

Root.pmとlogin.pmは以下のようにした

package lixi::Controller::Root;

use strict;
use warnings;
use base 'Catalyst::Controller';

__PACKAGE__->config->{namespace} = '';

sub auto : Private {
  my ($self, $c) = @_;

  if (!$c->user_exists) {
    $c->detach('/login/default');
    return 0;
  }else{
    my $userdata = lixi::Model::DBIC::Userdata->search( account => $c->user);
    $c->stash->{display_name} = $userdata->first->display_name;
  }
}

sub default : Private {
  my ( $self, $c ) = @_;
  # TOPPAGE
  $c->stash->{template} = 'top.tt';
}

sub end : Private { 
    my ( $self, $c ) = @_;
    $c->forward('lixi::View::TT');
}

1;

Rootではauto:Privateで常にユーザーでログインしているかチェック
auto:Privateは一番最初に動作する部分

login.pm

package lixi::Controller::login;

use strict;
use warnings;
use base 'Catalyst::Controller';

sub default : Private {
  my ( $self, $c ) = @_;
  my $req_account = $c->req->param('account');
  my $req_password = $c->req->param('password');
  #check
  if($req_account && $req_password){
    if($c->login($req_account , $req_password)){
      $c->response->redirect('/');
    }else{
      $c->stash->{error_msg} = "あかうんとめいまたはパスワードが違います";
    }
  }
  $c->stash->{template} = 'login.tt';
}

1;

ログインチェックと駄目だった場合のメッセージをerror_msgにいれる

以上で適当な認証は終わり

まだまだ続く