Author image John Drago

NAME

Router::Generic - A general-purpose router for the web.

DEPRECATED

Do not use for new code.

SYNOPSIS

Constructor

  use Router::Generic;
  
  my $router = Router::Generic->new();
  

Simple Route

  $router->add_route(
    name      => 'Simple',
    path      => '/foo/bar',
    target    => '/foobar.asp',
  );
  
  $router->match('/foo/bar/');  # /foobar.asp

Simple Named Capture

  $router->add_route(
    name      => 'Zipcodes',
    path      => '/zipcodes/:code',
    target    => '/zipcode.asp'
  );
  
  $router->match('/zipcodes/90210/');  # /zipcode.asp?code=90210
  $router->match('/zipcodes/80104/');  # /zipcode.asp?code=80104
  $router->match('/zipcodes/00405/');  # /zipcode.asp?code=00405

Another way to spell the same thing

  $router->add_route(
    name      => 'Zipcodes',
    path      => '/zipcodes/{code}',
    target    => 'zipcode.asp'
  );

Eager Named Capture

  $router->add_route(
    name      => 'Eager',
    path      => '/stuff/{*Whatever}',
    target    => '/yay.asp',
  );
  
  $router->match('/stuff/blah/');     # /stuff/blah
  $router->match('/stuff/a/b/c');     # /yay.asp?Whatever=a%2Fb%2Fc - (a/b/c escaped)

Named Capture with RegExp

  $router->add_route(
    name      => 'ZipcodeRegexp',
    path      => '/zipcodesRegExp/{code:\d{5}}',
    target    => '/zipcode.asp'
  );
  
  $router->match('/zipcodesRegExp/90210/');   # /zipcode.asp?code=90210
  $router->match('/zipcodesRegExp/80104/');   # /zipcode.asp?code=80104
  $router->match('/zipcodesRegExp/00405/');   # /zipcode.asp?code=00405

More Interesting

  $router->add_route(
    name      => 'WikiPage',
    path      => '/:lang/:locale/{*Article}',
    target    => '/wiki.asp',
    defaults  => {
      Article => 'Home',
      lang    => 'en',
      locale  => 'us',
    }
  );
  
  $router->match('/en/us/');              # /wiki.asp?lang=en&locale=us&Article=Home
  $router->match('/fr/ca/');              # /wiki.asp?lang=fr&locale=ca&Article=Home
  $router->match('/en/us/Megalomania');   # /wiki.asp?lang=en&locale=us&Article=Megalomania

Route with Default Values

  $router->add_route({
    name      => 'IceCream',
    path      => '/ice-cream/:flavor',
    target    => '/dessert.asp',
    defaults  => {
      flavor  => 'chocolate'
    }
  });

Fairly Complex

  $router->add_route(
    name      => "ProductReviews",
    path      => '/shop/{Product}/reviews/{reviewPage:\d+}',
    target    => '/product-reviews.asp',
    defaults  => {
      reviewPage  => 1,
    }
  );
  
  $router->match('/shop/Ford-F-150/reviews/2/');  # /product-reviews.asp?Product=Ford-F-150&reviewPage=2

List of Targets

As of version 0.006 you can also pass an arrayref of targets and get them all back, parameterized just the same:

  $router->add_route(
    name      => "Bank",
    path      => '/banks/:city',
    target    => [ '/bank-[:city:].asp', '/bank-generic.asp' ],
    defaults  => {
      reviewPage  => 1,
    }
  );
  
  # Scalar context returns an arrayref when there are multiple targets:
  my $matches = $router->match('/banks/Dallas/');
  print $matches->[0];  # /bank-Dallas.asp?city=Dallas
  print $matches->[1];  # /bank-generic.asp?city=Dallas
  
  # List context returns a list:
  my @matches = $router->match('/banks/Dallas/');
  print $matches[0];    # /bank-Dallas.asp?city=Dallas
  print $matches[1];    # /bank-generic.asp?city=Dallas

* This whole contextual-return-types thing started up in v0.007.

Get the URI for a Route

  my $uri = $router->uri_for('IceCream');
  print $uri;   # /ice-cream/chocolate/
  
  my $uri = $router->uri_for('/ProductReviews', {
    Product => 'Tissot-T-Sport',
    reviewPage  => 3,
  });
  print $uri;   # /shop/Tissot-T-Sport/reviews/3/
  
  my $uri = $router->uri_for('WikiPage', {
    Article => 'Self-aggrandizement',
    lang    => 'en',
    locale  => 'ca',
  });
  print $uri;   # /en/ca/Self-aggrandizement/
  
  my $uri = $router->uri_for('Zipcodes', {
    code => '12345'
  });
  print $uri;   # /zipcodes/12345/

Get the Route for a URI

  my $route = $router->route_for('/banks/Dallas/');
  
  my $route = $router->route_for('/banks/Dallas/', 'POST');
  
  my $route = $router->route_for('/banks/Dallas/', 'GET');

DESCRIPTION

Router::Generic provides URL Routing for the web.

What is URL Routing?

URL Routing is a way to connect the dots between a URL you see in your browser and the page that the webserver should actually process. You could also say that URL Routing is a way to abstract the URL in the browser from the page that the webserver should actually process. It's all in your perspective.

