The prior parts of this series are here:

  http://lists.preshweb.co.uk/pipermail/dancer-users/2015-October/005237.html
  http://lists.preshweb.co.uk/pipermail/dancer-users/2015-October/005238.html

The final two parts will be coming shortly, after I finish my editing pass.


======================


The first two parts of this article series broke the monolithic `lib/App.pm` 
file up into a series of Perl modules, each focused on some functional centroid 
of your application.

If we describe the default `lib/App.pm` file as monolithic, then we might get 
away with describing the view files as polylithic, because we do not need to 
break the view files up; they naturally map to web pages, so the app's URL 
scheme effectively keeps them separated by purpose.

Although we do not start this part of the series needing to break up an overly 
large file, I can pass along some tips along that might cause you to rethink 
the current structure of your app's `views/*` tree.


**The View Naming Scheme Should Match the URL Scheme**

Older web frameworks like PHP, ASP, and JSP define the web app's URL structure 
in the file system. The route scheme defined in the previous part of this 
article series would look like this in PHP:

    index.php
    mf/index.php
    mf/subfeature/index.php
    
The view code — HTML and inline JavaScript — is intermixed with the server-side 
code in the same file. Worse, single files like `mf/subfeature.index.asp` have 
to handle multiple HTTP verbs; in our example, `GET`, `POST`, `PUT`, and 
`DELETE`. If each verb returns a different web page, you end up with a huge 
top-level `if/elseif/else` blocks dividing the file up into a bunch of 
logically separate chunks. There are other bad consequences to this web 
framework design choice, such as that if you want your text editor to do syntax 
coloring correctly, it needs to be able to switch among PHP, JavaScript, and 
HTML syntax, all within the same file. The programmer ends up needing to do the 
same sort of context switching, too.

Simply put, this is a mess.

[Template::Toolkit](http://www.template-toolkit.org/) — Dancer's default 
template system — solves part of this problem by separating the view files from 
the server-side Perl code. Modern web app development frameworks like 
[Angular](https://angularjs.org/) solve the rest of it by encouraging you to 
move all of your JavaScript code into separate `*.js` files, rather than put it 
inline in the HTML file.

There is nothing about the way Dancer works that enforces or even encourages 
any particular `views/*` file naming scheme. Thus this tip: for your own 
sanity, I recommend that you name your view files after the corresponding 
route. The Dancer scheme corresponding to the above PHP scheme might look 
something like this:

    views/top.tt
    views/mf/top.tt
    views/mf/subfeature/get.tt
    views/mf/subfeature/post.tt
    views/mf/subfeature/put.tt
    views/mf/subfeature/delete.tt
    
This maps Dancer routes 1:1 to view files, using a naming scheme that is easy 
to remember, since it's nearly identical to the URL scheme. It's so simple a 
mapping that you could write a Perl one-liner to do the transform. By 
restricting yourself to a simple naming convention, you have removed a whole 
class of details that you have to keep in mind while working on your web app; 
you don't have to guess where the view file for a given route handler lives, 
you already *know* where it lives. You will quickly rediscover this simple 
mapping years from now when you have to work on the web app again.

This naming scheme is very much a suggestion, rather than a prescription. For 
one thing, you may find that the `DELETE /mf/subfeature` route needs no view, 
because it just deletes one database record and then redirects you to `GET 
/mf/subfeature` to show the change in the normal view. In that case, you would 
not need `views/mf/subfeature/delete.tt`.

The important thing is to *have* a well-thought-out naming scheme, not 
necessarily to follow the one I've laid out above.


**Factor Common UI Elements Out**

If you reuse a particular UI element across different parts of your web app, 
put its view file into a special view directory. I use `views/parts` for this. 

If you have a web page that is incorporated into your web app dynamically on 
the client side via Ajax, you should have a corresponding Dancer route for it, 
and probably a Perl module for it, too:

    views/parts/component.tt
    lib/App/Parts/Component.pm
     
Then in `lib/App/Parts/Component.pm`:

    sub _get {
        # ...work out how to build the component
        
        return Dancer::template '/parts/component' => {
            stuff => 'goes here'
        };
    }

    prefix '/parts' => sub {
        get 'component' => sub { _get(); };
    };

I chose the naming scheme for the function carefully. I want to be able to use 
short names here that correspond to the HTTP verb that the function handles 
without colliding with the short names exported by Dancer's DSL. The [standard 
Perl style rules](http://perldoc.perl.org/perlstyle.html) say that a leading 
underscore marks a function that isn't meant to be called from outside the 
module, which is exactly correct here; only Dancer should be calling that 
function.

You may prefer a different scheme, such as `Get()`, even though this is 
unconventional Perl style. As I've said in previous parts of this series, the 
important thing is to *have* a consistent style, not that you use the style 
I've chosen here.

One argument in favor of `Get()` over `_get()` is that you might need to be 
able to reuse this route handler in another of your Perl modules, bodily 
incorporating the HTML fragment within another web page on the server side, 
thus saving an Ajax round trip the first time that that web page is served. 

For example, `lib/App/MajorFeature.pm` might have something like this in it:

    sub Get {
        my ($ctx) = @_;         # see Part 2 for the definition of context()
        
        # Work out how to build the main part of the top-level /mf page
        ...Perl code here...

        # Assemble the pieces
        return Dancer::template '/mf/top' => {
            component   => App::Parts::Component::Get(),
            other_stuff => 'goes here',
        };
    }
    
    prefix '/mf' => sub {
        get '/' => sub { Get(context); };
        # other route handlers here  
    };

Then within `views/mf/top.tt`, you can reference `$component` to bodily include 
that component's HTML fragment into the larger page.

Now that we have sorted out the purely server-side HTML and Perl code files, we 
will look at the Dancer app design decisions that affect its presentation to 
the browser.
_______________________________________________
dancer-users mailing list
[email protected]
http://lists.preshweb.co.uk/mailman/listinfo/dancer-users

Reply via email to