Probably the most popular Perl web framework is Catalyst now, it is also the web framework I know the best - this is why I choose it as the point of reference for the analysis below.
my ( $self, $c, ... ) = @_, not very DRY. The
$cparameter was dragged everywhere as if it was plain old procedural programming.
At some point I counted how often methods from the manual use the controller object versus how often they use the context object which contains the request data - the result was 117 and 38 respectively. Many people commented that their code often uses the controller object. It's hard to comment on this until the code is made public, my own experience was very close to that show by the manual, but this disproportion is only an illustration. The question is not about switching $c with $self. The question is why the request data, which undeniably is in the centre of the computation carried on in controllers, has to be passed around via parameters just like in plain old procedural code instead of being made object data freely available to all methods?
My curiosity about this was never answered in any informative way until about year ago I finally got a reply in private communication from some members of the core team - they want the controller object to be mostly immutable, and that of course would not be possible if one of it's attributed contained data changing with each new request. Immutable objects are a good design choice and I accepted this answer but later I though: Wait a minute - what if we recreated the controller object anew with each request? Then it could hold the request data and still be immutable for his whole life time. This was the moment when WebNano was born.
I have tried many Perl web frameworks but I found only one more that uses controllers in request scope - it is a very fundamental distinguishing feature of WebNano. It's not only about reducing the clutter of repeatable parameter passing - I believe that putting object into their natural scope will fix a lot of the widely recognized problems with the Catalyst stash and data passed through it. In procedural programming it is not a controversial rule to put your variable declarations into the narrowest block that encompasses it's usage - the same should be true for objects. And if you think that creating the controller object with each request would be too slow - then think again: Object::Tiny on one of the tester machines could create 714286 object per second - and even Moose - the slowest OO framework tested there could create 13459 objects, a few of such operations should not make much difference for web applications. By the way I also tested these theoretical estimations with more down to earth ab benchmarks for a trivial application serving just one page - WebNano came out as the fastest, by a wide margin, framework tested.
Catalyst started the process of
decoupling the web framework from the other parts of the application, with
Catalyst you can use any persistence layer for the model and any templating
library for the view. The problem is that the
view methods in Catalyst become rather empty - there is no common
behaviour among the various libraries - so all these methods do is finding the
model or view by it's name. For WebNano I decided that
->Something is shorter and not less informative then
->model('Something') - and I got rid of these methods.
The other thing that I decided not to add to WebNano is initialization of components. The Catalyst application builds all it's components and later serves as some kind of a Service Locator for them. Because of that the framework needs to know how to create every component of the application and we have the endless list of Catalyst adapters for practically any other popular CPAN library. In WebNano I decided to follow the usual advice and decouple these two distinct parts of the application into separate code units - or rather to limit WebNano to web part and let the programmer use whatever she wishes for the initialization part. I was told that Bread::Board is a very sophisticated tool for that. For my more simple experiments MooseX::SimpleConfig was very convenient.
Writing a dispatcher is not hard - it becomes hard and complex when you try to write a dispatching model that would work for every possible application. I prefer to write a simple dispatcher covering only the most popular dispatching scenarios - and let the users of my framework write their own specialized dispatching code for ther specialized controllers. With WebNano this is possible because these specialized dispatchers don't interfere with each other.
I also believe that this will make the controller classes more encapsulated - thus facilitating building libraries of application controllers.
I think with this type of inheritance it would be more natural to publish applications to CPAN, because they could be operational without any installation. A user could run them directly from @INC and only later override the configuration or templates as needed.
It is well known that using Moose generates some significant startup overhead. For web applications running in persistent environments - this does not matter much because that overhead is amortized over many requests, but if you run your application as CGI - this suddenly becomes important. I was very tempted to use Moose in WebNano - but even if CGI is perceived so passe now - it is still the easiest way to deploy web applications and also one that has the most widespread support from hosting companies. Fortunately using MooseX::NonMoose it is very easy to treat any hash based object classes as base classes for Moose based code, so using WebNano does not mean that you need to stick to the simplistic Object Oriented Framework it uses.
The plan is to make WebNano small, but universal, then make extensions that will be more powerful and more restricted. I think it is important that the base platform can be used in all kinds of circumstances.
Recently I discovered that many of my design choices are echoed in the publications by the Google testing guru Miško Hevery. My point of departure for the considerations above were general rules - like decoupling, encapsulation etc - his concern is testability - but the resulting design is remarkably similar. There is a lot of good articles at his blog, some were similar to what I already had thought over, others were completely new to me. I recommend them all.