m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

Perl入学式に参加した感想&その後ちょっと作ったMojoliciousのサイト

Perl入学式 Advent Calendar 2014の12月16日(火)の記事です。
昨日はpapixさんのReplyでお手軽にPerlのコードを動かす話でした。

Perlの対話型環境は始めて聞きました。ちょっと触ってみようかと思います。

ここではPerl入学式への参加したことやその感想などを記載していきます。
あとついでに作ったサイト(超しょぼい)の説明とか…





入学式について

参加してよかったこと

Perlについて本当の基礎から学ぶことができた。
正規表現が組めないと使えないイメージしかないからなかなか手を付けることが出来なかった。
入学式に参加して基礎から学ぶ機会を与えられたのでPerlについて興味をもつきっかけを得ることができました。
まだ、あんまり理解していないけど…

本当に最初から学ぶことができる

プログラミングの初心者でも他の言語を知ってても最初から学べる。内容によっては時間が足りないこともあるのがネックかな?と思うくらい。

サポーターが意外と多い

ハンズオンとかの場合は大抵2~3人、または4~5人辺りでしたけど私が参加した時は入学式参加者全員がすぐに聞ける状態でしたのでわからないことはすぐに聞きやすかったと思います。

Perlの情報を集めやすくなった

cpanはいまいち読めんけどいろいろなところと組み合わせればなんとかなる。
twitterや他のコミュニティサイトで質問してくれたら教えてくれるところも嬉しい。

2014年度は参加は未定中

基本、2013年のYAPCから参加し始めて最後までいたので独学でできそうなところは自分でなんとかしたほうがいいと思いましたし、勉強会の空き席の状況がほとんどなかったので盛況なんだなと思いました。
ただ、他の勉強会がある場合は時間を調整して参加しています。


実は参加するかどうかでかなり悩んだ

IT系のコミュニティとか勉強会とかは私の回りの人間は誰も参加していないので「参加するべきか?」とか「敷居高い?」とか「いまさらやるべきじゃない?」などの考えが結構でて申し込みを行うのは結構ギリギリまて時間をかけてしまいました。

作ったWebサイト

注意

期待しないで、想像しているより300%以上しょぼいです。

どんなサイト

8月下旬に痛風になってしまいまして。プリン体ってなんぞや?ということから同じように痛風になっている人やプリン体がどんなものに含まれているのかを軽くまとめたサイトです。専門的な知識はないのでいくつかのサイトを巡って得た情報を出してるだけですけど…

作成する上でベースとしたもの

http://yusuke.be/post/80142082560

画面

画面では痛風患者の統計とか出す
f:id:m_shige1979:20141203215446p:plain

画像をクリックするとプリン体含有量のリストを表示
f:id:m_shige1979:20141203215458p:plain

環境

サーバ

CentOS

データベース

MySQL

JS

AngularJS

CSS

Twitter Bootstrap

起動アプリ

Starman
supervisor

アプリケーション構成

以下のコマンドで簡単なアプリを構成

mojo generate app Tufu::Web

ソースコードは実際
https://github.com/mshige1979/tufuapi
を見たほうが速いかも

ファイル構成

.
├── config.pl
├── env
│   ├── sass
│   │   ├── config.rb
│   │   ├── images
│   │   ├── sass
│   │   │   └── app.scss
│   │   └── stylesheets
│   │       ├── ie.css
│   │       ├── print.css
│   │       └── screen.css
│   └── sql
│       ├── alter.sql
│       ├── beer_insert.sql
│       ├── ddl.sql
│       └── food_insert2.sql
├── lib
│   └── Tufu
│       ├── Config
│       │   └── App.pm
│       ├── DB
│       │   └── Schema.pm
│       ├── DB.pm
│       ├── Model
│       │   ├── App.pm
│       │   ├── Beer.pm
│       │   ├── Food.pm
│       │   └── StatisticsNum.pm
│       ├── Model.pm
│       ├── Web
│       │   ├── App.pm
│       │   └── Root.pm
│       └── Web.pm
├── log
├── public
│   ├── css
│   │   ├── app.css
│   │   ├── bootstrap.css
│   │   ├── bootstrap.css.map
│   │   ├── bootstrap.min.css
│   │   ├── bootstrap-theme.css
│   │   ├── bootstrap-theme.css.map
│   │   └── bootstrap-theme.min.css
│   ├── favicon.ico
│   ├── fonts
│   │   ├── glyphicons-halflings-regular.eot
│   │   ├── glyphicons-halflings-regular.svg
│   │   ├── glyphicons-halflings-regular.ttf
│   │   └── glyphicons-halflings-regular.woff
│   ├── icon_image.png
│   ├── img
│   │   ├── draft_beer_t.png
│   │   └── tama02.jpg
│   └── js
│       ├── angular-animate.js
│       ├── angular-animate.min.js
│       ├── angular-animate.min.js.map
│       ├── angular.js
│       ├── angular.min.js
│       ├── angular.min.js.map
│       ├── angular-resource.js
│       ├── angular-resource.min.js
│       ├── angular-resource.min.js.map
│       ├── angular-route.js
│       ├── angular-route.min.js
│       ├── angular-route.min.js.map
│       ├── angular-touch.js
│       ├── angular-touch.min.js
│       ├── angular-touch.min.js.map
│       ├── app.js
│       ├── bootstrap.js
│       ├── bootstrap.min.js
│       ├── jquery-2.1.1.js
│       ├── jquery-2.1.1.min.js
│       └── jquery-2.1.1.min.map
├── script
│   └── tufu_web
├── t
│   └── basic.t
└── templates
    ├── exception.production.html.ep
    ├── layouts
    │   └── default.html.ep
    ├── not_found.production.html.ep
    └── root
        ├── index.html.ep
        ├── item1.html.ep
        ├── item2.html.ep
        └── main.html.ep

