meta META update

I’m on holiday in the states at the moment, so I haven’t had much chance to do much Perl stuff.  That said, I did take a chance to add proposals for the new META.yml spec.

For those of you that don’t know, META.yml is a small file that ships in modern CPAN distributions that contains, well, meta information.  It’s a way to know things about the distribution like author, pre-requisits, etc, etc, without actually having to examine or even execute other files in the distribution.  A lot of the more fancy automated tools that do really clever things with CPAN distibutions rely on it.

I added two suggestions.  The first suggestion was a way to provide more detailed information on the repository (a.k.a. the version control system) the original source for the module was stored in.  Currently we support a simple URL, but it’s not clear if this is the URL for the web front end or the version control resource itself, nor how to go from one to the other.  I’d also like a format (“git”, “svn”, “cvs”) and type (“github”, “sourceforge”) so automated tools don’t have to derive things they can do from the URL alone.  I’m imaginging a tool where I can type in a module name and it’ll do the right thing to get me to a stage where I can immediatley start working on a patch (e.g. forking the project and checking out the fork if it’s on github, svn co-ing the project if its a subversion mirror, etc)

The second proposal was the more straight forward.  I suggested that we provide an official way for inhouse extensions to META.yml for private use that won’t get stomped on by future versions of the spec and won’t get complained about by tools the use META.yml: Simply reserving the ‘x-’ prefix to keys much like in HTTP headers.

You can view my full proposals (or submit your own proposals before the 1st of October) on the CPAN Meta Spec Proposals page on the Perl QA wiki.

Posted in 1

Permalink 1 Comment

Think McFly, Think!

This week I made a time machine with Perl.

So, writing tests for testing anything that happens with the passage of time is very very hard to do as you don’t know how long any operation might actually take on the computer you’re testing on. Code may take different time to run on different machines. Sleep instructions may take longer on busy machines than on non-busy machines. The user might suspend the computer in the middle of your test suite!

Multiple this across the large number and diverse set of computers that might run your test suite after you upload your module to the CPAN and you’re in a situation where you’re going to get some false negatives, where the test suite will fail even though nothing’s really wrong. Telling your end users to “try it again, it’l probably work this time” isn’t exactly a recipe for instilling confidence in your code.

That’s why I developer my trusty time machine. Test::Time::HiRes is what I call it, and it allows me fine grained control over passage of a certain type of time – the time that Test::HiRes reports back to modules using it.

The easiest way to think of it is an alternative implementation of Time::HiRes where time only progresses time when you (or the code you’re testing) tells it to. This means that all sleeps your code does take an instant of wallclock seconds, but the simulated clock moves automatically. As such this code runs in milliseconds, rather than hours:

use Test::Time::HiRes;
use Time::HiRes qw(sleep);
sleep(3600 * 10);

We can also jump around in time however we want.

use Test::More tests => 2;
use Test::Time::HiRes;

# Time::Hash::Expire uses Time::HiRes to get it's timing
use Tie::Hash::Expire;
tie my %hash, "Tie::Hash::Expire", { expire_seconds => 10 };
$hash{"foo"} = 1;

ok($hash{"foo"}, "key not expired");
time_travel_by(3600);
ok(!$hash{"foo"}, "key expired");

(or back in time, or to particular points in time.) We have complete control!

Of course, I’m not the first person to come up with this idea. The very fine Test::MockTime does the same thing for Perl’s inbuilt time(),localtime(), and gmtime() functions. I’m just extending the concept for more accurate time.

It’s not quite a DeLorean, but it’ll do me.

