The Exception That Rather Proves the Rule
I had really wanted to stay out of the recent commentary on CGI. I really did. I was going to be able to until a recent article was published in which the authors try to step in on the side of CGI. I believe the authors to be talented perl developers posting in good faith, but the result really pushed me to reply. The application developed as a case for CGI and preventing “overkill” is a perfect example of why a framework is needed.
The Plack App
First of all, although the authors initially signify that they feel that frameworks are overkill, they do in fact quite quickly reach for one. Plack is the reference implementation of a micro framework build on the PSGI standard. It isn’t the lack of a framework, it is the lack of a full framework, by design. So while the authors seem to be saying CGI.pm is bad (at least we agree on that!) and CGI the protocol is good they neither use the protocol directly nor avoid using a framework by say using PSGI directly.
Next they proceed to develop their application, leaving hooks all over the place for testing (and yes testing is good) but the result is almost a new micro framework on top of a micro framework (Plack). Classes for configuration, templates in configuration so they can be overloaded for easier testing, a home rolled logger. BTW they are only classes in that they provide methods, but notice that all of them are class methods, you don’t instantiate this. The result is surprisingly hard to read for such a small app and one that is intended to convey the simplicity of small when small is all that is needed.
Then we get to the footnote. The authors quite quickly noticed that their home rolled framework doesn’t handle utf8 parameters correctly. They had to add more code to properly handle it.
In looking at their code, I also noticed that because they don’t handle exceptions, their own exception thrown if the response isn’t a JSON object would result in no response being sent to the client. A simple oversight I’m sure. They also don’t seem to have a landing page with the form, which likely means that it is in a static directory that they didn’t bundle, but it means that I don’t see it and it has no tests.
A Mojolicious Alternative
Now I’m on the Mojolicious core team, so I think its probably no surprise that in working out a response I reached for Mojo. I even made a Lite app rather than compare apples to apples and make a fully OO “full app” because once you strip out most of their “framework”, you really don’t need it. That said I believe that most of what I’ll write following this could be applied to any modern framework like Dancer(2) or Web::Simple or what have you.
I didn’t time it, but given the lightness of dependencies, I highly suspect that Mojo installs more quickly than did the dependencies of the presented application. Dancer probably is comparable to the presented application. Catalyst … well, ok, Plack is probably better at that consideration. Mojolicious apps can natively run as CGI. PSGI apps like Dancer can be deployed as CGI as well. So for all the fuss over ease of deployment, I don’t see much benefit for the presented application over alternatives.
Anyway, my alternative implementation is on my Github at https://github.com/jberger/slack_invite. Given the head start of cribbing from their app and owing to a lot of experience I was able to write this replacement and tests in about an hour. Without that it probably wouldn’t have been too much more than that amount of time plus the time to figure out how to interact with slack.
The biggest feeling I have looking at the result is how much easier it is to read. There’s a logger and a user agent available. Exceptions are handled natively (I just replaced the default template) as is UTF-8, JSON and rendering a response with headers appropriate to the response type.
One thing Mojo does give me over other frameworks is a testing framework and especially the ability to inject a mock service to replace slack. It lets me use CSS selectors to test the rendered responses, even the initial form. Note that you can use Test::Mojo and/or Mojo::UserAgent in your non-Mojo applications too if you’d like.
Summary
Again I want to stress that I’m not trying to attack the authors of this article. In fact I applaud them for showing code that they use and sticking up for technologies that they believe in. That said I think this article, rather than making the case they intend in fact makes the opposite case. Even when you want to do something small and build a webapp that will be used infrequently, an application written in a proper framework is still the way to go.
Hmm.
How much of a role does Plack play in that app? It parses the request parameters. That’s it. That’s the entirety of the reason that their codebase doesn’t qualify as raw PSGI. They don’t even use Plack::Response (I often don’t either).
I think that example reflects on Plack about as much as it relies on it. The fact that it’s overcomplicated has nothing to do with Plack.
For that matter, Plack is not a framework, micro or not – unless the term “framework” is meaningless anyway. To me, something is a framework if it owns the control flow and you write code to slot things into that. I don’t know any other coherent definition of what makes something a framework. That one accurately describes Mojo, Dancer and Catalyst. It does not whatsoever describe Plack. It doesn’t describe the code of the app in question either.
Do I have to be bothered to rewrite your example to use just Plack::Request in order to prove that the reason for how much better it is isn’t Mojo? Or can we take just that as a given, now that I’ve said so, and spare me the exercise?
Ultimately… “Mojo core developer bangs out example that uses Mojo well to good effect” is not an earth-shattering takeaway, nor is “Mojo core developer finds code that makes good use of Mojo super-easy to read”, no? 😊
Hi Aristotle,
I don’t know what the proper definition of a “framework” is, but what you’re talking about is a router. A framework to me is a bunch of tools bundled together to do some of the common processing. Plack is that, whether or not it has a router. It also has a similar sized dependency footprint to many frameworks and much larger than than Mojo.
Anyway, you’ve missed my point entirely. My question is “what does the style of code presented in the article gain for the user?” Is it simplicity? No there’s lots of manual intervention needed, as evidenced by the utf8 problems. Is it a lighter dependency footprint (as is usually an argument for CGI.pm)? No, whether or not you use a router, Plack is not dependency light. Is it ease of deployment? No, that was the point of highlighting that PSGI frameworks and Mojo can all be deployed on CGI. Indeed these authors are doing so.
Sometimes “worse is better”, sure, but I truly don’t see what the advantage is here. Meanwhile, if someone sees that frameworks make this very easy, there is then no reason to put yourself through the effort and potential errors of rolling something so manually.
If you have some argument for why the presented code is preferable to other frameworks, I’m all ears. If you’d like to write a more concise Plack::Request version, I’d be happy to see it. I’ll be especially glad to hear how it improves some consideration that the users will have, whether in development or deployment of the app. Better can sometimes be better too.
It’s the one you won’t want to hear.
I have some Plack CGI scripts on a machine where I do not have root, served by an Apache I do not control, run by the system perl, using only OS vendor packages for Perl modules. The machine has had regular security and system updates since… (almost) none of which I’ve ever noticed, despite my scripts being hit regularly by cronjobs from my end. (The ones I noticed were for reasons unrelated to the CGI scripts.)
I’ve never touched the scripts again since I wrote them… back in the days of Mojolicious 2.x.
I will concede the fact that Plack is an unfortunately heavy dependency to install. 😕 This is in no small part due to the disease of configure-time dependencies that has gripped CPAN in recent years. That was better once.
If you absolutely cannot maintain your scripts (ie want them to live forever unmaintained), and yet your system will get updates to libraries, then you have to have blind trust that nothing in any library will ever break or you need to be proactive to protect yourself. Yes mojolicious moves faster than most but still to assume that no library will ever have a breaking change is putting a lot of faith in systems you don’t control.
That said, you can mitigate this by ensuring that your dependencies don’t move. Use local copies of your dependencies early in @INC. Use carton to do the automatically or just bundle them. If you do this, a Mojolicious 2.x script will continue to work as written, as would any system, Plack, CGI or otherwise.
It’s not that I can. It goes back to the question you posed: “what does [maintaining such a script to keep it running] gain for the user?” My scripts have served my needs with no changes whatsoever. What would I have gained from maintaining them?
But I haven’t had to. What would I have gained from it relative to the situation as it has been?
Also, is “just run ancient versions of everything” a great answer to “upgrading stuff requires a ton of make-work”?