liteじゃないほうでMojoliciousを作成したほうがテンプレートのファイルなどがわかれているので管理はし易いと思います。
それにしても実際使用していないファイルが結構あるw

実装を一部記載

ここで簡単にコントローラーのルーティングの設定やキャッシュ設定を行いました

lib/Tufu/Web.pm
package Tufu::Web;
use Mojo::Base 'Mojolicious';

# This method will run once at server start
sub startup {
  my $self = shift;

  # Documentation browser under "/perldoc"
  $self->plugin('PODRenderer');

  $self->app->hook(before_routes => sub {
    my $c = shift;
    $c->req->headers->if_modified_since(
        'Thu, 01 Jun 1970 00:00:00 GMT'
    );
  });

  # Router
  my $r = $self->routes;

  # Normal route to controller
  $r->get('/')->to('root#index');
  $r->get('/templates/main.html')->to('root#main');
  $r->get('/templates/item1.html')->to('root#item1');
  $r->get('/templates/item2.html')->to('root#item2');

  # api用
  $r->get('/api/food')->to('app#food');
  $r->get('/api/beer')->to('app#beer');
  $r->get('/api/dataGraph')->to('app#dataGraph');

  # 画面をリロードした場合の対策
  $r->get('/item1')->to('root#item');
  $r->get('/item2')->to('root#item');

データベース設定
Tengなどを使用するので定義

lib/Tufu/DB.pm
package Tufu::DB;
use parent 'Teng';
__PACKAGE__->load_plugin('SearchBySQLAbstractMore');
1;

データベースのスキーマ定義
Tengで使用するためのテーブルを設定

lib/Tufu/DB/Schema.pm
package Tufu::DB::Schema;
use strict;
use warnings;
use Teng::Schema::Declare;
use utf8;
use Encode;

table{
    name 'food';
    pk   'id';
    columns qw/id kind item_name prin_value unit_name/;

};

table{
    name 'beer';
    pk   'id';
    columns qw/id maker kind item_name alcohol_content prin_value unit_name/;

};

table{
    name 'statistics_num';
    pk   'year';
    columns qw/year num_gk num_male num_female/;

};

1;


モデル設定
接続処理などの共通部分を設定

lib/Tufu/Model.pm
package Tufu::Model{
    use Mouse;
    use Tufu::DB;
    use Tufu::Config::App;

    has 'db' => (
        is => 'ro',
        isa => 'Tufu::DB',
        lazy_build => 1
    );

    has 'connect_info' => (
        is => 'ro',
        isa => 'ArrayRef',
        lazy_build => 1
    );

    sub _build_connect_info{
        my $self = shift;
        Tufu::Config::App::config->{connect_info};
    }

    sub _build_db{
        my $self = shift;

        Tufu::DB->new(
            connect_info => $self->connect_info
        );

    }

    __PACKAGE__->meta->make_immutable();
}

1;

プリン体データ取得コントローラー
データ取得用のモデルを使用してデータをjsonで返却

lib/Tufu/Web/App.pm
package Tufu::Web::App;
use Mojo::Base 'Mojolicious::Controller';

use Tufu::Model::Food;
use Tufu::Model::Beer;
use Tufu::Model::StatisticsNum;
use Data::Dumper;

sub food {
   # パラメータ指定
   my $self = shift;
   my $log = $self->app->log;

   $log->debug("food start");

   my $model = new Tufu::Model::Food();
   my $list = $model->find();

   $log->debug(Dumper($list));
   $log->debug("food end");

   # json指定
   $self->render(json => $list);
 }

 sub beer {
   # パラメータ指定
   my $self = shift;
   my $log = $self->app->log;

   $log->debug("beer start");

   my $model = new Tufu::Model::Beer();
   my $list = $model->find();
   $log->debug(Dumper($list));

   $log->debug("beer end");

   # json指定
   $self->render(json => $list);
 }

sub dataGraph {
   # パラメータ指定
   my $self = shift;
   my $log = $self->app->log;

   $log->debug("dataGraph start");

   my $model = new Tufu::Model::StatisticsNum();
   my $list = $model->find();
   $log->debug(Dumper($list));

   $log->debug("dataGraph end");

   $self->render(json => $list);
}

1;

データ取得モデル

lib/Tufu/Model/Beer.pm
package Tufu::Model::Beer{
    use Mouse;
    use Tufu::Model::App;

    extends Tufu::Model::App;

    has 'table' => (
        is => 'ro',
        isa => 'Str',
        default => 'beer'
    );

    sub find{
        my ($self) = @_;

        my $data;
        my $list = [];

        # 全件取得
        $data = $self->db->search($self->table, {});

        # ハッシュ型で設定
        while(my $row = $data->next()){
            push @{$list}, {
                id => $row->id,
                maker => $row->maker,
                kind => $row->kind,
                item_name => $row->item_name,
                alcohol_content => $row->alcohol_content,
                prin_value => $row->prin_value,
                unit_name => $row->unit_name
            };
        }

        # 結果返却
        return $list;

    }

    __PACKAGE__->meta->make_immutable();

}

ついでにjsも

app.js
google.load('visualization', '1', {
    packages: ['corechart']
});

google.setOnLoadCallback(function() {
    angular.bootstrap(document.body, ['tufuApp']);
});

var tufuApp = angular.module('tufuApp', ['ngRoute', 'ngAnimate']);

tufuApp.config(function($routeProvider, $locationProvider){

    $routeProvider.when('/', {
        templateUrl: "templates/main.html",
        controller: MainController

    }).when('/item1', {
        templateUrl: "templates/item1.html",
        controller: Page1Controller

    }).when('/item2', {
        templateUrl: "templates/item2.html",
        controller: Page2Controller

    }).otherwise({
        redirectTo: '/'
    });

});

tufuApp.config(function($locationProvider){
    $locationProvider.html5Mode({
        enabled: true,
        requireBase: false
    });
});

tufuApp.controller('NaviController', NaviController);
tufuApp.controller('GraphController', GraphController);

function MainController($scope, $http){

}

function Page1Controller($scope, $http){
    $http({
        method: 'get',
        url: '/api/food',
        withCredentials: true
    }).success(function(data) {
        $scope.food_list = data;

    }).error(function(data, status) {
        alert('通信エラーが発生しました');

    });
}

function Page2Controller($scope, $http){
    $http({
        method: 'get',
        url: '/api/beer',
        withCredentials: true
    }).success(function(data) {
        $scope.beer_list = data;

    }).error(function(data, status) {
        alert('通信エラーが発生しました');

    });
}

function NaviController($scope){
    $scope.link = function(){
        location.href = "/";
    }
}

function GraphController($scope, $http){

    $http({
        method: 'get',
        url: '/api/dataGraph',
        withCredentials: true
    }).success(function(dataList) {
        var data = new google.visualization.DataTable();

        data.addColumn('string', '年');
        data.addColumn('number', '合計');
        data.addColumn('number', '男性');
        data.addColumn('number', '女性');
        data.addRows(
            dataList
        );

        var ac = new google.visualization.ComboChart(document.getElementById('chart_div'));

        ac.draw(data,
            {
                "title" : '通院者数推移(千人)',
                "hAxis": {
                    "title": "年"
                },
                "seriesType": "bars", // 全体は棒グラフ(default='line')
                "isStacked": true,
                "series": {
                    "0": {"type": "line"}
                } // 平均だけ折れ線グラフ });
            }
        );

    }).error(function(data, status) {
        alert('通信エラーが発生しました');

    });
}

つまづいたこと

エラー画面どうしよう

▶ templatesにexception.production.html.epとかnot_found.production.html.epで対応しようかな

configファイルにパスワードとか入れるべき?

▶ 環境変数で対応して様子見

angularjsわからん

▶ がんばろう少しずつ理解していくようにする

Perlのモデルの使い方がなんかちがうかも

▶ 慣れていけばもっといい方法が見つかると思う。基本はテンプレートメソッドとかシングルトンでなんとかしよう(なってないかもしれん)

内容とかデザインしょぼい(´・ω・`)

▶ センスって重要ですな

今後の状況

まだまだ、perlに慣れていないのでもっと勉強する必要が必要。
どのような感じで作成するのがベストであるかはcpanなどを見て勉強していきます。
あと健康にも気をつけよう

おまけ

アドベントカレンダーが始まったのである程度のデータを集めてサイトを作成
https://serene-springs-4867.herokuapp.com/
※遅いです

おまけまーくつー

http://app1.mshige1979tools1.net/
※使ってて虚しくなります

まとめ

Perlを使いこなすには正規表現は必要ですけど、正規表現が全くわからなくでもPerlを使うことはできます。まあ、できたほうが断然便利なんで勉強はちょくちょくやるかも



以上