Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask

Dispatchers for dummies

by sri (Vicar)
on Oct 14, 2008 at 16:10 UTC ( #717029=perlmeditation: print w/replies, xml ) Need Help??

(A little meditation from my blog.)

The most fun for me when building a new web framework is always the dispatcher design.
Thats one of the main reasons why Mojo is completely dispatcher agnostic.

Now you might be asking "WTF is a dispatcher?".
It is the part of your framework that maps incoming requests to the actual code that builds the response for it.

A very naive way of doing this would be a simple list of paths with classes and methods mapped to them.
/foo/bar/index -> Foo::index() /test123.html -> Bar::default() /cookies/list/fresh -> Cookies::list()
Every web framework out there has their own way of doing this, they all have advantages and disadvantages.

Catalyst 5 - Perl
Lets start with my good old Catalyst 5.
Meta data is kept close to the code which makes initial development easier, but for debugging you depend very much on good log output.
Changing paths later on can be painful, and using attributes is also very tricky because the Perl api for them sucks, it can look quite good though if used right.
# /foo/bar/index package Foo::Bar; sub index : Relative { ... } # /test123.html package Foo::Bar; sub test : Path('/test123.html') { ... } # /cookies/list/fresh package Cookies; sub list : Relative Args(1) { ... }
Servlets 3.0 - Java
Servlets 3.0 will do something similar with annotations. (I'm not going to give you full examples here's Java and ugly as hell)
@Servlet(urlMappings={"/foo", "/bar"}) public class ControllerWithAnnotations { @GET public void handleGet(HttpServletRequest req, HttpServletResponse +res) { ... } }
Jifty - Perl
For quite some time i was a big fan of declarative dispatchers like the Jifty one.

Meta data and code are once again close together, but you have to use a full blown domain specific language.
You can't use basic Perl features like inheritance and need to reinvent a lot.
The flow is easier to follow than in Catalyst though without using log output.
# /foo/bar/index on 'foo/bar/index' => do { ... } # /test123.index on 'test123.html' => do { ... } # /cookies/list/fresh on '/cookies/list/*' => do { ... }
Catalyst 4 - Perl
Fun fact, we nearly built a declarative dispatcher for Catalyst 4, a long time before it was cool. :)
__PACKAGE__->action('/foo/bar/index' => sub { ... });
Ruby on Rails - Ruby
Ruby on Rails originally started using a static "/controller/action/args" mapping with Apache's mod_rewrite in front of it.
To get rid of the Apache dependency they've invented one of the imo coolest dispatcher concepts out there called Routes.

Routes separate meta data and code completely, so you can use all the basic language features in your code.
For easy initial development you start with a simple default route like ":controller/:action/:id" and later move on to more complicated url mappings.
# /foo/bar/index map.connect 'foo/bar/index', :controller => "foo", :action => "index" # /test123.html map.connect 'test123.html', :controller => "foo", :action => "test" # /cookies/list/fresh map.connect ':controller/:action/:quality', :controller => "cookies", +:action => "list", :quality => "fresh", :requirements => { :quality => /\w+/ }
Most interesting for me is the ability to reverse the route and generate urls from the pattern.
<%= link_to "Fresh Cookies", :controller => "cookies", :action => "lis +t", :quality => "fresh" %>
Merb - Ruby
Merb uses an alternative Routes implementation, with a lot of cool new ideas.
# /foo/bar/index r.match("/foo/bar/index").to(:controller => "foo", :action => "index") # /test123.html r.match("/test123.html").to(:controller => "foo", :action => "test") # /cookies/list/fresh r.match(%r[^/cookies/list/(\w+)$]).to(:controller => "cookies", :actio +n => "list", :quality => 'path[1]')
Django - Python
The prize for the ugliest dispatcher goes to Python's Django, which uses plain old regex for everything.
urlpatterns = patterns('', (r'^foo/bar/index$', 'foo.views.index'), (r'^test123\.html$', 'foo.views.test'), (r'^cookies/list/(\w+)$', 'cookies.views.list'), )
Mojolicious - Perl
For Mojolicious i'll be using a Perl-ish Routes implementation with some Catalyst goodness added.
More about that in my next article. ;)
# /foo/bar/index $r->route('/foo/bar/index')->to(controller => 'foo', action => 'index' +); # /test123.html $r->route('/test123.html')->to(controller => 'foo', action => 'test'); # /cookies/list/fresh $r->route('/:controller/:action/:quality', quality => qr/\w+/) ->to(controller => 'cookies', action => 'list', quality => 'fresh'); $c->url_for(controller => 'cookies', action => 'list', quality => 'fre +sh');
Note that this was just a very basic overview, all frameworks have much more features than i've shown here.
But i hope it will help all you future framework developers, building dispatchers is fun, get Mojo and start today!

Replies are listed 'Best First'.
Re: Dispatchers for dummies
by Tanktalus (Canon) on Oct 14, 2008 at 21:24 UTC

    If you were to, say, use perl 5.10, would you be able to get rid of some of the parsing (which is likely done in perl) and use named captures with regexes?

    $r->route(qr[^/(?<controller>[^/]+)/(?<action>[^/]+)/(?<quality>[^/]+) +(?:/(?<extras>.*))$])->to(...) # (ok, an x modifier with some gratuitous whitespace might be wise her +e..)
    This would give named parameters (through %+ or %-, though I'd likely use %-). And nearly unlimited ability to validate URLs before we get into the real code. For example, being able to send /foo/123 to one piece of code, and /foo/abc to another (based on \d vs \D).

    So, if you're given a string, you can have the : markers as above. But if you're given a Regexp reference and the current perl is 5.10, you have the opportunity for some much more interesting handling, which you almost don't even have to handle.

      I would like to support Perl 5.8, apart from the fact that raw regex looks quite ugly.
      A huge advantage of patterns like i'm using them is that you can rebuild the path very easily.

      The /foo/123 /foo/abc example is already possible since patterns get compiled to regex internally.
      $r->route('/:(number)_:(word)/foo', number => qr/\d+/, word => qr/\w+/ +)
Re: Dispatchers for dummies
by aufflick (Deacon) on Oct 16, 2008 at 00:52 UTC
    I don't know what it is, but I quite like web dispatchers too :)

    In my HTTP::Server::Brick I followed a simple declarative approach.

    I've always liked the OpenACS request processor. In your installation you have a number of standard packages (eg. comments, admin) and you write your own packages for your functionality. You can then instantiate your package (maybe more than once) on a sub-url of your choosing. Instantly all the url paths offered by that package are available relative to that mount point. Since you can instantiate more than once, your code is able to introspect which instance it is to make sure it is, say, retrieving the right blog entries from the database.

    OpenACS is built on top of AOLServer which follows a fairly standard declarative dispatcher design where you call a tcl api to register a proc against a particular url.

Re: Dispatchers for dummies
by EvanCarroll (Chaplain) on Oct 18, 2008 at 17:10 UTC
    Thanks for this post. I'll definitely be evaluating Merb in the immediate future because of the ORM-agnosticness attribute.

    I'm buzzing in because I would take a gander a great many of the new catalyst sites use the :Chained dispatch method which you don't even mention. It makes debugging very easy, and changing paths simple.

    Evan Carroll
    I hack for the ladies.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://717029]
Approved by Corion
Front-paged by Old_Gray_Bear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (5)
As of 2020-09-28 21:30 GMT
Find Nodes?
    Voting Booth?
    If at first I donít succeed, I Ö

    Results (144 votes). Check out past polls.