|go ahead... be a heretic|
Maypole and I - Tales from the Frontier of a Relationshipby Corion (Pope)
|on Sep 05, 2004 at 14:00 UTC||Need Help??|
What is Maypole?
Maypole is a Perl framework for MVC-oriented web applications, similar to Jakarta's Struts. Maypole is designed to minimize coding requirements for creating simple web interfaces to databases, while remaining flexible enough to support enterprise web applications.
Simon Cozens, http://maypole.simon-cozens.org
It is a fact universally accepted that a database with contents must be in search of a presentation.
Jane Austen, «Pride and Prejudice Reloaded»
When I first caught eye of Maypole, I was startled by the shining beauty and the effortless grace with which it seemed to produce shiny web applications. I envied those who had a firm grasp of it and strived to employ Maypole to collect and publish the data available to me. But in that time, Maypole was a very immature Primadonna with very specific needs that were only faintly hinted at on websites only known to a few select people already in the clique. Armed with my knowledge of Class::DBI and frugal knowledge of mod_perl, I found myself rejected by Maypole and its requirements often for no reason apparent to me and loudly complained about this in the chatterbox, to the bemusement and annoyance of the other regulars there, I assume. After all, Maypole had been touted as the web application framework, being hawked through articles like flowers on the 14th of February and had received real money from the Perl Foundation. I felt I had a right to it.
Maypole aims to bring the virtues of the traditional MVC separation to web applications, by putting your data model into the database via Class::DBI, representing the data view as templates rendered via Template Toolkit and modifying your data through actions controlled by Apache::MVC, the heartpiece of Maypole itself. As much as the documentation touted these virtues, and as much as I acknowledge that having these virtues is a goal to strive for, not being able to get into closer contact with Maypole was something of a turn-off for me.
A few months have passed since and I have expanded my understanding of the nature of Maypole, and my love/hate relationship has come to some fruitition in the form of a small application working and online. While there are idiosyncrasies to it, there are also ways to wield Maypole as an effective tool to create web applications - if you set your aim right and avoid some pitfalls. Many of the things I will list are actually documented somewhere on the Maypole wiki but haven't found their way into Perl code distributed with Maypole itself yet.
The road to hell is paved with good intentions.
To get a start on the good side of Maypole, you need to know, understand and not entirely dislike Class::DBI. The whole object structure that Maypole operates on corresponds to your Class::DBI object and table layout. If you do not know Class::DBI or get easily lost with the setup of has_many() relationships and their ilk, start by getting familiar with Class::DBI without having Maypole in the way. Fighting a battle on two fronts is unadvisable, so make sure this side is covered. If you already have an existing database not specifically tailored with Class::DBI in mind, forget it. People have tried to use Class::DBI for existing databases, but I found them to be far more unhappy with what Class::DBI offered them than I ever was, but I see Class::DBI as a cheap object persistence solution with a good querying and batch manipulation language, and in that regard it has seldom let me down.
Secondly, Maypole requires Apache1 and mod_perl. If your version of Perl and the mod_perl delivered with your version of Apache1 match, you are off well. There is Maypole::CGI, but you will still need Apache::Request. chromatic has adapted Maypole to Jellybean, so that might be a good alternative approach to avoid another variable in your setup. If you decide to keep Maypole in its own Apache1 playpen and you want to avoid ruining your existing Perl installation by cluttering it with lots of obscure modules you will never look again, you are setting yourself up for much trouble in return. You will then have to create a separate instance of Apache1 with mod_perl, matching your perl executable, and fiddle with the include paths until it all works properly. If you decide to actually deploy a Maypole-based application, you will have to use a dedicated Apache1/mod_perl server anyway, as the namespace clashes will force you to do so. Although I have not personally tried it, chromatics solution of using Jellybean with Maypole::Container::Jellybean has the appeal of not requiring Apache1 and just requiring a working Perl installation, something you should have.
The third overtly documented prerequisite component is Template Toolkit - liked by many, even bestowed with a documenting book. You will not need to really know Template Toolkit, but basic knowledge about how a(ny) templating toolkit works and the common unifying abstractions will be required knowledge. If you know any of Mason, Petal or Template Toolkit, you will be well off and I think that even with only knowledge of HTML::Template, you will have enough to find your way around in Template Toolkit. In theory, it is easy to replace the templating backend for Maypole, and there already are the required modules and templates for HTML::Mason, but none have surfaced for Petal and HTML::Template yet.
No battle plan ever survives contact with the enemy.
Feldmarschall von Moltke
The installation process should be as easy as perl -MCPAN -e "install Maypole", but, alas, it is not. The prerequisite listing of Maypole 1.7 gets you almost there though. The missing modules are:
Best install these three modules in that order and then install Maypole. Template::Plugin::Class is the only module explicitly missing in the prerequisites of Maypoles Makefile.PL and the Maypole wiki will guide you towards the other missing modules. On that occassion the authors and users of Module::Build earn bad karma for their future life as Module::Build does not support the PREFIX= parameter of ExtUtils::MakeMaker and thus any local installation of Template::Plugin::Class will require manual intervention instead of simply working through CPAN.pm. The rest of the modules depends on your choice of database and cannot be automated and detected much more sensibly.
A first date
It might seem easy enough, but [it] is just like a stroll in the park.
Larry Wall, <1994Jun15.firstname.lastname@example.org>
After some rounds of forced installs (in my case because mod_perl was not in @INC of plain perl), you will believe you have everything together and plan for the first ride. You modify your webserver configuration, you set a location, dress up and navigate to the URL, to find yourself alone on a blank page, or at best with a 500 HTTP Error for company. You decide to wait around, but as you grow weary after 30 seconds and look into the error log, you find that your date has not shown up and left you without a message. Error reporting, if present at all, is abysmal with Maypole. Syntax errors in your code modules abort the loading of these modules at that place so you will end up with one half of a module loaded into your webserver when an error stopped parsing of that module. There is no facility of tracing progress, so you will have to put calls to warn all over the place to glimpse a hint of Maypole where possibly the cause for the error could lie. This also holds true for errors in your templates - they vanish secretly and silently as well. Maypole itself generates some "redefinition" and "undefined" warnings itself, but all of these warnings you will only see if you write a command line script to exercise your code. The only good advice I can offer here is to make your modules emit a message to the log as they have been parsed and to manually load all your class modules from within your main module in an eval loop to catch and log all occurring errors yourself.
Inner workings and structure of Maypole
All of the "Model" part of Maypole is based around the idea that you want to perform an action on a row in a table through a request. To that effect, Maypole maps all requests of the form table/action/row_id to your Class::DBI subclass wrapping table, retrieves the data corresponding to row_id into it, and then calls the method indicated by action of that object. The whole association of table and implementing class happens without any additional declaration just by matching name to class name. Only those methods declared as :Exported can be called in such a manner, so there is no way of reaching internal methods from the outside through Maypole, creating unsuspected security holes. The idea of wrapping the table/class, the action and the row identifier in the URL is nothing new, for example the (Python-based) Zope-based Plone framework. Before Maypole will execute such a request, it allows you to perform authentication and authorization to make sure that only those actions get performed that the user is allowed to perform.
The downside of this approach is that actions acting on a larger set of objects than one have no easy match, and the syntax deviates far enough from the common CGI parameter syntax that it does not scale well for more than one object to be acted upon on a request. The situation that you want an action to be performed on more than one object in one request does not only happen when you want the same action performed on a list of objects of the same class, like ordering several different kinds of flowers for your bouquet, it also happens when you want to display two objects together that can be formed into a new product, like a diamond and a ring, which together form a present. The present has no representation in the database though, as it consists simply of the two components and should not need its own database table.
... and they lived happily ever after
Any sufficiently advanced technology is indistinguishable from magic.
Sir A. C. Clarke
Hokey religion, and ancient weapons are no match for a good blaster at your side.
George Lucas, «Star Wars»
After you have managed to get the supplied example, beerdb, working, you will fever to embark on some ventures of your own devising instead of remaining on the known shores that Simon Cozens outlined for you. Many times have I complained about how Maypole stacks more magic upon the piles of magic that Class::DBI heaps upon the database. My advice is to stay away from all additional magic like Class::DBI::Loader::Relationship, which purports to give you a concise, natural (english) language way of declaring the relationships in your database. All it will give you though, are headaches after headaches as it misinterprets your instructions in the most innovative yet useless ways. Even Simon recommends to stay away from it, and he is the author of it.
A place where the magic comes in handy is when you declare which methods your classes should make visible to the outside. You simply declare a subroutine in your class as :Exported, and it will be reachable via the request to table/action/row_id. This declaration mechanism also makes it very easy to add introspection to any application. A list of all valid external requests can be easily created, allowing you to add a detailed permission scheme to any application. I wrote such a scheme in one day, and at the price of 7 additional tables and a bit of self-contained code Maypole allows easy restriction of any action to specific usergroups.
Maypole does not enforce or supply any user management or permission in its bare form - it is just a web display for database rows. This leaves you with the freedom to keep your application as simple as possible. There is no common way to add plugins with their own database schema easily and without much thought yet, but including that would increase the overall complexity to something comparable to Plone and thus raise the barrier of entry quite a bit.
Update: Changed link to the Maypole Wiki