March 2012 Archives

Forward::Routes block syntax

Forward::Routes provides routes for framework developers.

The following feature refers to the Github version and is experimental: https://github.com/forwardever/Forward-Routes

Commit

Forward::Routes supports the nesting of routes, which can result in better performance and maintainability.

However, as your project grows, route stuctures can quickly become confusing.

The following routes:

my $r = Forward::Routes->new;
my $authors = $r->add_route('/authors');

$authors->add_route->to('Author#index');
my $author = $authors->add_route('/:author_name');

$author->add_route->to('Author#show');
my $articles = $author->add_route('articles');

$articles->add_route->to('Article#index');
my $article = $articles->add_route('/:article_id');

$article->add_route->to('Article#show');
my $comments = $article->add_route('comments');

$comments->add_route->to('Comment#index');
$comments->add_route('/:comment_id')->to('Comment#show');

match:

# tests
my $m = $r->match(get => '/authors');
is_deeply $m->[0]->params, {controller => 'Author', action => 'index'};

$m = $r->match(get => '/authors/steven');
is_deeply $m->[0]->params, {controller => 'Author', action => 'show',
  author_name => 'steven'};

$m = $r->match(get => '/authors/steven/articles');
is_deeply $m->[0]->params, {controller => 'Article', action => 'index',
  author_name => 'steven'};

$m = $r->match(get => '/authors/steven/articles/4');
is_deeply $m->[0]->params, {controller => 'Article', action => 'show',
  author_name => 'steven', article_id => 4};

$m = $r->match(get => '/authors/steven/articles/4/comments');
is_deeply $m->[0]->params, {controller => 'Comment', action => 'index',
  author_name => 'steven', article_id => 4};

$m = $r->match(get => '/authors/steven/articles/4/comments/3');
is_deeply $m->[0]->params, {controller => 'Comment', action => 'show',
  author_name => 'steven', article_id => 4, comment_id => 3};

Therefore, with two additional lines of code:

sub new {
    ...
    my $code_ref = pop @_ if @_ && ref $_[-1] eq 'CODE';
    $code_ref->($self) if $code_ref;
    ...
}

I have added a block syntax to the module, so the code can now also be written like this:

my $b = Forward::Routes->new;

$b->add_route('/authors', sub {
    my $authors = shift;

    $authors->add_route->to('Author#index');
    $authors->add_route('/:author_name', sub {
        my $author = shift;

        $author->add_route->to('Author#show');
        $author->add_route('articles', sub {
            my $articles = shift;

            $articles->add_route->to('Article#index');
            $articles->add_route('/:article_id', sub {
                my $article = shift;

                $article->add_route->to('Article#show');
                $article->add_route('comments', sub {
                    my $comments = shift;

                    $comments->add_route->to('Comment#index');
                    $comments->add_route('/:comment_id')
                      ->to('Comment#show');

                });

            });

        });

    });
});

And in order to make it more simple, use Method::Signatures::Simple

use Method::Signatures::Simple;

my $ms = Forward::Routes->new;

$ms->add_route('/authors', func($authors) {

    $authors->add_route->to('Author#index');
    $authors->add_route('/:author_name', func($author) {

        $author->add_route->to('Author#show');
        $author->add_route('articles', func($articles) {

            $articles->add_route->to('Article#index');
            $articles->add_route('/:article_id', func($article) {

                $article->add_route->to('Article#show');
                $article->add_route('comments', func($comments) {

                    $comments->add_route->to('Comment#index');
                    $comments->add_route('/:comment_id')
                      ->to('Comment#show');

                });

            });

        });

    });
});

About Forward Ever

user-pic I blog about Perl.