Module::Setupで身に付けるよい習慣
あらすじ
前回[twitter:@hachiojipm]で行われた[twitter:@norry_gogo]さんのLTで次のような発言がありました。
自分が書いたPerlコードを添削してくれる人が周りにいなくて困ってる
添削したったでー
おすすめのファイル構成について
一枚岩のスクリプトだとテストがしづらいので普段から以下のようなディレクトリ構成にする事をおすすめします。
Your-Dist/script/*.pl Your-Dist/t/*.t Your-Dist/lib/**/*.pm Your-Dist/Makefile.PL
scriptはbinでも何でも良いと思います。こんな感じのディレクトリがいいと思います。
それを簡単につくるためのcpanモジュールがいくつかあるのですが、今回はその中からModule::Setupをおすすめします。
Module::Setupをインストール
cpanからModule::Setupをinstallします。
% cpanm Module::Setup
Module::Setupをインストールするとmoudule-setupというコマンドが使えるようになります。
% which module-setup /Users/okamura/perl5/perlbrew/perls/current/bin/module-setup
Makefile.PLに次のような行が記述されているとそこにperlで書かれたスクリプトが置かれるらしいです。
install_script('bin/module-setup');
スケルトンつくる
私は$HOME/project以下で作業する習慣があるので次のようにします*1
名前空間は適当です*2
% cd ~/project % module-setup Twitter::Reader % cd Twitter-Reader
[twitter:@norry_gogo]さんが作成したコードをもってきます。
% cd ~/project/Twitter-Reader % mkdir script % curl https://raw.github.com/gist/997598/2c05441a36fa9a8b7708afd5edf75e35e97b0d94/twitter_ril.pl -o script/read_it_later.pl
とりあえず実行してみる
とりあえず生で乾杯ぐらいの気持ちで以下の作業を行います。あらら、モジュールが入ってませんでした。
% cd ~/project/Twitter-Reader % perl -cw script/read_it_later.pl Can't locate Net/Twitter.pm in @INC ...
Net::Twitterが必要なのですが、私の環境には入っていませんでした。こういう事をさけるためにはMakefile.PLを以下のように追記します
% cat Makefile.PL use inc::Module::Install; name 'Twitter-Reader'; all_from 'lib/Twitter/Reader.pm'; requires 'Net::Twitter'; requires 'URI::Find'; requires 'Web::Scraper'; tests 't/*.t'; author_tests 'xt'; test_requires 'Test::More'; auto_set_repository; auto_include; WriteAll;
cpanmでinstallします。
% cpanm installdeps . okamura% cpanm --installdeps . --> Working on .Configuring /Users/okamura/project/Twitter-Reader ... OK ==> Found dependencies: Net::Twitter--> Working on Net::Twitter Fetching http://search.cpan.org/CPAN/authors/id/M/MM/MMIMS/Net-Twitter-3.17001.tar.gz ... OK Configuring Net-Twitter-3.17001 ... OK ==> Found dependencies: DateTime, Data::Visitor::Callback, ...(中略)...
ちなみにMakefile.PLの雛形はmodule-setupを使うと作成されますので便利です。
そして依存モジュールが全てinstallされている事を確認します。
:!perl -cw script/read_it_later.pl script/read_it_later.pl syntax OK
メソッドはlib/Twitter/Reader.pmにお引っ越し
とりあえずメソッドコピペします。
package Twitter::Reader; use 5.010; use strict; use warnings; our $VERSION = '0.01'; use Net::Twitter; use URI::Find; use Web::Scraper; use LWP::UserAgent; use YAML; use Scalar::Util 'blessed'; use Encode; sub get_list_statuses { my ($list, $page) = @_; my $statuses; my $success = 1; eval { $statuses = $nt->list_statuses({ user => $list->{user}, list_id => $list->{list_id}, per_page => 200, page => $page, since_id => $list->{since_id} }); }; if (my $err = $@) { die $@ unless blessed $err && $err->isa('Net::Twitter::Error'); $success = undef; } return ($statuses, $success); } sub find_uris_from { my $text = shift; state @uris; @uris = (); state $finder = URI::Find->new(sub{ my ($uri, $orig_uri) = @_; push @uris, $orig_uri; return $orig_uri; }); $finder->find(\$text); return @uris; } sub expand_uri { my $uri = shift; my $res = $ua->head($uri); return unless $res->is_success; return $res->request->uri; } sub get_html_title { my $uri = shift; state $scraper = scraper { process 'title', 'title' => 'TEXT'; }; my $html; eval { $html = $scraper->scrape(URI->new($uri)); }; return if $@; return "-- No title --" unless $html->{title}; return $html->{title}; } 1;
syntax checkをしてみます。たくさんエラーがでてきました。元のコードではget_list_statuses(), expand_uri(), get_html_title()がそれぞれスコープ外のグローバル変数を読み込んでいたのでメソッドを移動した際にふがふがぎゃふんとなった事が原因のようです。
:!perl -cw lib/Twitter/Reader.pm Array found where operator expected at lib/Twitter/Reader.pm line 17, at end of line (Missing operator before ?) Variable "$scraper" is not imported at lib/Twitter/Reader.pm line 36. (Did you mean &scraper instead?) Variable "$scraper" is not imported at lib/Twitter/Reader.pm line 41. (Did you mean &scraper instead?) syntax error at lib/Twitter/Reader.pm line 17, near "state @uris" Global symbol "@uris" requires explicit package name at lib/Twitter/Reader.pm line 17. Global symbol "@uris" requires explicit package name at lib/Twitter/Reader.pm line 17. Global symbol "$finder" requires explicit package name at lib/Twitter/Reader.pm line 18. Global symbol "@uris" requires explicit package name at lib/Twitter/Reader.pm line 20. Global symbol "$finder" requires explicit package name at lib/Twitter/Reader.pm line 23. Global symbol "@uris" requires explicit package name at lib/Twitter/Reader.pm line 24. Global symbol "$ua" requires explicit package name at lib/Twitter/Reader.pm line 29. Global symbol "$scraper" requires explicit package name at lib/Twitter/Reader.pm line 36. Global symbol "$scraper" requires explicit package name at lib/Twitter/Reader.pm line 41. lib/Twitter/Reader.pm had compilation errors.
ひとまず引数として受け取るように改造してみます。第一引数に$classとしているのは特に意味はありません。
Twitter::Reader::find_uris_from($text,@uris)と書くよりもTwitter::Reader->find_uris_from($text,@uris)と書くのが個人的に好きだからです。
package Twitter::Reader; use 5.010; use strict; use warnings; our $VERSION = '0.01'; use Net::Twitter; use URI::Find; use Web::Scraper; use LWP::UserAgent; use YAML; use Scalar::Util 'blessed'; use Encode; sub get_list_statuses { my ( $class, $nt, $list, $page ) = @_; my $statuses; my $success = 1; eval { $statuses = $nt->list_statuses( { user => $list->{user}, list_id => $list->{list_id}, per_page => 200, page => $page, since_id => $list->{since_id} } ); }; if ( my $err = $@ ) { die $@ unless blessed $err && $err->isa('Net::Twitter::Error'); $success = undef; } return ( $statuses, $success ); } sub find_uris_from { my ( $class, $text ) = @_; state @uris; @uris = (); state $finder = URI::Find->new( sub { my ( $uri, $orig_uri ) = @_; push @uris, $orig_uri; return $orig_uri; } ); $finder->find( \$text ); return @uris; } sub expand_uri { my ( $class, $ua, $uri ) = @_; my $res = $ua->head($uri); return unless $res->is_success; return $res->request->uri; } sub get_html_title { my ( $class, $uri ) = @_; state $scraper = scraper { process 'title', 'title' => 'TEXT'; }; my $html; eval { $html = $scraper->scrape( URI->new($uri) ); }; return if $@; return "-- No title --" unless $html->{title}; return $html->{title}; } 1;
もう一回実行してみます。
:!perl -cw lib/Twitter/Reader.pm lib/Twitter/Reader.pm syntax OK
これでメソッドを外部に配置する事ができました。
script/read_it_late.plから外部モジュールのメソッドを呼ぶ
以下のようにscript/read_it_late.plを書き直します。
#!/usr/bin/env perl use 5.010; use strict; use warnings; use lib 'lib'; use Twitter::Reader; use Net::Twitter; use URI::Find; use Web::Scraper; use LWP::UserAgent; use YAML; use Scalar::Util 'blessed'; use Encode; use Data::Dumper; my $config_uri ='conf/conf.yml'; my $config = YAML::LoadFile($config_uri); my $nt = Net::Twitter->new( traits => [qw/API::REST API::Lists/], ); my $read_it_later = URI->new('https://readitlaterlist.com/v2/add'); my $ua = LWP::UserAgent->new; for my $list ( @{$config->{lists}} ) { my $page = 1; my $start_since_id = $list->{since_id}; my $new_since_id = $start_since_id; LOOP_PAGE: while (1) { my ($statuses, $success) = Twitter::Reader->get_list_statuses($nt,$list, $page); $new_since_id = $start_since_id unless $success; last LOOP_PAGE unless @$statuses; for my $status (reverse @$statuses) { warn $status->{id}; my @uris = Twitter::Reader->find_uris_from($status->{text}); for my $uri (@uris) { my $expand_uri = Twitter::Reader->expand_uri($ua,$uri); next unless $expand_uri; warn my $html_title = Twitter::Reader->get_html_title($expand_uri); next unless $html_title; $read_it_later->query_form( apikey => $config->{read_it_later}{apikey}, username => $config->{read_it_later}{username}, password => $config->{read_it_later}{password}, url => $expand_uri, title => sprintf "[TW]%s@%s / %s\n", $list->{list_name}, $status->{user}{screen_name}, $html_title, ); my $res; eval { $res = $ua->head("$read_it_later"); }; if ( $@ ) { warn $@; next; } if ($res->is_success) { printf "[TW]%s@%s / %s (%s)\n", $list->{list_name}, $status->{user}{screen_name}, encode('utf-8', $html_title), $expand_uri; } } $new_since_id = $status->{id} if $new_since_id < $status->{id}; } $page++; } $list->{since_id} = $new_since_id; } YAML::DumpFile($config_uri, $config); exit();
確認
% perl -cw script/read_it_later.pl script/read_it_later.pl syntax OK
昔書いたPOST,GETの違いについて追記した
たいした事ではないのですが、GETとPOSTではアクセスログにパラメータが記録されているのでそこの点でもちがうよね、というのに突如気づいた。
http://d.hatena.ne.jp/okamuuu/20100525/1274800489
そんだけ。
shipitのはじめかた
あらすじ
macbookairを新調してから初めてshipitコマンドを実行したんですが、いろいろと忘れている事があったので備忘録を書いておきます。
Shipitを用意
インストールします。
% cpanm ShipIt
設定ファイルを用意します。
% vi ~/project/your-Dist/.shipit steps = FindVersion, ChangeVersion, CheckChangeLog, DistTest, Commit, Tag, MakeDist, UploadCPAN git.tagpattern = %v git.push_to = origin
cpan-upload-httpを用意
インストール
% cpanm cpan-upload-http
設定ファイル
% vi ~/.pause user your-account password ********* mailto = your-email-address
MANIFESTがないと怒られたら
MANIFESTは次のコマンドで作成できます。
% make manifest
shipit
ここまできたらあとは実行するだけです。
% shipit
shipitが途中で失敗したら
gitにtagを作成するところまで進んでcpanへのアップロードが失敗した場合にもう一度shipitをやり直したいのでtagを消したいという場合は次のような操作をします。
最初にローカルのタグを削除します。
% git tag -d 0.05
削除したことをmasterに伝えます。
% git push origin :refs/tags/0.05
テスト駆動開発のすすめ
hachiojipmに行ってきたのですが#4でも#5でもTestを書くのが難しいという声が聞こえたので「テストは書いてみると簡単」「テストがあると開発が楽」という事を伝えてみようと努力する試みです。
ということでサンプルコードを書いてみました。
https://github.com/okamuuu/Sample-Plack-Test
おもむろにt/web.tとかつくってみる
最初にテストを書いてみましょう。
#!/usr/bin/env perl use strict; use warnings; use Test::Most; use Plack::Test; use HTTP::Request::Common qw/GET POST/; my $app = sub { return [ 200, [ "Content-Type" => "text/plain", "Content-Length" => 31 ], ["hello, I'm okamuuu. Thank you!!"] ]; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->( GET "/" ); is $res->code, 200; is $res->header('Content-Type'), 'text/plain'; is $res->content, "hello, I'm okamuuu. Thank you!!"; }; done_testing();
テスト実行。もちろん成功
% prove -vl t/web.t t/web.t .. ok 1 ok 2 ok 3 1..3 ok All tests successful.
t/web.tを書き換える
では実際にcode-refを返すシンプルな俺々WaFをつくってみましょう。
以下のようにちょっと書き換えます。
#!/usr/bin/env perl use strict; use warnings; use Test::Most; use Plack::Test; use HTTP::Request::Common qw/GET POST/; use_ok('MyApp::Web::Handler'); my $app = MyApp::Web::Handler->app(); test_psgi $app, sub { my $cb = shift; my $res = $cb->( GET "/" ); is $res->code, 200; is $res->header('Content-Type'), 'text/plain'; is $res->content, "hello, I'm okamuuu. Thank you!!"; }; done_testing();
テスト実行。もちろん失敗
:!prove -vl t/web.t t/web.t .. not ok 1 - use MyApp::Web::Handler; <省略>
lib/MyApp/Web/Handler.pmを追加
まずはuse_okのテストを通してみましょう。以下のようにuseできるだけのパッケージをつくります。
package MyApp::Web::Handler; use strict; use warnings; 1;
テスト実行。use_okはできているのですが、done_testingに到達するまえにプログラムがdieしています。まあそんなメソッドないですからねえ。
:!prove -vl t/web.t t/web.t .. ok 1 - use MyApp::Web::Handler; Can't locate object method "app" via package "MyApp::Web::Handler" at t/web.t line 21. # Tests were run but no plan was declared and done_testing() was not seen. <省略>
lib/MyApp/Web/Handler.pmにメソッドを追加
code-refを返すメソッドを追加してあげます。
package MyApp::Web::Handler; use strict; use warnings; sub app { return sub { [ 200, [ "Content-Type" => "text/plain", "Content-Length" => 31 ], ["hello, I'm okamuuu. Thank you!!"] ]; }; } 1;
テスト実行。成功します。こんなテストでも通ると気分がよくなります。
:!prove -vl t/web.t t/web.t .. ok 1 - use MyApp::Web::Handler; ok 2 ok 3 ok 4 1..4 ok All tests successful.
テストがあれば安心して改修できます。
MyApp::Web::Handler->appが返すcode-refはPlackからハッシュリファレンスを受け取っています。
このハッシュリファレンスからユーザーが何を要求しているのか読み解くことになるます。
なのですがMyApp::Web::Handlerはかまわずに配列リファレンスを返しています。ちょっと改造します。
package MyApp::Web::Handler; use strict; use warnings; use Plack::Request; sub app { return sub { my $env = shift; my $request = Plack::Request->new($env); my $response = $request->new_response(200); $response->content_type('text/plain'); $response->body("hello, I'm okamuuu. Thank you!!"); return $response->finalize(); }; } 1;
テスト実行。成功しました。テストがあればコードを書き直した後のチェックが早くて便利ですね。
:!prove -vl t/web.t t/web.t .. ok 1 - use MyApp::Web::Handler; ok 2 ok 3 ok 4 1..4 ok All tests successful.
「遊ぼう」と言えば 「遊ぼう」と答え 「好き」と言えば 「好き」と答える。
いまのままだと「遊ぼう」と言っても「好き」と言っても「ばか」って言っても全部「おっす。おれokamuuu。ありがとよ!!」としか答えません。
とりあえず「またね」と言われたら「またね!!」って返事するようにしましょう。テストを書き換えます。
#!/usr/bin/env perl use strict; use warnings; use Test::Most; use Plack::Test; use HTTP::Request::Common qw/GET POST/; use_ok('MyApp::Web::Handler'); my $app = MyApp::Web::Handler->app(); test_psgi $app, sub { my $cb = shift; my $res = $cb->( GET "/" ); is $res->code, 200; is $res->header('Content-Type'), 'text/plain'; is $res->content, "hello, I'm okamuuu. Thank you!!"; }; test_psgi $app, sub { my $cb = shift; my $res = $cb->( GET "/bye" ); is $res->code, 200; is $res->header('Content-Type'), 'text/plain'; is $res->content, "bye bye!!"; }; done_testing();
テストを実行するとやっぱり失敗してしまいました。
:!prove -vl t/web.t t/web.t .. ok 1 - use MyApp::Web::Handler; ok 2 ok 3 ok 4 ok 5 ok 6 not ok 7 # Failed test at t/web.t line 27. # got: 'hello, I'm okamuuu. Thank you!!' # expected: 'bye bye!!' 1..7 # Looks like you failed 1 test of 7.
ふりわける
ユーザーのリクエストに応じてどのメソッドを呼べばいいのか判定する処理を書けば良い気がします。なんかそんなのをつくります*1;
MyApp::Web::Handlerを作成します。
package MyApp::Web::Handler; use strict; use warnings; use MyApp::Web::Router::Sinatraish; use Plack::Request; sub app { return sub { my $env = shift; my $router = MyApp::Web::Router::Sinatraish->router; my $route = $router->match($env) or return [ 404, [], ['not found'] ]; my $request = Plack::Request->new($env); my $response = $route->{code}->($request); return $response->finalize(); }; } 1;
それからMyApp::Web::Router::Sinatraishを作成します。
package MyApp::Web::Router::Sinatraish; use strict; use warnings; use Router::Simple::Sinatraish; get '/' => sub { my $request = shift; my $response = $request->new_response(200); $response->content_type('text/plain'); $response->body("hello, I'm okamuuu. Thank you!!"); return $response; }; get '/bye' => sub { my $request = shift; my $response = $request->new_response(200); $response->content_type('text/plain'); $response->body("bye bye!!"); return $response; }; 1;
テストすると成功します。
:!prove -vl t/web.t t/web.t .. ok 1 - use MyApp::Web::Handler; ok 2 ok 3 ok 4 ok 5 ok 6 ok 7 1..7 ok All tests successful.
まとめ
そんなこんなで少しずつWaFっぽくなってきたと思います。このようにテストを少しずつ変化させながら開発したりすると実装に集中することができるのですごく楽です。
ちなみに私はviからproveコマンドを呼んでいます*2。
そんなこんなでテストを書くのが難しい、という声を聞きますが、私の場合は逆にこうやって開発したほうが手間がかからないので楽だと思います。
だからみんなもテストを書いてみませんか?
Hachioji.pm #5
前回に引き続き八王子に行ってきました。
ごはん
http://www.hotpepper.jp/strJ000765160/
とりあえず焼き肉屋さんで楽しく食事できたのでよかった。
んー晩ごはんにビール飲みながら焼き肉たべれるなんて幸せの極みだなーと思った。
ちょっとLTができる状況でなかったのでタイ料理屋さんで2次会+LTやりました。
http://www.thailanna.khattiya.jp/index.html
このお店の店員は感じよかったなー。
タイ行ってみたくなってしまった。
LT
[twitter:@umeyuki]さん
メモをとるのに便利なアプリを紹介。だったかな。
アプリに自分のメールアドレスを登録しておいてすぐに送信しちゃうとか、なんかそんなの*1
うーん。全然お話できませんでした。
[twitter:@hondalica]さん
メタリカ好きなプログラマさん。過去のhachioji.pmで行った店の位置を取得してごにょごにょ*2
どうやらお店の設定がかごしま食肉であるはずが想定外の2次会突入のためデモのときちょっと戸惑ってました。
[twitter:@partynight12th]さん
Webkit系のブラウザでCSSをごにょごにょ*3。
発表するためのパワポを発表しながら作成するという、前代未聞のライブコーディングを披露されてました。
[twitter:@nekoya]さん
cobblerっていうOSインストールのご案内。サーバーを20台まとめてセットアップする時にすごい楽だそうです。
ちなみに以前は京都にお住まいだったそうです。
[twitter:@hide_o_55]さん
groonga+node.jsのデモ。groonga使ってるようなので今度話しかけよう。
[twitter:@ytnobody]さん
ごめんなさい。がまんできなかったのでトイレに行ってました。
帰ってきたら終わってました。
就職おめでとうございます。その会社、俺も一回応募したことあるんだぜ…
[twitter:@hirobanex]さん
「Smart::Argsとかおすすめです。よくわかんないけど」とよくわかんないけど軽妙なトークでした。Smart::Argsがおすすめなのはなんだろうと思って聞いてみたらその人の前職(ry
[twitter:@equinox79]さん
Chrome Extension「Twitter Search It」はこの人がつくりました。
[twitter:@mgiken]さん
GANCの歴史は彼からはじまった。発表内容はArcでエディタを操作。それviコマンド呼んでるだけ(ry
いつのまにかGANちゃんと呼ばれていました。
アニメ好きな青年でLT以外の会話がやや残念ですが、LTはすごくまじめな事をしゃべってました。
諸事情で新潟に引き返すそうです。Niigata.pmを作りたい、と。新潟にもPerl使いはいるので結束したいとの事。
個人的には今回のLTで一番良いトークだと思いました。
前後の話がやや残念でしたけど^^
[twitter:@takeR14a]さん
未来のコミッター。焼き肉屋で同じ席でした。ALIX boardという小型サーバー用途のなんかそんなのを紹介。ドライバーと筐体をnekoyaさんに渡してました。ねじ、なくしてなければいいけど^^
#3でお隣にすわってました。SeasaaのPerl Mongerさん。電子書籍の出版・販売サービス「forkN」を紹介。Seasaaって給料おいくらかしら?
[twitter:@norry_gogo]さん
カメラが欲しいそうです。海中から魚を撮った写真を見せてくれました。uzullaさんとカメラの話をしてました。uzulaさんはクラブイベントで写真撮ってるそうな。
前回のお題「今度作りたいもの」で作ってきたものを発表。Twitterのなんかそんなの。Twitterの機能をよくわかってないので説明できません><
自分が書いたPerlコードを添削してくれる人が周りにいなくて困ってるらしいです。テストこれから書いていきたいけど書くのが難しいと悩んでました。
テスト入門的な記事書こうかなー。
[twitter:@okamuuu]
俺のターン。「HTML+CSS+jQuery」フロント側の技術が最近いいなとおもったので紹介しました。資料は以下。前回の反省を生かして1分で終わるぐらいの分量にしました。
http://okamuuu.github.com/AboutjQuery/slides.html
[twitter:@uzulla]さん
このイベントの主催者です。PHPオンリーなDotCloud(?)を紹介。いろいろ手間取りながらも5分ぐらいでDB接続までできてました。
[twitter:@maka2_donzoko]さん
お約束ですが遅れて登場。盛大な拍手で迎えられてました。周りの客はみんな怪訝そうな顔をしてました。まあそれぐらい盛大な拍手です。
最近いいなー。さいきんいいなー。さい…
PlackとGroongaでblog作ってるときにつまづいたこと
groongaでboolなカラムをfalseに変更する場合falseとかnullとか0を渡せばいいのですが、なぜかtrueになるのでなんだろう、という話です。
結論
Plack::Request->parametersから受け取ったハッシュが以下のようになっている
$VAR1 = { '_key' => 'ho', 'name' => 'ge', 'display_fg' => '0' };
0が数値ではなくて文字列の'0'になってました。こいつをJSON::decode_jsonにかけるとこーなるからそーなる、という話でした。
{"display_fg":"0","name":"ge","_key":"ho"}
ひとまずWeb::Controllerの中に次のような記述をしておいた
if ( not $params{display_fg} ) { $params{display_fg} = 0; }
Plack::Requestがparametesのvalueを数値で返してくれたらうれしいのですが、1が数値か文字かを判断するのは開発者側の決める問題なんでしょうね。
Router::Simple悩み中
※追記あり
以下のようにRouter::SimpleをつかうとUserのRequestから実行すべきController、actionを判定できます。
かつ、ユーザーがどの年月を指定しているかといった情報も知る事ができます。
use strict; use warnings; use Router::Simple; use Data::Dumper; my $router = Router::Simple->new(); $router->connect( '/blog/{year}/{month}', { controller => 'Blog', action => 'monthly' } ); my $env = { 'PATH_INFO' => '/blog/2011/05', 'REQUEST_METHOD' => 'GET', }; my $match = $router->match($env); print Dumper $match; =result $VAR1 = { 'controller' => 'Blog', 'month' => '05', 'action' => 'monthly', 'year' => '2011' }; =cut 1;
なのですが、自分で書いたWeb::Controllerは次のようにcapturesをハッシュではなく、配列で渡したい。
My::Web::Controller::Blog->monthly($c, @captures);
package My::Web::Controller::Blog; sub monthly { my ( $class, $c, $year, $month ) = @_; ... }
つまりRouter::Simple::Route->match($env)にはこうした情報を返してほしい
$VAR1 = { 'controller' => 'Blog', 'month' => '05', # 不要 'action' => 'monthly', 'year' => '2011' # 不要 'captures' => ['2010', '05'], };
Router::Simple::Route->matchが返すハッシュリファレンスにcapturesを加えると最も簡単。
以下'0.09'のRouter::Simple::Routeの108行目から下の行に次のような処理を加えたい。
if ( @captured ) { $match->{captures} = [@captured]; }
ただし、そうすると元々あったテストが次々とこける。
t/01_simple.t .. not ok 1 # Failed test at t/01_simple.t line 15. # Structures begin differing at: # $got->{captures} = ARRAY(0x1008317a8) # $expected->{captures} = Does not exist
うーん。元々のテストに影響を与えずにcaputresを配列で取得したい場合はどうすれば良いだろう。
ついでにcapturesを配列で受け取る場合はこれらをhash化する必要はないので記述を省略したい。
追記(2011-05-22 20:00:00)解決しました。
splatというものを使うと配列で記憶します。以下のように書き換えます。
$router->connect( '/blog/*/*', { controller => 'Blog', action => 'monthly' } );
$VAR1 = { 'controller' => 'Blog', 'splat' => [ '2011', '05' ], 'action' => 'monthly' };
08_splat.tに書いてあったorz