The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

URI::Dispatch - determine which code to execute based upon path

SYNOPSIS

    my $dispatch = URI::Dispatch->new();
    $dispatch->add( '/', 'Homepage' );
    
    # common matching patterns are available
    $dispatch->add( '/user/#id', 'Profile' );
    
    # optional parts of the path
    $dispatch->add( '/article/#id[/#slug]', 'Article' );
    
    # named captures
    $dispatch->add( '/tag/#name:slug', 'Tag' );
    
    # use a custom regexp
    $dispatch->add( '/a-z/#letter:([a-z])', 'AZ::Page' );
    
    # pass in a path and determine what matches
    my( $handler, $options) 
        = $dispatch->handler( '/article/5/awesome-article' );
    # handler='Article', options=['5','awesome-article']
    
    # automatically calls Tag::get (as that matches the path)
    my $response = $dispatch->dispatch( '/tag/perl' );
    
    # construct paths
    my $uri = $dispatch->url( 'article', [ '1', 'some-article' ] );
    # uri='/article/1/some-article'

METHODS

add( path, handler [, name] )

Add path that can be handled by handler, with an optional symbolic name. The $path string will be matched literally, except for the special markers described below. They have been specially chosen because they are not legal URI path characters, so should never break your actual chosen URI scheme.

captures

To capture part of the path for later use, mark it with a hash (#) and the capture type. Builtin types are:

id

matches digits

hex

matches digits and the letters a, b, c, d, e, and f case insensitively

slug

matches lowercase letters, digits and hyphens

year

matches four digits

month

matches numbers 01 through 12

day

matches numbers 01 through 31

date

matches year, month and day separated by dashes

hour

matches numbers 00 through 23

minute

matches numbers 00 through 59

second

matches numbers 00 through 59

time

matches hour, minute and second separated by any of colons, periods or dashes

*

matches anything

0

matches nothing, an empty match; useful for binding to things like homepage matches with optional parameters (eg. '/#0[page-#id/]').

(regexp)

matches a custom regular expression

named captures

Rather than relying on the order of the captures, they can be named. The name goes immediately after the hash (#), is formed of "word" characters (alphanumeric plus underscore) and is followed by a colon and then the capture type. Some examples:

  • #id:id

  • #title:slug

  • #letter:([a-z])

optional segments

To mark part of the path as optional, surround it with square brackets. Optional segments cannot be nested.

Limitations

Different handlers having the same path and multiple handlers having different paths will result in unpredictable behaviour.

handler( path )

Determine which handler should be used for the given path. The handlers are examined in the same order that they were given with add().

Returns the handler string, and either an array of the captured elements, or a hash if the captures were named. For example, this code:

    $dispatch->add( '/article/#key:id/#title:slug', 'article' );
    my( $handler, $captures )
        = $dispatch->handler( '/article/5/awesome-article' );

would return $captures set to:

    {
        key   => '5',
        title => 'awesome-article',
    }

dispatch( path or request, [ ... ] )

Call the handler that matches the given argument, which can either be a simple string that represents a path, or it can be a Plack::Request object. The handlers are examined in the same order that they were given with add().

The handler is interpreted as a class, and the HTTP method is the subroutine within the class to call.

Any extra arguments to dispatch() are passed to the handler routine first, then the reference to the array of captures or hash of named captures.

path string

When dispatch() is called with a simple string, the method is assumed to be an HTTP GET. For example:

    $dispatch->add( '/tag/#name:slug', 'Tags::SingleTag' );
    my $response = $dispatch->dispatch( '/tag/perl', $obj );

would set $response to the return value of

    Tags::SingleTag::get( $obj, { name => 'perl' } );

Plack::Request

When dispatch() is called with a Plack::Request object, the path and method are determined automatically; and the object is passed to the handler before the captures, but after any extra arguments to dispatch(). For example:

    $dispatch->add( '/tag/#name:slug', 'Tags::SingleTag' );
    
    # $env contains the environment of an HTTP DELETE 
    # request on /tag/perl
    my $request  = Plack::Request->new( $env );
    my $response = $dispatch->dispatch( $request, $obj );

would set $response to the return value of

    Tags::SingleTag::delete( $obj, $request, { name => 'perl' } );

Hash reference

When dispatch() is called with a hash reference, the path, method and request objects are taken from that hash. Method defaults to GET if it is not specified; request is optional.

As for a Plack::Request object, the request object is passed to the handler before the captures, but after any extra arguments to dispatch(). For example:

    $dispatch->add( '/tag/#name:slug', 'Tags::SingleTag' );
    my $response = $dispatch->dispatch(
        {
            path    => '/tag/perl',
            method  => 'HEAD',
            request => $request_obj,
        },
        'extra_arg',
    );

would set $response to the return value of

    Tags::SingleTag::head( 'extra_arg', $request_obj, { name => 'perl' } );

url( handler or name, $arguments )

Build a path that would be accepted by the route specified by name (or handler if it wasn't added with a name parameter). When the path contains captures, you can pass them as an arrayref (or hashref if they are named captures).

The $arguments are tested to ensure they would match. If they would not, an Ouch exception is thrown. This can be caught in your code like so:

    use Ouch qw( :traditional );
    
    ...
    
    $dispatch->add( '/list/#letter:([a-z])', 'az-page' );
    try { $url = $dispatch->url( 'az-page', 'too big' ); };
    if ( catch 'wrong_input' ) {
        # handle errors
    }

EXCEPTIONS

cannot_mix

Named and positional captures cannot be mixed. An attempt to do so will throw this exception.

unmatched_brackets

Thrown if the opening and closing square brackets representing optional segments of a path do not match up.

wrong_input

A provided argument when calling url() will not match the relevant capture type.

args_short

Not enough arguments are provided when calling url().

args_wrong

The wrong type of arguments (arrayref versus hashref) were provided when calling url().

no_param

An unknown builtin parameter type was requested.

404

No handler was found for the path when calling dispatch().

AUTHOR

Mark Norman Francis, norm@cackhanded.net.

COPYRIGHT AND LICENSE

Copyright 2011 Mark Norman Francis.

This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 388:

Expected text after =item, not a number