URL Routing is valuable for search engine optimization (SEO) and for reducing work associated with moving files and folders around during the iterative process of building a website.

Anatomy of a Route

Every route must have a name, a path and a target. The defaults hashref is optional.

  • name (Required)

    The name parameter should be something friendly that you can remember and reference later.

    Examples are "Homepage" or "Products" or "SearchResults" - you get the picture.

  • path (Required)

    The path parameter describes what the incoming URL will look like in the browser.

    Examples are:

    /hello/:who

    Matches /hello/world/ and /hello/kitty/

    Does not match /hello/every/one/

    /colors/{Color}

    Matches /colors/red/ and /colors/not-really-a-color/

    Does not match /colors/red/white/blue/

    /options/{*Options}

    Matches /options/, /options/foo/ and /options/foo/bar/baz/bux/

    /areacodes/{Area:\d{3}}

    You can use regular expressions to restrict what your paths match.

    Matches /areacodes/303/ but does not match /areacodes/abc/

  • target (Required)

    The url that should be processed instead. Any string is accepted as valid input.

    So /ice-cream.asp and /flavor.asp?yay=hooray are OK.

  • defaults (Optional)

    The defaults parameter is a hashref containing values to be used in place of missing values from the path.

    So if you have a path like /pets/{*petName} and your defaults looks like this:

      $router->add_route(
        name    => 'Pets',
        path    => '/pets/{*petName}',
        target  => '/pet.asp',
        defaults => {
          petName => 'Spot',
        }
      );

    You get this:

      $router->match('/pets/');   # /pet.asp?petName=Spot

    And this:

      $router->uri_for('Pets');   # /pets/Spot/

    The defaults are overridden simply by supplying a value:

      $router->match('/pets/Fluffy/');  # /pet.asp?petName=Fluffy

    And this:

      $router->uri_for('Pets', {petName => 'Sparky'});  # /pets/Sparky/

    Similarly, if you have the following route:

      $router->add_route(
        name  => "DefaultsAlways",
        path  => "/",
        target  => "/index.asp",
        method  => "*",
        defaults  => {
          foo => "bar"
        }
      );

    You get this:

      $router->uri_for("DefaultsAlways"); # /?foo=bar
    
      $router->match('/');  # /index.asp?foo=bar

Caching

Router::Generic provides route caching - so the expensive work of connecting a uri to a route is only done once.

PUBLIC METHODS

add_route( name => $str, route => $str, [ defaults => \%hashref ] )

Adds the given "route" to the routing table. Routes must be unique - so you can't have 2 routes that both look like /foo/:bar for example. An exception will be thrown if an attempt is made to add a route that already exists.

Returns true on success.

match( $uri )

Returns the 'routed' uri with the intersection of parameters from $uri and the defaults (if any).

Returns undef if no matching route is found.

uri_for( $routeName, \%params )

Returns the uri for a given route with the provided params.

Given this route:

  $router->add_route({
    name      => 'IceCream',
    path      => '/ice-cream/:flavor',
    target    => '/dessert.asp',
    defaults  => {
      flavor  => 'chocolate'
    }
  });

You would get the following results depending on what params you supply:

  my $uri = $router->uri_for('IceCream');
  print $uri;   # /ice-cream/chocolate/
  
  my $uri = $router->uri_for('IceCream', {
    flavor  => 'strawberry',
  });
  print $uri;   # /ice-cream/strawberry/

route_for( $path, [ $method ] )

Returns the route matching the path and method.

LIMITATIONS

Before version 0.009 there were some limitations in the grammar that prevented paths like /wiki/:lang-:locale/{*Page} from picking up the :lang and :locale correctly. As of version 0.009 this works correctly.

SIMPLE CRUD EXAMPLE

  $router->add_route(
    name    => 'CreatePage',
    path    => '/main/:type/create',
    target  => '/pages/[:type:].create.asp',
    method  => 'GET'
  );
  
  $router->add_route(
    name    => 'Create',
    path    => '/main/:type/create',
    target  => '/handlers/dev.[:type:].create',
    method  => 'POST'
  );
  
  $router->add_route(
    name    => 'View',
    path    => '/main/:type/{id:\d+}',
    target  => '/pages/[:type:].view.asp',
    method  => '*',
  );
  
  $router->add_route(
    name      => 'List',
    path      => '/main/:type/list/{page:\d+}',
    target    => '/pages/[:type:].list.asp',
    method    => '*',
    defaults  => { page => 1 }
  );
  
  $router->add_route(
    name    => 'Delete',
    path    => '/main/:type/delete/{id:\d+}',
    target  => '/handlers/dev.[:type:].delete',
    method  => 'POST'
  );

This works great with ASP4.

ACKNOWLEDGEMENTS

Part of the path parsing logic was originally based on Router::Simple by Matsuno Tokuhiro http://search.cpan.org/~tokuhirom/ et al.

The path grammar is a copy of the route grammar used by ASP.Net 4.

AUTHOR

John Drago <jdrago_999@yahoo.com>

LICENSE

This software is Free software and may be used and redistributed under the same terms as any version of Perl itself.