目的
mmt.pmをmojoliciousで書き直す。
https://github.com/john-smith-7701/mmt に登録した。
初めの一歩
$ mojo generate app Tool::mmt
$ tree toolmmt
toolmmt
├── lib
│ └── Tool
│ ├── mmt
│ │ └── Example.pm
│ └── mmt.pm
├── log
├── public
│ └── index.html
├── script
│ └── toolmmt
├── t
│ └── basic.t
└── templates
├── example
│ └── welcome.html.ep
└── layouts
└── default.html.ep
10 directories, 7 files
$ svn import toolmmt svn+ssh://userId@localhost/usr/local/svn/repos/toolmmt -m "Initial Import."
$ svn co svn+ssh://userId@www21051ue.sakura.ne.jp/usr/local/svn/repos/toolmmt
2015/11/10現在
$ tree -f |perl -alne '@x=split /\s+/,`wc -l $F[-1] 2>/dev/null`;$l=sprintf("%5d %s",$x[0],$_);$l=~ s/ 0 / /;$l=~ s{\..*/}{};print $l'
.
├── lib
│ └── Tool
│ ├── mmt
13 │ │ ├── Example.pm
512 │ │ └── Mmt.pm
25 │ ├── mmt.pm
│ ├── Model
46 │ │ └── Webdb.pm
7 │ └── Model.pm
├── log
3979 │ └── development.log
├── public
11 │ └── index.html
├── script
11 │ └── toolmmt
4 ├── svn-commit.tmp
├── t
9 │ └── basic.t
└── templates
├── example
7 │ └── welcome.html.ep
├── layouts
5 │ └── default.html.ep
└── mmt
27 ├── datalist.html.ep
5 ├── desc.html.ep
29 └── mainform.html.ep
12 directories, 15 files
$
2015/11/21
とりあえず素のメンテ作成。cssも JavaScript (12/9) も無し。
[2016/01/11]
.
├── lib
│ └── Tool
│ ├── Model
│ │ ├── Webdb
29 │ │ │ └── constant.pm
210 │ │ └── Webdb.pm
7 │ ├── Model.pm
│ ├── mmt
35 │ │ ├── Commodity.pm
13 │ │ ├── Example.pm
759 │ │ ├── Mmt.pm
19 │ │ └── Usertbl.pm
28 │ └── mmt.pm
├── log
├── public
│ ├── css
121 │ │ └── default.css
11 │ ├── index.html
│ └── js
├── script
11 │ └── toolmmt
4 ├── svn-commit.tmp
├── t
9 │ └── basic.t
└── templates
├── example
7 │ └── welcome.html.ep
├── layouts
33 │ ├── default.html.ep
32 │ └── defsubwin.html.ep
└── mmt
36 ├── datalist.html.ep
9 ├── desc.html.ep
4 ├── dumper.html.ep
53 ├── mainform.html.ep
32 └── subwin.html.ep
15 directories, 21 files
構成
script/toolmmt # アプリケーションスクリプト
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
BEGIN { unshift @INC, "$FindBin::Bin/../lib" }
# Start command line interface for application
require Mojolicious::Commands;
Mojolicious::Commands->start_app('Tool::mmt');
lib/Tool/mmt.pm # アプリケーションクラス(ルーター等)
package Tool::mmt;
use Mojo::Base 'Mojolicious';
use Tool::Model;
has 'model' => sub {Tool::Model->new}; # modelを追加
has 'controller' => "mmt";
# This method will run once at server start
sub startup {
my $self = shift;
# Documentation browser under "/perldoc"
$self->plugin('PODRenderer');
$self->plugin('TagHelpers');
# Router
my $r = $self->routes;
# Normal route to controller
$r->get('/')->to('example#welcome');
$r->get('/mmt/:_table/desc')->to('mmt#desc');
$r->get('/mmt/:_table')->to(controller => $self->controller,action =>'mainform');
$r->post('/mmt/:_table')->to(controller => $self->controller,action => 'registry');
$r->get('/mmtx/:controller')->to(controller => $self->controller,action =>'mainform'); # 特別なプレースフォルダ :controllerと:action 柔軟なルーティングの構築が可能になる
$r->post('/mmtx/:controller')->to(controller => $self->controller,action => 'registry');
}
1;
lib/Tool/Model.pm
package Tool::Model;
use Mojo::Base 'Mojolicious';
use Tool::Model::Webdb;
has 'webdb' => sub { Tool::Model::Webdb->new }; # 自作モデルとか
1;
lib/Tool/Model/Webdb.pm #モデル (DB CONNECT とか…)
package Tool::Model::Webdb;
use Mojo::Base 'Mojolicious';
use DBI;
use utf8;
use Data::Dumper;
use Tool::Model::Webdb::constant;
has const => sub {
my $s = shift;
return Webdb::constant->new(); # constant dataを読み込む
};
has dbh => sub {
my $self = shift;
my $data_source = $self->const->{data_source};
my $user = $self->const->{user};
my $password = $self->const->{password};
my $dbh = DBI->connect(
$data_source,
$user,
$password,
{RaiseError => 1,
mysql_enable_utf8 =>1,
mysql_auto_reconnect =>1, # 再接続させる
}
);
$dbh->do("set names UTF8");
return $dbh;
};
sub desc_table{
my $s = shift;
my $m = shift || 'm';
my $f;
$s->{'m'}->{key} = [];
$s->{'m'}->{item} = [];
my $dbh = $s->dbh;
my $sth = $dbh->prepare($s->desc_param($m));
$sth->execute();
my $flag = 0;
while(my $ref = $sth->fetchrow_hashref()){
$f = $ref->{Field};
$s->{'m'}->{$f}->{Type} = $ref->{Type};
$s->{'m'}->{$f}->{Null} = $ref->{Null};
$s->{'m'}->{$f}->{Key} = $ref->{Key};
$s->{'m'}->{$f}->{Default} = $ref->{Default};
$s->{'m'}->{$f}->{Extra} = $ref->{Extra};
$s->{'m'}->{$f}->{Size} = $s->size($ref->{Type});
if($ref->{Key} eq 'PRI'){
push @{$s->{'m'}->{key}},$f;
}elsif($ref->{Type} =~ /timestamp/){
$s->{'m'}->{timestamp} = $f;
}else{ push @{$s->{'m'}->{item}},$f;
}
$flag = 1;
}
$sth->finish();
return $s->{'m'};
}
sub size { . . . }
.
.
1;
lib/Tool/Model/Webdb/constant.pm # コンスタントデータ
package Webdb::constant;
use utf8;
sub new {
my $class = shift;
my $self = bless {},$class;
$self->_initalize;
return $self;
}
sub _initalize{
my $s = shift;
$s->{data_source} = "DBI:mysql:database=YourDB;host=localhost";
$s->{user} = "YourName";
$s->{password} = "YourPassword";
$s->{explan} = { # DB項目の説明
'担当者' => { # TABLE NAME
'ID' => 'admin:管理者,0~999', # FIELD NAME => COMMENT
},
'商品' => {
'商品区分' => '0:課税,1:非課税,2:軽減課税',
},
};
}
1;
lib/Tool/mmt/Mmt.pm # controller
package Tool::mmt::Mmt;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::Log;
use utf8;
use Encode;
use Text::CSV::Encoded;
has 'mmtForm' => 'mmt/mainform';
has 'mmtDataList' => 'mmt/datalist';
# This action will render a template
sub mainform {
my $self = shift;
$self->{'m'} = $self->app->model->webdb->desc_table($self->param('_table')); # model 呼び出し
$self->{'m'}->{'table'} = $self->param('_table');
$self->set_input_names();
$self->action_set();
$self->my_render($self->mmtForm);
}
sub my_render{
my $s = shift;
my $render = shift;
$s->stash->{_title} = join('',$s->param('_table'));
$s->render($render);
}
sub registry{
my $self = shift;
$self->action_set();
my $log = Mojo::Log->new();
$log->debug( "IN registry" );
for my $action (@{$self->{'_action'}}) {
if($action->{'name'} eq $self->param('_action')){
return $action->{action}();
}
}
$self->stash->{'_dumper'} = $self->dumper ($self->param()) .
$self->param('_action');
$self->render('mmt/dumper');
}
.
.
.
templates/mmt/mainform.html.ep # データメンテナンス画面
% layout 'default';
% title "mmt - $_title " ;
<h1><%= $_title %></h1>
%= form_for url_for('_table'=> param('_table')) => (method => 'POST') => begin
<%= hidden_field timestamp => param('timestamp') %>
<%= hidden_field _table => param('_table') %>
<table>
% for my $item (@{$self->{'m'}->{'key'}}){
<tr>
<th><div id="<%= 'l_' . $self->{'n'}->{$item} %>"> <%= $self->Label($item) %></div></th>
<td><%== $self->input_field($item) %>
<%== $self->get_explan(param('_table'),$item) %></td>
<td><div id="<%= 'd_' . $self->{'n'}->{$item} %>">
</tr>
% }
</table>
%= submit_button $self->{'_action'}[0]->{'name'} ,id => '_action',name => '_action'
<%== $self->serch_input_field() %>
%= submit_button $self->{'_action'}[4]->{'name'} ,id => '_action',name => '_action'
%= submit_button $self->{'_action'}[6]->{'name'} ,id => '_action',name => '_action'
<br>
INFO:<%= $self->{errstr} %>
<hr>
<table>
% my $i = 0;
% for my $item (@{$self->{'m'}->{'item'}}){
<tr>
<th><div id="<%= 'l_' . $self->{'n'}->{$item} %>"> <%= $self->Label($item) %></div></th>
<td><%== $self->input_field($item) %>
<%== $self->get_explan(param('_table'),$item) %></td>
<td><div id="<%= 'd_' . $self->{'n'}->{$item} %>"></div></td>
</tr>
% }
</table>
% for my $item (@{$self->{'_action'}}[1..3]){
%= submit_button $item->{'name'} ,id => '_action',name => '_action'
% }
% end
<hr>
<form method="post" action="<%= url_for('_table'=> param('_table')) %>"
enctype ="multipart/form-data">
<input type="file" name="upload_file" />
<%= hidden_field _table => param('_table') %>
<input type="submit" value="Upload" name="_action" />
</form>
templates/mmt/datalist.html.ep # 一覧表示画面
% layout 'default';
% title "mmt - $_title";
<h1><%= $_title %></h1>
<table><tr><th></th>
% for my $item (@{$self->{'m'}->{'key'}},@{$self->{'m'}->{'item'}}){
<th><div id="<%= $item %>"> <%= $self->Label($item) %></div></th>
% }
</tr>
% my $count = 0;
% while (my $ref = $self->{'sth'}->fetchrow_hashref()){
% $count++;
% last if $count > 2000;
<tr>
%= form_for url_for('_table'=> param('_table')) => (method => 'POST') => begin
<%= hidden_field _table => param('_table') %>
<td>
%= submit_button $self->{'_action'}[0]->{'name'} ,id => '_action',name => '_action'
</td>
% for my $name (@{$self->{'m'}->{key}}){
<td><%= $ref->{Encode::encode("utf8",$name)} %>
<%= hidden_field $self->{'n'}->{$name} => $ref->{Encode::encode("utf8",$name)} %>
</td>
% }
% for my $name (@{$self->{'m'}->{item}}){
<td><%= $ref->{Encode::encode("utf8",$name)} %></td>
% }
% end
</tr>
% }
</table>
%= "* No Data *" if ($count == 0);
%= "* Max over * " if ($count > 2000);
templates/mmt/Commodity.pm # Mmtを継承したアプリケーション
package Tool::mmt::Commodity;
use Mojo::Base 'Tool::mmt::Mmt';
has 'mmtDataList' => 'mmt/datalist';
sub init_set {
my $s = shift;
$s->mmtForm('mmt/mainform');
$s->param('_table','商品');
}
sub look_up_set{
my $s = shift;
$s->{'m'}->{LOOK_UP}->{ref $s}->{$s->{'n'}->{'大分類'}} =
["select 略称 from 分類名称 where 中分類 = '' and 小分類 = '' and 大分類 = ? ",
[$s->{'n'}->{'大分類'}] ];
$s->{'m'}->{SUBWIN}->{ref $s}->{$s->{'n'}->{'大分類'}} =
["select 大分類,略称 from 分類名称 where 中分類 = '' and 小分類 = ''",
[]];
$s->{'m'}->{LOOK_UP}->{ref $s}->{$s->{'n'}->{'中分類'}} =
["select 略称 from 分類名称 where 大分類 = ? and 中分類 = ? and 小分類 = '' ",
[$s->{'n'}->{'大分類'},$s->{'n'}->{'中分類'}] ];
$s->{'m'}->{SUBWIN}->{ref $s}->{$s->{'n'}->{'中分類'}} =
["select 中分類,略称 from 分類名称 where 大分類 = ? and 中分類 <> '' and 小分類 = ''",
[$s->{'n'}->{'大分類'}] ];
$s->{'m'}->{LOOK_UP}->{ref $s}->{$s->{'n'}->{'小分類'}} =
["select 略称 from 分類名称 where 大分類 = ? and 中分類 = ? and 小分類 = ? ",
[$s->{'n'}->{'大分類'},$s->{'n'}->{'中分類'},$s->{'n'}->{'小分類'}] ];
$s->{'m'}->{SUBWIN}->{ref $s}->{$s->{'n'}->{'小分類'}} =
["select 小分類,略称 from 分類名称 where 大分類 = ? and 中分類 = ? and 小分類 <> ''",
[$s->{'n'}->{'大分類'},$s->{'n'}->{'中分類'}] ];
}
1;
TODO
- validation
検索SUB画面 (1/7)
- 綺麗な画面
ENTERで項目移動 (12/9)
- セッション管理
- login処理
- メニュー作成
- レポートライターツール
ENTERで項目移動 (12/9)
[toolmmt/templates/layouts/toolmmt/templates/layouts]
<!DOCTYPE html>
<html>
<head><title><%= title %></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript"> <!-- [jQuery] Enterキーでフォーカスを移動するには http://blog.makotoishida.com/2013/02/javascript-enter.html -->
$(function(){
var elements = "input[type=text]";
$(elements).keypress(function(e) {
var c = e.which ? e.which : e.keyCode;
if (c == 13) {
var index = $(elements).index(this);
var criteria = e.shiftKey ? ":lt(" + index + "):last" : ":gt(" + index + "):first";
$(elements + criteria).focus();
e.preventDefault();
}
});
});
</script>
</head>
<body><%= content %></body>
</html>
ajaxにてテーブル参照とサブウインドを開く
sub make_ajax{
my $s = shift;
my $p = '';
my $onload = '';
#
# マスタ参照ajax(LOOK_UPをサーチ)
#
for (keys %{$s->{'m'}->{LOOK_UP}->{ref $s}}){
next unless ($_ =~ /^(\D)+(\d)+$/);
$p .= $s->new_updater($_);
$onload .= "\$('#$_').change();\n";
}
#
# マスタ参照ウインド(WUBWINをサーチ)
#
for (keys %{$s->{'m'}->{SUBWIN}->{ref $s}}){
next unless ($_ =~ /^(\D)+(\d)+$/);
$p .= $s->new_subwin($_);
}
return $p . $onload;
}
#
# マスタ参照(http://www21051ue.sakura.ne.jp:3003/mmtx/commodity?_action=get_name&n=item5&p=001&p=002)
#
sub get_name{
my $s = shift;
my @names = qw/未登録/;
#eval {
@names = $s->app->model->webdb->dbh->selectrow_array(
$s->{'m'}->{LOOK_UP}->{ref $s}->{$s->param('n')}->[0],undef,$s->param('p'));
#};
if ($@){
$s->render(json=>$@);
} else{
my $json = {rec=>@names};
$s->render(json=>$json); # JSONを描画する
}
}
sub subwin{
my $s = shift;
my $subwin = $s->{'m'}->{SUBWIN}->{ref $s}->{$s->param('n')};
my $sql = $subwin->[0];
my $render = $subwin->[2] || 'mmt/subwin';
my $param = $s->param('p');
my $dbh = $s->app->model->webdb->dbh;
$s->{'sth'} = $dbh->prepare($sql);
$s->{'sth'}->execute($s->param('p'));
$s->stash->{_title} = '検索';
$s->stash->{_sql} = $sql;
$s->stash->{_controller} = ref $s;
$s->render($render);
}
sub new_updater{
my $s = shift;
my $n = shift;
my $p = '"';
$p .= join '',map{qq{ + "&p=" + \$('#$_').val()}} @{$s->{'m'}->{'LOOK_UP'}->{ref $s}->{$n}->[1]};
return <<End_Script;
jQuery('#$n').change( function (){ // 内容が変化した時に実行
jQuery.ajax({ // http通信を行う
type: 'GET', // 通信種類を指定(GET,POST,PUT,DELETE)
dataType: 'json', // サーバーから返されるデータタイプ
data: "_action=get_name&n=$n$p , // サーバーに送信する値
success:function(data,textStatus,jqXHR){ // ajax通信が成功した時のajax event
jQuery('#d_$n').html(data.rec); // 返値を描画
return false;
}
});
});
End_Script
}
sub new_subwin{
my $s = shift;
my $n = shift;
my $p = '"';
$p .= join '',map{qq{ + "&p=" + \$('#$_').val()}} @{$s->{'m'}->{'SUBWIN'}->{ref $s}->{$n}->[1]};
my $win_para = "width=600,height=500,resizable=yes,scrollbars=yes";
return <<End_Script;
jQuery(document).ready(function(){
jQuery('#l_$n').html( // LABELをボタンに変更する
"<input type=button value=@{[$s->Label($s->get_input_name($n))]}>");
});
jQuery('#l_$n').click( function (){ // SUB WINDOWを開く
window.open("@{[$s->url_for->query(
_action=>'subwin'
,n=>$n
)
]}$p,'_blank','$win_para');
return false;
});
End_Script
}
1;
SUB WINDOW
[layouts/defsubwin.html.ep]
<!DOCTYPE html>
<html>
<head><title><%= title %></title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript"> <!-- [jQuery] Enterキーでフォーカスを移動するには http://blog.makotoishida.com/2013/02/javascript-enter.html -->
$(function(){
var elements = "input[type=text]";
$(elements).keypress(function(e) {
var c = e.which ? e.which : e.keyCode;
if (c == 13) {
var index = $(elements).index(this);
var criteria = e.shiftKey ? ":lt(" + index + "):last" : ":gt(" + index + "):first";
$(elements + criteria).focus();
e.preventDefault();
}
});
});
function setVal(terget,val){
window.opener.$("#"+terget).val(val); // 選択値を親画面にセットする
window.opener.$("#"+terget).change(); // changeイベントを発生する
window.opener.$("#"+terget).focus(); // フォーカスを移動する
window.close(); // 自ウィンドを閉じる
}
</script>
<%= stylesheet '/css/default.css' %>
</head>
<body>
<div class="subwin">
<%= content %>
</div>
</body>
</html>
[mmt/subwin.html.ep]
% layout 'defsubwin';
% title "mmt - $_title";
<h1><%= $_title %></h1>
<%= $_sql %><br />
[<%= $_controller %>][<%= param('n') %>][<%= join '|',param('p') %>]
<table border=1><thead><tr>
% for my $item (@{$self->{'sth'}->{'NAME'}}){
<th><div id="<%= $item %>"> <%= Encode::decode('UTF-8',$item) %></div></th>
% }
</tr>
</thead>
<tbody>
% my $count = 0;
% while (my $ref = $self->{'sth'}->fetchrow_arrayref()){
% $count++;
% last if $count > 2000;
<tr>
% my $i = 0;
% for my $item (@{$ref}){
% $i++;
<td>
% if($i == 1){
<a href="" onclick="setVal('<%= param('n') %>','<%= $item %>');">
% }
<%= $item %></td>
% }
</tr>
% }
</tbody>
</table>
%= "* No Data *" if ($count == 0);
%= "* Max over * " if ($count > 2000);