プルダウンと返り値
<form> 月: <select name="month"> <option value="選択してください">選択してください</option> <option value="2018/05">2018/05</option> <option value="2018/06">2018/06</option> <option value="2018/07">2018/07</option> <option value="2018/08">2018/08</option> <option value="2018/09">2018/09</option> <option value="2018/06">2018/10</option> <option value="2018/11">2018/11</option> <option value="2018/12">2018/12</option> </select></form>
上記コードの通り、プルダウンを作った。
<input type="submit" name="submit" value="Serach" />
Searchをしても適切な返り値が表示されない。
月: <select name="month"> <option value="選択してください">選択してください</option> <option value="2018/05">2018/05</option> <option value="2018/06">2018/06</option> <option value="2018/07">2018/07</option> <option value="2018/08">2018/08</option> <option value="2018/09">2018/09</option> <option value="2018/06">2018/10</option> <option value="2018/11">2018/11</option> <option value="2018/12">2018/12</option> </select>を消したところ、適切な返り値が表示されるようになった。
ただ、このやり方は推奨されるやり方ではなさげ。
日本語文字化けに関して
ブラウザを通してデータベースに入力される日本語文字列が文字化けを起こしている。
上記サイトを参考にして、character_set_database、character_set_server がデフォルトでutf8になるように変更した。
+--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec)
また、テーブル自体も作り直したが、文字化けが解消されない。
__PACKAGE__->config( schema_class => 'Kikaito::Schema', connect_info => { dsn => 'dbi:mysql:host=localhost:database=kikaito', user => 'root', password => '', mysql_enable_utf8 => 1, } );
Model/DB.pm内にあるconfigに"mysql_enable_utf8 => 1,"を追加すると文字化けが解決した。
DBIx::Classについて
package DBIx::Class; use strict; #Perlの文法を厳密にチェック use warnings; #詳細な警告を出力 #バージョンナンバーを格納するグローバル変数の宣言。 our $VERSION; #"0.082841"という数値を$VERSIONに格納。 $VERSION = '0.082841'; #もし$VERSIONに"_"が含まれていたら、$VERSIONに格納されている値をPerlの文として評価する。アンダースコアが付いている(αバージョンが付いている)と数値への自動変換が行なわれない。 $VERSION = eval $VERSION if $VERSION =~ /_/; use DBIx::Class::_Util; #"c3"というmro(メソッド解決順序)を使用する。C3は多重継承における健全なメソッド解決順序を提供することを目的としたアルゴリズムである。 use mro 'c3'; #/usr/local/share/perl5/DBIx/Cl∂ass/OptionalというディレクトリにあるDependencies.pmというモジュールを使用する。 use DBIx::Class::Optional::Dependencies; #クラスを継承するにはbaseモジュールを使用する。これにより親クラスのメソッドを使えるようになる。 use base qw/DBIx::Class::Componentised DBIx::Class::AccessorGroup/; use DBIx::Class::StartupCheck; use DBIx::Class::Exception; #mk_group_accessorsは、アクセサのグループを作るためのモジュールであるClass::Accessor::Groupedで使用できるメソッド。 __PACKAGE__->mk_group_accessors(inherited => '_skip_namespace_frames'); #"^"はメタ文字。行頭にマッチするかどうか調べる機能がある。"|"はメタ文字。いくつかのパターンから1つを選択する機能がある。 __PACKAGE__->_skip_namespace_frames('^DBIx::Class|^SQL::Abstract|^Try::Tiny|^Class::Accessor::Grouped|^Context::Preserve'); #DBIx::Classにある_Util.pm内のdetected_reinvoked_destructorというパッケージ(サブルーチン)を呼び出す。 sub DESTROY { &DBIx::Class::_Util::detected_reinvoked_destructor } sub mk_classdata { shift->mk_classaccessor(@_); } sub mk_classaccessor { #引数受け取り。"DBIx::Class"を格納。 my $self = shift; $self->mk_group_accessors('inherited', $_[0]); $self->set_inherited(@_) if @_ > 1; } sub component_base_class { 'DBIx::Class' } sub MODIFY_CODE_ATTRIBUTES { #引数受け取り。 my ($class,$code,@attrs) = @_; $class->mk_classdata('__attr_cache' => {}) unless $class->can('__attr_cache'); $class->__attr_cache->{$code} = [@attrs]; return (); } sub _attr_cache { #引数受け取り。"DBIx::Class"を格納。 my $self = shift; my $cache = $self->can('__attr_cache') ? $self->__attr_cache : {}; return { %$cache, %{ $self->maybe::next::method || {} }, }; } sub DBIx::Class::_ENV_::HELP_URL () { 'http://p3rl.org/DBIx::Class#GETTING_HELP/SUPPORT' } 1;
package DBIx::Classについて
package DBIx::Class; use strict; #Perlの文法を厳密にチェック use warnings; #詳細な警告を出力 #バージョンナンバーを格納するグローバル変数の宣言。 our $VERSION; #"0.082841"という数値を$VERSIONに格納。 $VERSION = '0.082841'; #もし$VERSIONに"_"が含まれていたら、$VERSIONに格納されている値をPerlの文として評価する。アンダースコアが付いている(αバージョンが付いている)と数値への自動変換が行なわれない。 $VERSION = eval $VERSION if $VERSION =~ /_/; use DBIx::Class::_Util; #"c3"というmro(メソッド解決順序)を使用する。C3は多重継承における健全なメソッド解決順序を提供することを目的としたアルゴリズムである。 use mro 'c3'; #/usr/local/share/perl5/DBIx/Cl∂ass/OptionalというディレクトリにあるDependencies.pmというモジュールを使用する。 use DBIx::Class::Optional::Dependencies; #クラスを継承するにはbaseモジュールを使用する。これにより親クラスのメソッドを使えるようになる。 use base qw/DBIx::Class::Componentised DBIx::Class::AccessorGroup/; use DBIx::Class::StartupCheck; use DBIx::Class::Exception; #mk_group_accessorsは、アクセサのグループを作るためのモジュールであるClass::Accessor::Groupedで使用できるメソッド。 __PACKAGE__->mk_group_accessors(inherited => '_skip_namespace_frames'); #"^"はメタ文字。行頭にマッチするかどうか調べる機能がある。"|"はメタ文字。いくつかのパターンから1つを選択する機能がある。 __PACKAGE__->_skip_namespace_frames('^DBIx::Class|^SQL::Abstract|^Try::Tiny|^Class::Accessor::Grouped|^Context::Preserve'); #DBIx::Classにある_Util.pm内のdetected_reinvoked_destructorというパッケージ(サブルーチン)を呼び出す。 sub DESTROY { &DBIx::Class::_Util::detected_reinvoked_destructor } sub mk_classdata { shift->mk_classaccessor(@_); } sub mk_classaccessor { #引数受け取り。"DBIx::Class"を格納。 my $self = shift; $self->mk_group_accessors('inherited', $_[0]); $self->set_inherited(@_) if @_ > 1; } sub component_base_class { 'DBIx::Class' } sub MODIFY_CODE_ATTRIBUTES { #引数受け取り。 my ($class,$code,@attrs) = @_; $class->mk_classdata('__attr_cache' => {}) unless $class->can('__attr_cache'); $class->__attr_cache->{$code} = [@attrs]; return (); } sub _attr_cache { #引数受け取り。"DBIx::Class"を格納。 my $self = shift; my $cache = $self->can('__attr_cache') ? $self->__attr_cache : {}; return { %$cache, %{ $self->maybe::next::method || {} }, }; } sub DBIx::Class::_ENV_::HELP_URL () { 'http://p3rl.org/DBIx::Class#GETTING_HELP/SUPPORT' } 1;
サインアップとログイン
サインアップとログイン処理を行うために書いたコード。
sub sign_up :Local { my ( $self, $c ) = @_; if ($c->request->method ne 'POST') { return; } my $params = $c->req->params; if($c->model("db::users")->search({username => $params->{username}}) == 0){ my $digest = sha1_hex($params->{password}); $c->model("db::users")->create({id => 0, username => $params->{username}, password => $digest}); $c->res->redirect("/register/completed"); } } sub login :Local { my ( $self, $c ) = @_; if ($c->request->method ne 'POST') { return; } my $params = $c->req->params; my $digest = sha1_hex($params->{password}); warn "$params->{username}: $params->{password}"; if($c->authenticate({username => $params->{username}, password => $params->{password}})){ warn "bbbb"; $c->response->redirect("http://united.jp/"); } else { $c->stash(error_msg => 'User name or password is incorrect.'); warn "aaaaaa"; } }
良いコードを参考にしましょう③
OpenWeatherMapを使用して、指定された日の12時の気温を取得するためのコードをどのようにして書くか。
自分が書いたコード。
#指定された日の12時の気温、最高気温、最低気温が入ったハッシュリファレンスを返すためのサブルーチンを作成。 sub return_temp{ my ($api_data, $date) = @_; unless (defined($api_data)){ print "API data is not defined.\n"; return undef; } my $date_12pm = $date." 12:00:00"; my $key; #12時の気温の取得。 my $temp_12 = 'undef'; foreach $key(keys $api_data->{'list'}){ my $dt_txt_phr = $api_data->{'list'}->[$key]->{'dt_txt'}; if ($dt_txt_phr) { if ($dt_txt_phr =~ /$date_12pm/){ $temp_12 = $api_data->{'list'}->[$key]->{'main'}->{'temp'}; } } }
他の人が書いたコード。
sub get_temp_at_noon { my ( $json, $date ) = @_; return undef if( !$json ); my $content = decode_json( $json ); my $hour = "12:00:00"; my ($noon) = grep { $_->{dt_txt} =~ /$date $hour/ } @{ $content->{list} }; if( $noon->{main}->{temp} ) { return $noon->{main}->{temp}; } return undef; }
JSONデータが定義されていない時undefを返すためのコードは、
unless (defined($api_data)){ return undef; }
上記のように書くより、次のように書いた方がコンパクトでわかりやすい。
return undef if( !$json );
最高気温と最低気温を取得する処理。
sub get_temp_maxmin { my ( $json, $date ) = @_; return undef if( !$json ); my $content = decode_json( $json ); my @temps = grep { $_->{dt_txt} =~ /$date/ } @{ $content->{list} }; my $maxmin = {}; $maxmin->{max} = ( sort map { $_->{main}->{temp_max} } @temps )[-1]; $maxmin->{min} = ( sort map { $_->{main}->{temp_min} } @temps )[0]; if( $maxmin->{max} && $maxmin->{min} ) { return $maxmin; } return undef; }
ハッシュを作らずいきなりハッシュのリファレンスを作成している。
@tempsの要素を1つずつmapで処理していて、その戻り値のリストをsortで並べ替え一番最後の値を返している。
Catalystでアプリケーションを作成した際に生成されるスケルトンについて
Catalystで新規のアプリケーションを作成すると次のようなスケルトンが生成される。
├── Changes ├── Makefile.PL ├── README ├── lib │ ├── MyApp │ │ ├── Controller │ │ │ └── Root.pm │ │ ├── Model │ │ └── View │ └── MyApp.pm ├── myapp.conf ├── myapp.psgi ├── root │ ├── favicon.ico │ └── static │ └── images │ ├── btn_120x50_built.png │ ├── btn_120x50_built_shadow.png │ ├── btn_120x50_powered.png │ ├── btn_120x50_powered_shadow.png │ ├── btn_88x31_built.png │ ├── btn_88x31_built_shadow.png │ ├── btn_88x31_powered.png │ ├── btn_88x31_powered_shadow.png │ └── catalyst_logo.png ├── script │ ├── myapp_cgi.pl │ ├── myapp_create.pl │ ├── myapp_fastcgi.pl │ ├── myapp_server.pl │ └── myapp_test.pl └── t ├── 01app.t ├── 02pod.t └── 03podcoverage.t
各ディレクトリがそれぞれどのような存在意義を持つかあやふやなまま何となく課題に取り組んでいる感が否めないので、ざっくばらんにまとめてみる。
・Changes
更新履歴ファイル。
・ Makefile.PL
ビルドファイル。ビルドとは、ソースコードファイルを独立したソフトウェア生成物に変換するコンピュータ上で実行されるプロセス、またはその結果のことである。 ビルドにおいて最も重要なのはコンパイルプロセスであり、ソースコードファイルを実行ファイルに変換する。
・README
READMEファイル。説明書。
・lib
モジュール(.pm)を格納するためのディレクトリ。
・lib/MyApp
アプリケーションのメインフォルダ。
・lib/MyApp/Controller
コントローラクラス。リクエストの受信からレスポンスの送信までを一手に管理するディレクトリ。
Model(実際のビジネスロジック)を呼び出すのも、その結果をView(ユーザーインターフェイス)に引き渡すのも、コントローラの役割である。
・lib/MyApp/Controller/Root.pm
ルートコントローラ。名前の通り、ルートパス「/」に関連づけられた特別なコントローラ。
・lib/MyApp/Model
モデルクラス。データベースへのアクセスなど、データの管理/操作を担当するコンポーネント。
・lib/MyApp/View
ビュークラス。ユーザーインターフェースとなる部分を担う。
アプリケーションにおいて最終的な出力を担当するコンポーネント。
標準的なHTMLページはもちろん、JSON、CSV、あるいは電子メールのような特殊なフォーマットまで自在に出力できるのが特徴。
・lib/MyApp.pm
アプリケーションクラス。アプリケーションクラスとは、アプリケーション共通の設定や使用するプラグインの登録などを行うためのクラス。
・myapp.conf
?
・myapp. psgi
?
・root
テンプレートや設定ファイル用のフォルダ。rootとは、枝分かれ構造の根の部分のことを指す。
・root/favicon.ico
Favicon。Faviconとはホームページのシンボル(アイコン)として使われる、画像ファイルのこと。
・root/static
静的ファイル用のフォルダ。
・root/static/images
画像用のフォルダ。
・script
ヘルパースクリプト。ヘルパースクリプトとは、モジュール(コントローラやビュー、モデル)の作成、開発サーバの起動を行うためのスクリプト。
・script/myapp_cgi.pl
CGI用のスクリプト。
・script/myapp_create.pl
モジュール、ビュー、コントローラを作成するスクリプト。
・script/myapp_fastcgi.pl
FastCGI用のスクリプト。
・script/myapp_server.pl
開発サーバ用のスクリプト。
・script/myapp_test.pl
コマンドラインからのテスト用スクリプト。
・t
テストスクリプト用のフォルダ。
・t/01app.t
テストスクリプト。
・t/02pod.t
テストスクリプト。
・t/03podcoverage.t
テストスクリプト。
上記のようにまとめることで各ディレクトリの意義は大まかに掴めたが、各ファイル内で書かれているコードの意味を理解できていないのでそちらの理解も着実に進めていく必要があるよね。
また後日ブログで更新します。