Source code comments vs. unit tests
I've read many opinions about comments, from "comments are good, they help you understand code" to "comments are bad, they become obsolete and mislead you" and everything in between.
I've read many opinions about comments, from "comments are good, they help you understand code" to "comments are bad, they become obsolete and mislead you" and everything in between.
I was pleased to discover that the jQuery UI library includes an autocomplete widget that looks good and works well, and I started using it straightaway to liven up my search boxes. The simplest way to use it is to return an array of JSON objects matching a query from /some/url, and feed the client the following bit of Javascript:
$('#query').autocomplete({source: '/some/url'});
All was well, until I came across this paper on Javascript Hijacking. A quick summary: someone includes a <script> tag on their web site pointing to http://your.serv.er/that/url, and write some Javascript to steal the data (by redefining the array constructor, or overriding the property setter for objects). Due to the very specific circumstances of this attack, it is often misunderstood or ignored as "just another XSS attack", but it can lead to leakage of confidential data. The attack was originally demonstrated by Jeremiah Grossman to steal contacts from someone's GMail account.
The paper outlines a few possible solutions: deal with it like any other cross-site request forgery by requiring the request to include a random unguessable (which means using POST or including the token in the URL), or prefixing the response with "while(1);" or some other statement that would cause the browser to fail to execute the code (e.g. wrap it inside a comment).
Tony Cook pointed out to me that the attack hinges on the fact that an array literal is a valid statement in Javascript, but an object literal by itself is not. This suggests that returning the matches as an object rather than array will defeat the attack:
{"matches": [{"label": "one", "value": 1}, ...]}
Although I can't be certain that no Javascript implementation will treat this as a valid statement, I like this approach better than using POST to fetch a completion list or extending CSRF protections to GET requests. Fortunately, jQuery UI's autocomplete widget is more than flexible enough to accommodate an array of matches wrapped in an object:
$('#query').autocomplete({ source: function (request, response_cb) { $.getJSON( '/some/url', {term: request.term}, function (data) { response_cb(data.matches); } ); } });
Unfortunately, this is already much more code than people should have to write to handle this case, and it doesn't even handle errors (if any do occur and are not properly handled, the autocomplete widget will behave strangely). I think jQuery UI should handle this case by default.
This code can easily be adapted to cope with a "while(1);" prefix—treat the response as a string and remove the prefix, then eval the remainder and pass the resulting object to the response callback. If someone can convince me that an object literal is insecure—something of which I am quite willing to be convinced—then I'll change my code to do that. (If this seems a too-casual attitude, it's because none of my autocompletion data are confidential. Defeating casual attacks is fine for the moment.)
"But… what about the model?" is a very common question from newcomers to Mojolicious, and not everyone is happy with the simple answer: "You can do whatever you want, Mojolicious doesn't care". What this really means (especially for people coming from Catalyst) is that you can keep using your DBIx::Class model with "use My::Model", or use whatever other method you prefer to interact with your database.
I don't get along with DBIx::Class myself, but enough people ask about using straight DBI that I felt it worthwhile to share the way I do it. There's not much to it: my code boils down to calling DBI->connect and keeping the handle for later use. Mojo's package attributes with delayed initialisation make it easy to do this.
I put the following code in my startup function (i.e., sub startup in lib/MyApp.pm):
sub startup { my $app = shift; # …read configuration and do other stuff… (ref $app)->attr( db => sub { DBI->connect("dbi:Pg:database=$db", $user, $pass) } ); }
This adds a "db" attribute to my application class (represented here by the $app object), which can be accessed as "$app->db". The first time it is accessed, the function I provided is called, and its return value is cached and returned as the value of the attribute thereafter. In this case, my sub is a closure that uses the $db, $user, and $pass variables read from the configuration file, and connects to a Postgres database using DBI/DBD::Pg, but those are just details. You could do the same thing with DBIx::Connector or a similar module.
(Aside: don't just create a database handle in startup. It will break if you run your app under a preforking server like Hypnotoad. Your startup will be called once before forking, but the DBI handle can't be shared between the server processes. The delayed attribute initialisation is perfect in this case, because each server will have its own handle.)
With this code, the database connection is created only the first time it is needed, and it is kept around as long as the application is alive (how long that is depends on which server your application runs under). Controllers can just do "$self->app->db" to fetch a db handle at any time without worrying about any of this.
Marcus Ramberg pointed out on IRC that I could also use "$app->helper(db => sub {...})" to make the handle accessible from controllers (as "$self->db") and from epl templates (as "db"). I don't need to do that for database handles, but someone else might find it useful.
(This is an old post that I forgot to submit when it was written.)
Late in December 2009, as a birthday present to myself, I went on a solo trek to Dayara Bugyal, a high-altitude alpine meadow in Garhwal. I meant to write about the week I spent in the mountains, but upon my return, I found the experience too overwhelming to try to describe all at once. A year has passed, and now I can begin to appreciate some of the ways in which the trip has changed me.
In retrospect, one of the most significant decisions I took was to leave my beloved "Field Guide to the Birds of India" behind. I left many other things behind because they didn't seem worth carrying 10km up a mountain on my back, but I could have found place in my pack for the field guide if I had tried. I consciously decided not to take it, which is why it wasn't even in the car with me.
In terms of not being able to identify the birds I saw, I didn't suffer. I doubt the field guide would have helped me to move more than two birds to the "definite" list, and I'm not sure about those two. If I had tried the exercise a few years ago, I may have learned more specifically about identifying the birds I saw. In a very narrow sense, I could even claim to have not learned anything new (apart from seeing a few new species).
But in fact, that trip marked the beginning of a fundamental change in how I looked at birds. Somewhere along the way, my observations became focused not only on identifying birds, but about describing the birds I saw. It's a difficult change to explain. It's not that I didn't observe birds carefully before—quite the contrary! But my observations were structured according to the field guide, as I looked for features I knew were useful to identify a particular species. I was fitting the birds to their descriptions.
These days, I try to build up a more complete mental model of the birds I look at. I'm more conscious of plumage features that don't contribute (or rather, that are not documented to contribute) to identification. I pay much more attention to age and moult state than before. When I'm watching a bird, I think about how to describe it without reference to the field guide. (I ask, "What would convince me if someone reported seeing this species without photographic evidence?" and try to make my mental model answer that question.) Described this way, it sounds like a deliberate change, but it took me by surprise when I realised that I was doing it (which was quite recently, long after my Dayara trek).
These changes began with my trying to compensate for not having a book, and realising the extent to which descriptions are limited by the space available (which only increases my admiration for Krys Kazmierczak, who has managed in his book to put his finger on the crux of identification for species after species). I had a taste of what it must have been like to explore an area before reliable field guides were available, when one couldn't know in advance which features were or were not important.
In the past year, I've also had the privilege of birding with a number of people who are vastly more experienced and knowledgeable than I am, and learning from how they looked at and thought about birds. I'm sure that pushed me in the right direction. I may also have built up enough field experience to begin formulating and expressing my own strategies for observation and identification.
In any case, I feel I have made real progress as a bird-watcher, and I am enjoying it more than ever.
Mojolicious comes with easy-to-use cryptographically signed session cookies out of the box.
Here's how I set up a web application behind a reverse proxy to serve unauthenticated pages over HTTP, and everything else over HTTPS-only.
If you are on Linux, just use /dev/urandom — and don't worry, what you've heard about it being cryptographically insecure is a myth.
I've been using Test::Mojo to write tests for my Mojolicious applications, and it's great.
my $t = Test::Mojo->new(app => "Wigeon"); $t->post_form_ok('/login', {__login => $login, __passwd => $password}) ->status_is(200) ->content_type_is("text/html") ->content_like(qr/Welcome/); $t->get_ok('/users-only') ->status_is(200) ->content_type_is("text/html") ->text_is('html body p', 'First paragraph');
When something breaks (i.e., an unexpected 404 or 500 response), however, Mojolicious renders the built-in not_found or exception template, and I see the whole HTML/CSS page in my test results. Usually many times, as one failure precipitates others.
I fixed this by loading Mojolicious::Controller early in my application class and overriding its render_not_found and render_exception methods.
use Mojolicious::Controller; ... { no warnings 'redefine'; package Mojolicious::Controller; sub render_not_found { my $self = shift; $self->render( status => 404, format => 'txt', text => "Not found: " . $self->req->url->path ); } sub render_exception { my ($self, $e) = @_; return if $self->stash->{'mojo.exception'}; $self->render( status => 500, format => 'txt', handler => undef, layout => undef, extends => undef, text => Mojo::Exception->new($e)->message, "mojo.exception" => 1 ); } }
Soon after I grumbled about having to do this on IRC, Sebastian Riedel added experimental support for mode-specific exception templates. One can now create stripped-down not_found.testing and exception.testing templates, and they will be automatically detected and used.
A possible first record of Phylloscopus cantator from Kolkata.
The Mojolicious documentation gave me the impression that the way to send a text/plain response was just:
$self->render_text("Plain text content");
I wrote a content_type_is test for such a response the other day, and its failure made me notice that the type was actually text/html. I asked about it on #mojo, and learned that render_text is just the opposite of render_data, and the function name has nothing to do with the content type of the response.
The incorrect examples at the very beginning of the rendering guide have been fixed, and the description of render_text has been changed. It used to say "Render the given content as plain text, note that text will be encoded" and now it says "Render the given content as Perl characters, which will be encoded to bytes".
Given the misleading examples present earlier, I wish the description had also said something like "The default content type of the response is text/html, and this function does not change it unless you specify a different format". Anyway, the correct way to send a text/plain response is to specify the format explicitly as text:
$self->render_text("Plain text content", format => 'txt');