Principles of Gantry
This framework grew over several years in the hands of several programmers. Though we haven't always made bumper sticker mottos, some come to mind which might help you understand what we do and why.
- MVC is the correct standard for web apps.
-
Once upon a time our do_* methods queried our databases directly and produced HTML output. The flow of our modules was hard to follow. The markup was hard to work on. Worst of all much of the sql and markup was repeated over and over again with cp or cut and paste.
- Data models should be Class::DBI::Sweet classes.
-
Instead of hand writing each select, we rejoice in the simplicity of Class::DBI, but we need some Sweet features, like order_by on retrieve_all.
- Views should be handled by the Template Toolkit.
-
Yes, we used to have an in house templating system. It was simple, so simple that do_* methods had to produce almost the final form of our html output. Only wrappers and simple substitutions were applied. We want more, but there's no need to write it here.
- A few templates go a long way.
-
At first, we thought that we might need a template for each do_* method. Then we coded and realized that we need only a few templates which can be shared widely. For instance, there is only one form template in the Billing sample app and it is very similar to the one we use for our production applications.
- Never code in a template.
-
Even though Template Toolkit allows you to write raw Perl in the template, please don't. This just opens up too many problems, including security problems. But primarily, it defeats the separation of concerns which is so useful for people to keep track of a project. Our minds work better if things are separated into manageable pieces.
- Avoid markup in code.
-
Following on the previous principle, avoid putting any sort of display markup in your code. Rather, build a data structure which the template can unpack into marked up form.
There are cases where the two concerns meet in the middle. In our applications, popup calendars are isolated into a single module which computes the dates and generates the javascript for the calendars. The alternative would be to duplicate a lot of computation in both the calendar plugin and the template.
- If you must include markup in the code, isolate it.
- If you must code in template, write a plugin.
- Explicit is better than implicit.
-
In some frameworks, extreme magic is employed so that only one Location directive is needed for each application. This has two problems. First, it requires the urls to exactly match the modules which handle them. This immediately defeats our naming convention. We place all of our apps in the Apps:: name space, but we never include 'apps' in the url. Second, and more importantly, it baffles new people who can't immediately determine dispatch from the httpd.conf alone.
- mod_perl rules, but there are different versions, all must be supported.
-
When converting to a new version of mod_perl, not all apps will move at once. Hence the framework provides a plugin scheme for different mod_perl engines.
- Plugin only things that everyone will need.
-
Plugins add a bit of complexity to a framework. We've chosen to use them for specifying things like which version of mod_perl you have and what templating system you use. For other things that only some modules even care about, we just use the standard or CPAN module directly. The rule is this: if Gantry.pm calls the methods, there will be plugins, otherwise not.
- Fewer files are better than more.
-
Since our CDBI model packages are typically only 10-15 lines, we put all of them in the same file for easy access.
- Configuration information belongs in httpd.conf.
-
There is a lot of data in any application which should not be hard coded. In particular, this allows development and production code to be identical, but have different effects on the outside world.
- Restarting the web server is painful, do it infrequently.
-
The following would be better advice if Apache2::Reload wasn't broken.
If you develop with the same architechture as you deploy, you must reinstall the app on each change, then restart the server. It is better to leave the app in a development directory, use lib and Apache::Reload or Apache2::Reload. These ensure that the app is reloaded as needed. Note that changes to the CDBI modules usually require a server restart.