The module is on the CPAN and on github. [Update: as of 2009-09-24 20:15:39 UTC, the module still hasn't reached search.cpan.org yet - I'll leave the link in place for when it does, but if you're impatient, head off to github]

Posted in 1

Permalink 1 Comment

Three Blinding Perl Tips, See How They Execute

I’ve got a lot of half written posts that I still need to complete, but I don’t want to let this blog stagnate. Quick! Time for an n things list – Three random Perl things that I’ve been using a lot recently:

Default Prompts

If you install Term::ReadLine::Perl you can provide defaults for interactive prompts in your program

my $term = Term::ReadLine->new('report');
my $filename = $term->readline(
   "file to write output to? ",
   "$ENV{HOME}/report-".DateTime->now->ymd.".csv"
);

When this executes this code creates a line in your terminal that has a default value already filled in for you to edit:

file to write output to? /Users/mark/report-2009-09-16.csv

Controlling Where lwp-request Sends Its Requests

The command line utility lwp-request, which is a handy Perl utility that ships with LWP that allows you to download webpages from the command line, can take proxy settings from the environment variables you pass to it.

bash$ http_proxy=http://127.0.0.1 lwp-request http://www.mywebsite.com

This is really useful for development: You can set your test apache running on your local machine / virtual machine / dev box up to answer on the same virtual host name as your live domain and, via the proxy settings, have lwp-request send requests there rather than to the real live machine whose DNS the domain name points to. This is also really useful for debugging reverse proxies in live – you can choose exactly which machine in your proxy chain to send to, bypassing the nginx / lighttpd / varnish front proxy machine and talking directly to the backend machine if you wish.

The “Just Show Me The Data” Incarnation for DBI

Most of the time I don’t use DBI directly – I use an object relational mapper like DBIx-Class. But sometimes I’ll get handed a chunk of SQL from our DBA and I just want to write a wrapper script to run that SQL and do something simple with it. DBI is very flexible, and the documentation talks at length about things like caching parsed queries, reading data in from the database row by row, efficency of data structures, etc, etc. What it’s not very clear about, however, is how to ignore all of that and just get all your data from executing some SQL in the easiest possible format to manipulate.

The magic incarnation looks like this:

    my $dbh = DBI->connect(
      "DBI:mysql:hostname=127.0.0.1;database=foo",
      $username,
      $password,
      { RaiseError => 1 },
    );

    my $rows = $dbh->selectall_arrayref( <<'SQL',{ Slice => {} }, "flintstone");
      SELECT *
        FROM characters
       WHERE last_name = ?
    SQL

This gives you a reference to an array, with each element in this array representing one row returned from the database as a hashref keyed by field name. This is really easy to process:

    foreach my $row (@{ $rows }) {
      say " * $row->{first_name} $row->{last_name}";
    }

If there's any problem at all DBI will raise an exception, so you don't need to worry about writing lots of error checking code.

Saying Thanks

Did you know that recent releases of perl come with a way to send thanks back to the authors of Perl?  Neither did I until I saw Paul Fenwick speak at YAPC::Europe this year.

Here’s what you’ve got to do.  First, make sure you’re running Perl 5.8.9 or 5.10.0 or later. Then from the command line run the “perlthanks” command with your email address:

bash$ perlthanks -r someone@example.com

This fires up a modified version of the “perlbug” command line tool – a utility for reporting bugs in the perl interpreter itself.  This version is designed:

This program provides an easy way to send a thank-you message
back to the authors and maintainers of perl.

If you wish to submit a bug report, please run it without the -T
flag (or run the program perlbug rather than perlthanks)

First of all, please provide a subject for the message.
Subject:

So we tap in “We think you’re totally awesome in every possible way” and hit return. It takes us onto the next stage:

It's now time to compose your thank-you message.

Some information about your local perl configuration will
automatically be included at the end of your message, because
we're curious about the different ways that people build and use
perl. If you'd rather not share this information, you're welcome
to delete it.

You will probably want to use a text editor to enter the body of
your report. If "vi" is the editor you want to use, then just
press Enter, otherwise type in the name of the editor you would
like to use.

If you have already composed the body of your report, you may
enter "file", and /Users/mark/local/bin/perlthanks will prompt
you to enter the name of the file containing your report.

Editor [vi]: 

I can cope with vi quite happily, so I hit return and then it fires up my editor containing the following text:

This is a thank-you report for perl from someone@example.com,
generated with the help of perlbug 1.39 running under perl 5.10.1.

-----------------------------------------------------------------
[Please enter your thank-you message here]

[You're welcome to delete anything below this line]
-----------------------------------------------------------------
---
Flags:
    category=thanks
    severity=none
---
Site configuration information for perl 5.10.1:

Configured by mark at Sun Aug  9 09:11:41 BST 2009.

Summary of my perl5 (revision 5 version 10 subversion 1) configuration:
...

You can tell this was originally written to collect bug reports, can’t you? Okay, so we fill in the bits between the dashed lines with some message like “You rock my world, oh yeah, oh yeah”. Save the file and quit vi and we see:

You have finished composing your message. At this point, you have
a few options. You can:

    * [Se]end the message to perl-thanks@perl.org,
    * [D]isplay the message on the screen,
    * [R]e-edit the message
    * Display or change the message's [su]bject
    * Save the message to a [f]ile to mail at another time
    * [Q]uit without sending a message

Action (Send/Display/Edit/Subject/Save to File): 

Typing "se" and hitting return sends the message off to perl-thanks@perl.org. Whee!

Devel::UseFromCommandLineOnly

On Friday I posted a horrible chunk of code to my blog. With all the scorn I threw about it there’s one bit I didn’t cover however, a sort of saving grace. The second line here:

package Test::EnhancedIs;
use base qw(Devel::UseFromCommandLineOnly);

Devel::UseFromCommandLineOnly? What the heck is that, and why am I subclassing it? Well, that, boys and girls, is another quick module I knocked up while butchering the internals of Test::Builder.

You see, I wrote Test::EnhancedIs as a proof of concept that I knew would very likely break just as soon as someone changed Test::Builder in the slightest. I’m offering it as unsupported experimental code, but even I know no sooner had I typed those warning words that someone else was busy typing

#!/usr/bin/perl

use strict;
use warnings;

use Test::More tests => 1;
use Test::EnhancedIs;  # whooo, easier tests

use Sharks;
is(Sharks->armed,"frickin' laser beams");

And committing it to their version control system. I know what’ll happen next is that that person or their coworkers, months down the line when this inevitably breaks, will hunt me or the hardworking geniuses that maintain Test::Builder down to blame us for breaking their code.

So I decided to not let that happen. If you run the above code it outputs:

bash$ perl test.t
1..1
Invalid use of Test::EnhancedIs in 'test.t' at line 7; This module can only be loaded from the command line at test.t line 7

Yep, that’s Devel::UseFromTheCommandLineOnly kicking in and hitting you with the cluestick. However, if you remove line 7 from the above example and run the test loading the extra module from the command line like so:

travis-4:~ mark$ perl -MTest::EnhancedIs test.t
1..1
not ok 1
#   Failed test at test.t line 9.
#          got: 'frickin' *sharp teeth'
#     expected: 'frickin' *laser beams'
# Looks like you failed 1 test of 1.

Everything is dandy.

Devel::UserFromCommandLineOnly is on CPAN and GitHub if you want to play with it.

Test::EnhancedIs

The other day I made an idiot out of myself on IRC.

Now this isn’t exactly news, but the way I did so is interesting. It seemed to me that Test::More’s is was unexpectedly failing when it was passed identical inputs. Having pondered over the issue for a good while, I turned to greater minds than myself for enlightenment; Thus I pastied this output to the London Perl Monger’s IRC channel:

bash$ perl -Ilib t/01multi.t
1..3
ok 1 - data okay
not ok 2 - ip
#   Failed test 'ip'
#   at t/01multi.t line 61.
#          got: '123.45.67.98'
#     expected: '123.45.67.89'
ok 3 - port
# Looks like you failed 1 test of 3.

Can you spot the obvious mistake? I didn’t. Of course, being a bunch of pedantic so-and-sos the London Perl Mongers are they immediately pointed out that “89″ is not the same as “98″, and that’s why the tests were failing….Ooops.

But wait? Why should I have to spot that. That’s not very lazy. Shouldn’t this comparing strings kind of thing be exactly what the computer is good at, and darn it, why can’t it just point out where the string starts being different, preferably with a big flashing arrow, a klaxon, and a troupe of dancing girls…

We can’t quite manage that, but with my new module, we can get a lot closer:

travis-4:Babel-WideLog mark$ perl -MTest::EnhancedIs -Ilib t/01multi.t
1..3
ok 1 - data okay
not ok 2 - ip
#   Failed test 'ip'
#   at t/01multi.t line 61.
#          got: '123.45.67.*98'
#     expected: '123.45.67.*89'
ok 3 - port
# Looks like you failed 1 test of 3.

Yep, a white dot on a red background. The universal Look at Me!. Actually, I did start by just making dot red, but then that wasn’t as clear when you’re running under prove which already colourises your output.

Sadly, the implementation of Test::EnhancedIs leaves a lot to be desired, and is really a proof of concept rather than what I’d consider actual safe, shippable code. Let’s have a look at the actual code and try not to wince too much:

package Test::EnhancedIs;
use base qw(Devel::UseFromCommandLineOnly);

use strict;
use warnings;
no warnings "redefine"; ## no critic (ProhibitNoWarnings)

our $VERSION = 0.00_01;

use Term::ANSIColor qw(colored);
use List::Util qw(min);

use Test::Builder;

# remember the original subroutine.  Note the BEGIN { } here - this is because
# without it this code will be run after the sub Test::Builder::_is_diag
# has been declared and we'll grab a ref to the wrong subroutine
my $uboat;
BEGIN { $uboat = \&Test::Builder::_is_diag }; ## no critic (ProtectPrivateVars)

# now write a new subroutine, overriding the subroutine in another package
# don't try this at home kids.
sub Test::Builder::_is_diag { ## no critic (ProtectPrivateSubs)
  my( $self, $got, $type, $expect ) = @_;

  # look for either a different character, or the end of either string
  my $index;
  foreach (0..min(length $got,length $expect)) {
    $index = $_;
    last if substr($got,$index,1) ne substr($expect,$index,1);
  }

  # put a marker in there
  substr($got,$index,0,colored("*","white on_red"));
  substr($expect,$index,0,colored("*","white on_red"));

  # run the original code
  return $uboat->($self,$got,$type,$expect);
}

1;

As you can tell from the comments, we’re really breaking the rules here. Anything that disables warnings like that and requires multiple Perl Critic tests to be disabled is more than a little bit worrying!

The worst of it is that we’re redefining a private function that’s inside the Test::Builder namespace. By convention any method or function that starts with an underscore in Perl is considered to be private and can change between versions of the code without notice, meaning that this code will probably not work on different versions of Test::Builder than the one I have installed (which is the latest) – including future versions that are yet to be released.

Still, as long as we’re aware of the pitfalls this isn’t too bad a snippet to have around to fire up from the command line for one off tests when our eyes start to glaze over, at least until it next breaks again. We’ll just have to be careful not to include it in any commands or scripts we save to disk, lest we start to rely on it.

Of course the real solution is to take this proof of concept to the Perl-QA guys and gals and ask them how we can get best this functionality integrated properly into the next release of Test::Builder. That’s a task for another day however.

Follow

Get every new post delivered to your Inbox.