URI-Builder

 view release on metacpan or  search on metacpan

lib/URI/Builder.pm  view on Meta::CPAN

our $VERSION = '0.04';

=head1 DESCRIPTION

This class is a close relative of L<URI>, but while that class is optimised
for parsing, this is optimised for building up or modifying URIs. To that end
objects of this class represent their URIs in sections, each of which are
independently mutable, that then need to be serialised to form a string. In
contrast, C<URI> uses a fully-formed string internally which must be parsed
afresh each time a mutation is performed on it.

At the moment only http and https URIs are known to work correctly, support
for other schemes may follow later.

=cut

use URI;
use Scalar::Util qw( blessed );
use Carp qw( confess );

# Utility functions
sub _flatten {
    return map {
        ref $_ eq 'ARRAY' ? _flatten(@$_)
      : ref $_ eq 'HASH'  ? _flatten_hash($_)
      : $_
    } @_ = @_;
}

sub _flatten_hash {
    my $hash = shift;

    return map {
        my ($k, $v) = ($_, $hash->{$_});
        $v = '' unless defined $v;
        map { $k => $_ } _flatten $v
    } keys %$hash;
}

use overload ('""' => \&as_string, fallback => 1);

=head1 ATTRIBUTES

The following attributes relate closely with the URI methods of the same
names.

=over

=item * scheme

=item * userinfo

=item * host

=item * port

=item * path_segments

=item * query_form

=item * query_keywords

=item * fragment

=back

In addition the C<query_separator> attribute defines how C<query_form> fields
are joined. It defaults to C<';'> but can be usefully set to '&'.

The accessors for these attributes have a similar interface to the L<URI>
methods, that is to say that they return old values when new ones are set.
Those attributes that take a list of values: C<path_segments>, C<query_form>
and C<query_keywords> all return plain lists but can be passed nested array
references.

=cut

my (@uri_fields, %listish, @fields);

BEGIN {
    # Fields that correspond to methods in URI
    @uri_fields = qw(
        scheme
        userinfo
        host
        port
        path_segments
        query_form
        query_keywords
        fragment
    );

    # Fields that contain lists of values
    %listish = map { $_ => 1 } qw(
        path_segments
        query_form
        query_keywords
    );

    # All fields
    @fields = ( @uri_fields, qw( query_separator ));

    # Generate accessors for all fields:
    for my $field (@fields) {
        my $glob = do { no strict 'refs'; \*$field };

        *$glob = $listish{$field} ? sub {
            my $self = shift;
            my @old = @{ $self->{$field} || []};
            $self->{$field} = [ _flatten @_ ] if @_;
            return @old;
        }
        : sub {
            my $self = shift;
            my $old = $self->{$field};
            $self->{$field} = shift if @_;
            return $old;
        };
    }
}

=head1 METHODS

=head2 new

The constructor.

In addition to the attributes listed above, a C<uri> argument can be passed as
a string or a L<URI> object, which will be parsed to popoulate any missing
fields.

    # a cpan URL without its path
    my $uri = URI::Builder->new(
        uri => 'http://www.cpan.org/SITES.html',
        path_segments => [],
    );

Non-attribute arguments that match other methods in the class will cause those
methods to be called on the object. This means that what we internally regard
as composite attributes can be specified directly in the constructor.

    # Implicitly populate path_segments:
    my $uri = URI::Builder->new( path => 'relative/path' );

Unrecognised arguments cause an exception.

=cut

