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