masonpoet

Specify route rules, and route to different components


I know how to to specify routes for page components using Mason::Plugin::RouterSimple, for example given a url of:

/archives/2015/07

I can create a component archives.mc as this:

<%class>
  route "{year:[0-9]{4}}/{month:[0-9]{2}}";
</%class>
Archives for the month of <% $.month %>/<% $.year %>

and similarly I can create a news.mc component that will handle urls of:

/news/2012/04

and that's fine (and very elegant!) but now what I want is to be able to handle urls like the following ones:

/john/archives/2014/12
/john/news/2014/03
/peter/news/2015/09
/bill/archives/2012/06

etc. I know I can write the route rules as:

<%class>
  route "{user:[a-z]+}/archives/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'archives' };
  route "{user:[a-z]+}/news/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'news' };
</%class>

but then the requests have to be handled by two different components. How can I route a request to different components? archives.mc and news.mc won't be matched by Mason because there's a username before the name of the component.


Solution

  • The problem is that, while urs like /archives/2014/12 can be easily handled by an /archives.mc component, for urls like /john/archives/2014/12 and /bill/archives/2012/06 it's not clear where to put the archives component.

    Mason will try to match the following components (it's a simplified list, please see Mason::Manual::RequestDispatch):

    ...
    /john/archives.{mp,mc}
    /john/dhandler.{mp,mc}
    /john.{mp,mc}
    

    but finally...

    /dhandler.{mp,mc}
    

    So my idea is to put a dhandler.mc component in the root directory:

    <%class>
      route "{user:[a-z]+}/archives/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'archives' };
      route "{user:[a-z]+}/news/{year:[0-9]{4}}/{month:[0-9]{2}}", { action=> 'news' };
    </%class>
    <%init>
      $m->comp($.action.'.mi', user=>$.user, year=>$.year, month=>$.month);
    </%init>
    

    If the url matches the first route, it will call the archives.mi component:

    <%class>
      has 'user';
      has 'year';
      has 'month';
    </%class>
    <% $.user %>'s archives for the month of <% $.month %>/<% $.year %>
    

    (I used a .mi component so it will be accessible only internally).

    The dhandler can be improved (better regexp, can check users from a database table and deny the request, etc.)

    Since my archives and news components can accept POST/GET data, and since I want to accept any data, I can just pass everything with:

     $m->comp($._action.'.mi', %{$.args});
    

    Not too elegand, but it looks like it does its work.