sub new {
    my $class = shift;
    my %opts = @_ == 1 && ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;

    $opts{query_separator} ||= ';';

    if (my $uri = $opts{uri}) {
        $uri = $class->_inflate_uri($uri);

lib/URI/Builder.pm  view on Meta::CPAN


sub default_port { shift->_implementor->default_port }
sub secure       { shift->_implementor->secure       }

=head2 authority

Returns the 'authority' section of the URI. In our case this is obtained by
combining C<userinfo>, C<host> and C<port> together as appropriate.

Note that this is a read-only operation.

=cut

sub authority {
    my $self = shift;
    my ($user, $host) = ($self->userinfo, $self->host_port);

    return $host ? $user ? "$user\@$host" : $host : '';
}

=head2 host_port

Returns the host and port in a single string.

=cut

sub host_port {
    my $self = shift;
    my ($host, $port) = ($self->host, $self->port);

    return $host ? $port ? "$host:$port" : $host : '';
}

=head2 path

Returns the path portion of the URI as a string.

Can be assigned to to populate C<path_segments>.

Leading, trailing and doubled slashes are represented faithfully using empty
path segments.

=cut

sub path {
    my $self = shift;

    my $old = join '/', $self->path_segments;

    if (@_) {
        my @segments = split '/', shift, -1;
        $self->path_segments(@segments);
    }

    return $old;
}

=head2 query

Returns a string representation of the query. This is obtained from either
C<query_form> or C<query_keywords>, in that order.

If an argument is passed, it is parsed to populate C<query_form>.

=cut

sub query {
    my ($self, $query) = @_;

    my @new;
    if ($query) {
        # Parse the new query string using a URI object
        @new = URI->new("?$query", $self->scheme)->query_form;
    }

    unless (defined wantarray) {
        # void context, don't bother building the query string
        $self->query_form(@new);
        return;
    }

    my $old;
    if (my @form = $self->query_form) {
        push @form, '' if @form % 2;
        my $uri = URI->new;
        $uri->query_form(\@form, $self->query_separator);
        $old = $uri->query();
    }
    else {
        $old = join '+', $self->query_keywords;
    }

    $self->query_form(@new);

    return $old;
}

=head2 path_query

Returns a string representation of the path plus the query string. See
L<URI/path_query>.

=cut

sub path_query {
    my $self = shift;
    my ($path, $query) = ($self->path, $self->query);

    my $old = $path . ($query ? "?$query" : '');

    if (@_) {
        my $uri = URI->new($_[0]);
        $self->$_([ $uri->$_ ]) for qw( path_segments query_form );
    }

    return $old
}

=head2 query_param

    @keys       = $uri->query_param
    @values     = $uri->query_param($key)
    @old_values = $uri->query_param($key, @new_values);

This works exactly like the method of the same name implemented in
L<URI::QueryParam>.

With no arguments, all unique query field names are returned

With one argument, all values for the given field name are returned

With more than one argument, values for the given key (first argument) are set
to the given values (remaining arguments). Care is taken in this case to
preserve the ordering of the fields.

=cut

sub query_param {
    my ($self, $key, @values) = @_;
    my @form = $self->query_form;

    if ($key) {
        my @indices = grep $_ % 2 == 0 && $form[$_] eq $key, 0 .. $#form;
        my @old_values = @form[ map $_ + 1, @indices ];

        if (@values) {
            @values = _flatten @values;
            splice @form, pop @indices, 2 while @indices > @values;

            my $last_index = @indices ? $indices[-1] + 2 : @form;

lib/URI/Builder.pm  view on Meta::CPAN

=item * as_iri

=item * ihost

=back

=head1 LICENSE

L<perlartistic>

=cut

1;

__END__

canonical
default_port
via URI::_server: _host_escape
via URI::_server: _port
via URI::_server: _uric_escape
via URI::_server: as_iri
via URI::_server: host
via URI::_server: host_port
via URI::_server: ihost
via URI::_server: port
via URI::_server: uri_unescape
via URI::_server: userinfo
via URI::_server -> URI::_generic: _check_path
via URI::_server -> URI::_generic: _no_scheme_ok
via URI::_server -> URI::_generic: _split_segment
via URI::_server -> URI::_generic: abs
via URI::_server -> URI::_generic: authority
via URI::_server -> URI::_generic: path
via URI::_server -> URI::_generic: path_query
via URI::_server -> URI::_generic: path_segments
via URI::_server -> URI::_generic: rel
via URI::_server -> URI::_generic -> URI: (!=
via URI::_server -> URI::_generic -> URI: (""
via URI::_server -> URI::_generic -> URI: ()
via URI::_server -> URI::_generic -> URI: (==
via URI::_server -> URI::_generic -> URI: STORABLE_freeze
via URI::_server -> URI::_generic -> URI: STORABLE_thaw
via URI::_server -> URI::_generic -> URI: _init
via URI::_server -> URI::_generic -> URI: _init_implementor
via URI::_server -> URI::_generic -> URI: _obj_eq
via URI::_server -> URI::_generic -> URI: _scheme
via URI::_server -> URI::_generic -> URI: as_string
via URI::_server -> URI::_generic -> URI: clone
via URI::_server -> URI::_generic -> URI: eq
via URI::_server -> URI::_generic -> URI: fragment
via URI::_server -> URI::_generic -> URI: implementor
via URI::_server -> URI::_generic -> URI: new
via URI::_server -> URI::_generic -> URI: new_abs
via URI::_server -> URI::_generic -> URI: opaque
via URI::_server -> URI::_generic -> URI: scheme
via URI::_server -> URI::_generic -> URI: secure
via URI::_server -> URI::_generic -> URI::_query: equery
via URI::_server -> URI::_generic -> URI::_query: query
via URI::_server -> URI::_generic -> URI::_query: query_form
via URI::_server -> URI::_generic -> URI::_query: query_keywords



( run in 1.239 second using v1.01-cache-2.11-cpan-39bf76dae61 )