日向夏特殊応援部隊

俺様向けメモ

URIとURI::fileの挙動

URIモジュールですが、良く使われるモジュールではあるのですが、
schemeを指定しないでURIモジュールでインスタンス化してから、改めてschemeを指定すると酷い結果になります。

use strict;
use warnings;

use Data::Dump qw(dump);
use URI;

local $\ = "\n";
my $u = URI->new("/usr/bin/perl");

print dump($u->scheme);
print dump($u->as_string);

$u->scheme("file");
print dump($u->scheme);
print dump($u->as_string);

で結果ですけど、

()
"/usr/bin/perl"
"file"
"file:/usr/bin/perl"

最後のas_stringが酷すぎる。。。
ちなみに最初からURI::fileでインスタンス化すると、

use strict;
use warnings;

use URI::file;

print URI::file->new("/usr/bin/perl")->as_string;

とやると結果は、

file:///usr/bin/perl

となります。まぁ、これはいいとして、、、URIモジュールのschemeの変更処理がまずいみたいですね。

sub scheme
{
    my $scheme = shift->_scheme(@_);
    return unless defined $scheme;
    lc($scheme);
}

とあるので、_schemeの実装を見ると、

sub _scheme
{
    my $self = shift;

    unless (@_) {
	return unless $$self =~ /^($scheme_re):/o;
	return $1;
    }

    my $old;
    my $new = shift;
    if (defined($new) && length($new)) {
	Carp::croak("Bad scheme '$new'") unless $new =~ /^$scheme_re$/o;
	$old = $1 if $$self =~ s/^($scheme_re)://o;
	my $newself = URI->new("$new:$$self");
	$$self = $$newself; 
	bless $self, ref($newself);
    }
    else {
	if ($self->_no_scheme_ok) {
	    $old = $1 if $$self =~ s/^($scheme_re)://o;
	    Carp::carp("Oops, opaque part now look like scheme")
		if $^W && $$self =~ m/^$scheme_re:/o
	}
	else {
	    $old = $1 if $$self =~ m/^($scheme_re):/o;
	}
    }

    return $old;
}

にある、

my $newself = URI->new("$new:$$self");

この部分。。。結果的に、

my $newself = URI->new("file:/usr/bin/perl");

って投げてるだけだし。。。

他のschemeについては保証出来ないけど、

my $newself = "URI::$new"->new($$self);

とすればいいんじゃないかなーと思う訳です。