Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

perl casual #4 大改造!! Perl劇的ビフォーアフター

3,134 views

Published on

http://cpanbook.koneta.org/post/5027293272/perl-casual-4

Published in: Technology
  • Be the first to comment

  • Be the first to like this

perl casual #4 大改造!! Perl劇的ビフォーアフター

  1. 1. 本が出ました• m(__)m
  2. 2. CONFIDENTIAL @tomitaPerl Casual #4 @tomita
  3. 3. アジェンダ• 物件1: バッチ処理• 物件2: メール送信フォーム の使い⽅紹介
  4. 4. 物件1 バッチ処理 1
  5. 5. $dbuser = company-batch;$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass);$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);open FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  6. 6. 0 0 1 * * cd /home/app/; perl batch/dump.pl
  7. 7. 改善したい点• 動くのだけど・・・
  8. 8. $dbuser = company-batch;$dbpass = company-batch-pass;use DBI; パスワードをスクリ$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = プトに書きたくない localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass);$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);open FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  9. 9. $dbuser = company-batch;$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass); 前⽉指定が怪しい。$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry あと引数で任意の⽉ WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month); を指定したいopen FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  10. 10. $dbuser = company-batch;$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass); 自作CSV出⼒部分の$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry 不安 WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);open FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  11. 11. 改善したい点• パスワードをスクリプトに書きたくな い• 前⽉指定が怪しい。• あと引数で任意の⽉を指定したい• 自作CSV出⼒部分の不安• を置いておいて
  12. 12. 潜在的な問題
  13. 13. p.12
  14. 14. $dbuser = company-batch;$dbpass = company-batch-pass;use DBI; use strict;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = use warnings; localtime($month); がない = ケアレス$month = sprintf("%4d%02d", $year+1900, $mon);$db =$st = ミスの可能性 DBI->connect(dbi:mysql:company, $dbuser, $dbpass); $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);open FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  15. 15. $dbuser = company-batch; 例外処理不⾜$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass);$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);open FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  16. 16. $dbuser = company-batch;$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7; ⽇本語を含む値($sec, $min, $hour, $mday, $mon, $year) = だった場合 localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass);$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);open FH, "> $month.csv";while (my $row = $st->fetchrow_arrayref) { print FH join ",", @$row; print FH "¥r¥n";}
  17. 17. 潜在的な問題• use strict; use warnings; がない ←• 例外処理不⾜• ⽇本語を含む値だった場合
  18. 18. use strictuse warnings;$dbuser = company-batch;$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass);$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section
  19. 19. perl -c> perl –c dump.plGlobal symbol "$dbuser" requires explicit package name atdump.pl line 5
  20. 20. 潜在的な問題• use strict; use warnings; がない• 例外処理不⾜ ←• ⽇本語を含む値だった場合
  21. 21. p.246, 247 PRポイント: 全編falseを返すのか、 例外を返すのか、例 外を返すにはどうし たらよいのかを触れ ている
  22. 22. p.15
  23. 23. use strictuse warnings;use autodie; autodie;my $dbuser = company-batch;my $dbpass = company-batch-pass;use DBI;my $month = time - 60*60*24*7;my ($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);my $db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass,{ RaiseError => 1,});my $st = $db->prepare(q{
  24. 24. MAILTO=admin@example.com0 0 1 * * cd /home/app/; perl batch/dump.pl
  25. 25. 潜在的な問題• use strict; use warnings; がない• 例外処理不⾜• ⽇本語を含む値だった場合 ←
  26. 26. p.248 DBI DBI PRポイント: 全編を通して”⽂字列” を受け取るのか、返 すのかに触れている
  27. 27. p.11 凡例
  28. 28. my $db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass,{ RaiseError => 1, mysql_enable_utf8 => 1,});...open(my $fh, >:encoding(cp932), $filename); open()の使い⽅ですが openモジュールの項目 で解説しています
  29. 29. 改善したい点• パスワードをスクリプトに書きたくな い• 前⽉指定が怪しい。• あと引数で任意の⽉を指定したい• 自作CSV出⼒部分の不安
  30. 30. 設定ファイルJSON形式YAML形式INI形式XML形式
  31. 31. p.223 App::Options
  32. 32. 改善したい点• パスワードをスクリプトに書きたくな い• 前⽉指定が怪しい。• あと引数で任意の⽉を指定したい• 自作CSV出⼒部分の不安
  33. 33. use strict;use warnings;use autodie;use DBI; App::Options(use App::Options( option => { filename_format => { ./%s.csv s.csv default => ./%s.csv }, dbuser => { required => 1 }, dbpass => { required => 1 }, month => { required => 1, type => /^20¥d{4}$/ }, /^20¥ });my $db = DBI->connect(dbi:mysql:company, $App::options{dbuser}, App::options{dbuser} $App::options{dbpass}, App::options{dbpass} { RaiseError => 1, mysql_enable_utf8 => 1, },);
  34. 34. •dump.confdbuser = company-batchdbpass = company-batch-pass> perl dump.pl --month=201103 --helpUsage: dump.pl [options] [args] --help print this message --dbpass=<value> [********] --dbuser=<value> [company-batch] --filename_format=<value> [./%s.csv] --month=<value> [201103] (/^20¥d{4}$/)
  35. 35. 改善したい点• パスワードをスクリプトに書きたくな い• 前⽉指定が怪しい。 ←• あと引数で任意の⽉を指定したい• 自作CSV出⼒部分の不安
  36. 36. p.95-p.95- Time::Piece
  37. 37. Time::Piece Piece;use Time::Piece;use Time::Seconds; Time::Seconds;use App::Options( option => { filename_format => { default => ./%s.csv }, dbuser => { required => 1 }, dbpass => { required => 1 }, month => { type => /^20¥d{4}$/, default => do { localtime; my $d = localtime; $d- month_last_day; $d -= ONE_DAY * $d->month_last_day; $d- strftime(%Y%m); $d->strftime(%Y%m); }, }, });my $db = DBI->connect(dbi:mysql:company,
  38. 38. 改善したい点• パスワードをスクリプトに書きたくな い• 前⽉指定が怪しい。• あと引数で任意の⽉を指定したい• 自作CSV出⼒部分の不安 ←
  39. 39. p.202 Text::CSV
  40. 40. my $rows = $db->selectall_arrayref(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section}, {}, $App::options{month},);my $filename = sprintf $App::options{filename_format}, $App::options{month};open(my $fh, >:encoding(cp932), $filename); $csv Text::CSV-my $csv = Text::CSV->new({ auto_diag => 1, binary => 1, eol => "¥r¥n", "¥});$csv->print($fh, $_) for @$rows; csv- print($fh,
  41. 41. なんということでしょう〜•before$dbuser = company-batch;$dbpass = company-batch-pass;use DBI;$month = time - 60*60*24*7;($sec, $min, $hour, $mday, $mon, $year) = localtime($month);$month = sprintf("%4d%02d", $year+1900, $mon);$db = DBI->connect(dbi:mysql:company, $dbuser, $dbpass);$st = $db->prepare(q{ SELECT section, COUNT(*) AS count FROM inquiry WHERE DATE_FORMAT(ctime, "%Y%m") = ? GROUP BY section});$st->execute($month);
  42. 42. なんということでしょう〜•afteruse strict;use warnings; https://gist.github.com/956691use autodie;use DBI;use Text::CSV;use Time::Piece;use Time::Seconds;use App::Options( option => { filename_format => { default => ./%s.csv }, dbuser => { required => 1 }, dbpass => { required => 1 }, month => { type => /^20¥d{4}$/, default => do { my $d = localtime;
  43. 43. 導⼊したモジュール• strict• warnings• autodie• Text::CSV• Time::Piece• App::Options
  44. 44. モジュールのインストール• → p.421- App::cpanminus
  45. 45. App::cpanminusの項目で取り上げてApp::cpanminusいる内容• cpanmコマンド自体の導⼊⽅法• インストールパスの確認⽅法• 特定のディレクトリにインストールす る⽅法(ユーザー権限で)• インストール失敗時のトラブルシュー ティング
  46. 46. > cpanm -l extlib autodie Text::CSV Time::Piece App::Optionsuse strict;use warnings;use FindBin; FindBin;use lib "$FindBin::Bin/extlib/lib/perl5";use autodie;use DBI;use Text::CSV;use Time::Piece;use App::Options(> perl -Mlib="extlib/lib/perl5/" dump.cgi
  47. 47. 物件2 メール送信フォーム 2
  48. 48. 割愛
  49. 49. 割愛
  50. 50. 1ページ目のサンプルがほとんど• strict/warningsに通らない• jcode.pl付属(2005年以前?)• メールのソースを⼿作り• 自作のバリデーション• エラー⽂⾔が送信プログラムに書いてある• ※だめというわけではありません
  51. 51. 改造• というよりは作り直しの例
  52. 52. サーバー サーバー・バリデーション ・バリデーション ・処理(メール送信)
  53. 53. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><link rel="stylesheet" href="http://static.koneta.org/base.css" type="text/css" /><title>お問い合わせフォーム</title></head><body><h1>お問い合わせフォーム</h1> https://gist.github.com/956691<form method="post" id="form"> <p>お名前<br /> <input type="text" class="input text" name="name" /></p> <p>メールアドレス<br /> <input type="text" class="input text ascii" name="email" /></p> <p>お問い合わせ内容<br /> <textarea class="input" name="comment"></textarea></p> <p class="notice message hide">入力内容を確認してください。</p> <p class="error message hide">送信失敗しました。info@koneta.orgまで送信願います。</p> <p class="information message hide step2">この内容で送信してもよいですか?</p> <button type="submit" class="step1" value="2">確認</button> <button type="submit" class="step2 hide" value="1">修正</button> <button type="submit" class="step2 hide" value="3">送信</button></form> <p class="success message hide step3">お問い合わせありがとうございました。</p></body></html>
  54. 54. サーバーサイド• JSON作成• JSON RPC サーバー• ⼊⼒値バリデーション• メール作成• メール送信• 割愛: トークン、丁寧なエラー
  55. 55. サーバーサイド• JSON作成 ←• JSON RPC サーバー• ⼊⼒値バリデーション• メール作成• メール送信
  56. 56. p.180 JSON
  57. 57. use JSON;my $res = { version => 1.1 };if (my $error = validate($req)) { $res->{error} = $error;}if ($res->{validate} and $req->param(step) eq submit) { $res->{result} = sendmail($req);} encode_json($res);my $body = encode_json($res);
  58. 58. サーバーサイド• JSON作成• JSON RPC サーバー ←• ⼊⼒値バリデーション• メール作成• メール送信
  59. 59. Plack::Request;use Plack::Request;my $app = sub { $req Plack::Request- new(shift); my $req = Plack::Request->new(shift); my $res = { version => 1.1 }; if (my $error = validate($req)) { $res->{error} = $error; } if ($res->{validate} and $req->param(step) eq submit) { $res->{result} = sendmail($req); } my $body = encode_json($res); return [ 200, Content- application/json json [ Content-Type => application/json ], [ $body ], ];};
  60. 60. なんということでしょう〜•before 割愛
  61. 61. なんということでしょう〜•afteruse strict;use wanings;use JSON https://gist.github.com/956691use Plack::Request;my $app = sub { my $req = Plack::Request->new(shift); my $res = { version => 1.1 }; if (my $error = validate($req)) { $res->{error} = $error; } if ($res->{validate} and $req->param(step) eq submit) { $res->{result} = sendmail($req); }
  62. 62. p.334, 335 Plack
  63. 63. サーバーサイド• JSON作成• JSON RPC サーバー• ⼊⼒値バリデーション ←• メール作成• メール送信
  64. 64. p.346 FormValidator::Lite• おすすめ理由 http://d.hatena.ne.jp/tomi-ru/20110119/1295394476
  65. 65. なんということでしょう〜•before 割愛
  66. 66. •after qw/Email/;use FormValidator::Lite qw/Email/;sub validate { my ($req) = @_; ($req) req FormValidator::Lite- new($req); my $form = FormValidator::Lite->new($req); $form- $form->check( email => [REQUIRED, EMAIL], comment => [REQUIRED], ); $form- has_error; return unless $form->has_error; return { name ValidateError ValidateError, => ValidateError, code => 100, message => Validate Error, };}
  67. 67. サーバーサイド• JSON作成• JSON RPC サーバー• ⼊⼒値バリデーション• メール作成 ←• メール送信
  68. 68. なんということでしょう〜•before 割愛
  69. 69. •afteruse utf8;use Encode;use Email::MIME; Email::MIME; Email::MIME-my $email = Email::MIME->create( header => [ From => $req->param(email), $req param(email), req- To info@koneta.org info@koneta.org, => info@koneta.org, Subject => [問い合わせフォーム], [問 わせフォーム], フォーム ], attributes => { charset iso-2022- => iso-2022-jp, encoding => 7bit, }, sprintf( body_str => sprintf( "[Name]¥n%s¥ n[Comment]¥n%s", "[Name]¥n%s¥n¥n[Comment]¥n%s", $encoding- $req param(name) req- $encoding->decode( $req->param(name) ), $encoding- $encoding->decode( $req->param(comment) ), $req param(comment) req- ),);
  70. 70. サーバーサイド• JSON作成• JSON RPC サーバー• ⼊⼒値バリデーション• メール作成• メール送信 ←
  71. 71. なんということでしょう〜•before 割愛
  72. 72. •afteruse Email::Send; Email::Send;my $sender = Email::Send->new({ mailer => Sendmail }); Email::Send- Sendmail Sendmail$sender- send($email)$sender->send($email) or die "send() failed!";
  73. 73. CONFIDENTIALありがとうございました。
  74. 74. • http://twitter.com/tomita
  75. 75. One More• ⼀⽅ロシアは・・・• テスト

×