[perl] URI::QueryParam の query_form_hash でappendしたい
Webアプリケーションなどで LWP で REST API を叩く場合に、動的にURLを作ったりする事がよくあります(他にも色々ありますよね)。今までは sprintf 等でシコシコ文字列を作っていたりしたのですが、 URI::QueryParam というモジュールが便利だと id:boxphere さんから教わって今はそのモジュールを使っています。
#!/usr/bin/perl use strict; use warnings; use URI; use URI::QueryParam; my $uri1 = URI->new('http://bonar.jp'); $uri1->query_param(foo => 'val_foo'); $uri1->query_param(bar => 'val_bar'); print "$uri1\n"; # http://bonar.jp?foo=val_foo&bar=val_bar
sprintf('%s/hoge.pl?foo=%s&bar=%s', $uri, @data) みたいにやるよりずっと読みやすくていいですね。ただ、query_param だと1個ずつしか指定できないですが、query_form_hash を使うとhashで一気にセットできます。
my $uri2 = URI->new('http://bonar.jp'); $uri2->query_form_hash( foo => 'val_foo', bar => 'val_bar', ); print "$uri2\n"; # http://bonar.jp?foo=val_foo&bar=val_bar
見た目にもすごくわかりやすいですよね。ただ、このメソッドはちょっと癖があって、hashでセットするとそのhashで内部のパラーメタが置き換えられてしまい、以前にセットしたものが消えてしまいます。
my $uri3 = URI->new('http://bonar.jp'); $uri3->query_form_hash( foo => 'val_foo', bar => 'val_bar', ); $uri3->query_form_hash( hoge => 'val_hoge', buzz => 'val_buzz', ); print "$uri3\n"; # http://bonar.jp?hoge=val_hoge&buzz=val_buzz
中のソースを見るとこんな感じです。
sub URI::_query::query_form_hash { my $self = shift; my @old = $self->query_form; if (@_) { $self->query_form(@_ == 1 ? %{shift(@_)} : @_); } my %hash; while (my($k, $v) = splice(@old, 0, 2)) { if (exists $hash{$k}) { for ($hash{$k}) { $_ = [$_] unless ref($_) eq "ARRAY"; push(@$_, $v); } } else { $hash{$k} = $v; } } return \%hash; }
$self->query_form(@_ == 1 ? %{shift(@_)} : @_); の部分がまさにそれですね。
何かのURIを扱うモジュール等で、オブジェクト生成時に基本となるパラメータをセットしておいて、呼び出し元でいくつかの個別のパラメータをappendした、みたいなよくありがちな局面で query_form_hash が使いづらかったりします。
今あるものに加えて新しいhashを追加するような関数があるといいのになあ。例えばこんなショートカットとか。
sub URI::query_form_hash_append { my $self = shift; return $self->query_form_hash(%{ $self->query_form_hash }, @_); } my $uri4 = URI->new('http://bonar.jp'); $uri4->query_form_hash_append( foo => 'val_foo', bar => 'val_bar', ); $uri4->query_form_hash_append( hoge => 'val_hoge', buzz => 'val_buzz', ); print "$uri4\n"; # http://bonar.jp?bar=val_bar&foo=val_foo&hoge=val_hoge&buzz=val_buzz
こういう風に呼び出せると理想的。僕だけですかね。
query_param_append($key, @val) というそれっぽいメソッドがあるのですが、これは用途がまったく違って、$key=$val[0]&$key\$val[1]&$key=$val[2].... というパラメータが生成されます。こういう操作ってどういう需要があるんだろう。。
URI はパラメータの順番を保持する事を考慮した実装になっているみたいですが、これって何のためなんですかね。ベースとなるパスの部分とパラメータのhash、というシンプルな構造でURIを表現するモジュールってないのかなあ。