The Role of Critique

Over the last week I've been noodling with a minor improvement to Perl Critic to do with private methods and roles. Sometimes I write roles that are essentially importers for utility methods - methods that are essentially imported into the class but don't become part of the public interface. Here's an example of such a role, something that allows you to write $self->_select("SELECT * FROM foo WHERE foo_id",123) on any class that has a dbh attribute.
package SuperProject::Role::WithSelect;

use Moo::Role;

requires 'dbh';

sub _select {
  my $self  = shift;
  my $sql   = shift;
  my @binds = @_;
  return $self->dbh->selectall_arrayref( $sql, { Slice => {} }, @binds);
}

1;
So what's the problem (aside from the fact that I should have been using DBIx::Class for the databse stuff?)
bash$ perlcritic --verbose 10 SuperProject/Role/WithSelect.pm
Private subroutine/method '_select' declared but not used at line 7, column 1.
 Subroutines::ProhibitUnusedPrivateSubroutines (Severity: 3)
   By convention Perl authors (like authors in many other languages)
   indicate private methods and variables by inserting a leading underscore
   before the identifier. This policy catches such subroutines which are
   not used in the file which declares them.

   This module defines a 'use' of a subroutine as a subroutine or method
   call to it (other than from inside the subroutine itself), a reference
   to it (i.e. `my $foo = \&_foo'), a `goto' to it outside the subroutine
   itself (i.e. `goto &_foo'), or the use of the subroutine's name as an
   even-numbered argument to `use overload'.
Ooops. Perl::Critic is complaining about that private subroutine that's not used in the role class. I guess I could change the method name from _select to select, but I don't want select to become part of the public interface to the class that's consuming this role. Another option would be to use a ## no critic declaration. But that's tedious, almost every role I write is going to be littered with extranious ## no critic delcarations and that's often a sign that something is seriously wrong. What we need is some way to turn off Subroutines::ProhibitUnusedPrivateSubroutines for all our roles, but leave it on for everything else. And that's what I've been working on with help from Jeff Thalhammer over the last week. With the release of Perl::Critic that was released today you can tweak Subroutines::ProhibitUnusedPrivateSubroutines in your perlcriticrc to automatically turn itself off for a file if somewhere in that file you use a particular module, like, say, Moo::Role.
[Subroutines::ProhibitUnusedPrivateSubroutines]
skip_when_using = Moo::Role

Share "The Role of Critique"

Share on: FacebookTwitter

Critical Hit

Last night I braved Winter Storm Juno to venture out to Albany Perl Mongers' technical meeting in East Greenbush.  Our special guest star Jeff Thalhammer had agreed to teleconference in from San Francisco to talk us through Perl::Critic.  This arrangement worked pretty well for us, with Jeff able to chat with the small number of us that had braved the storm and answer our questions. The only flaw with the setup was Jeff wasn't able to try the pokemon cupcakes I'd baked. Bring on cupcake-over-ip technology is what I say. I've been using Perl::Critic for many years now, and I wasn't sure I would get much out of the technical meeting, but I was very wrong. Jeff was able to give several tips, answer questions, and point out a bunch of improvements that I was unaware of since I first started using Perl::Critic years ago.

only

The biggest win for me was learning about using the only option directly in a .perlcriticrc itself. This allows you to only run the rules specified in the critic file rather than any installed rules and is awesome for continuous integration test suites running off our in-house .perldcriticrc because if a new rule gets installed (due to an update our distribution dependencies) we don't start automatically using it (and failing it) without some sort of review.

verbose

The various levels of verbose output that Perl::Critic's verbose options now provides are very helpful. Bumping the output to level 8 (with --verbose=8) helpfully tells you the name of the policy that you're violating, which is useful if you want to disable it with a ## no critic (...), or tweak the .perlcriticrc or simply read the documentation for the module so you can better understand what the policy is complaining about

profile-proto

My ancient personal .perlcriticrc file is getting a bit long in the tooth. Not only has my coding style changed over the many years as I've learned more about Perl, but Perl::Critic itself has improved introducing new policies both in the core distribution and on the CPAN itself. This is why I was excited to learn about the profile-proto which can spit out a fully-documented default .perlcriticrc

Let's Go Shopping

Jeff took us on a brief shopping tour of the CPAN to show us some of the cool new policies that are available. Some standouts include:

Thanks

Thanks to both Jeff for taking the time to talk to us, and to Patrick Cronin for once again organising the meeting, providing pizza and all the other thankless stuff he does to organise our meetings.

Share "Critical Hit"

Share on: FacebookTwitter

Getting Choosy

Today I released a new version of Mac::Choose which wraps the choose command line utility. Basically this lets you, from Perl, pop open a fuzzy match dialog like quicksilver, alfred, et al. This is probably best shown rather than explained:
use Mac::Choose qw(choose);
print choose qw(
      red
      orange
      yellow
      green
      blue
      indigo
      violet
) or die "User canceled selection";
choose_example_demo The exciting thing about this release is that while previous versions required you to go to Tiny Robot Software's website to download a copy of choose, this version of Mac::Choose ships with the choose binary and installs a copy alongside the Perl module.

Using File::ShareDir

In order to handle the intallation of the binary in a place where my code could once again find it I altered my distribution to use the File::ShareDir infrastructure. Essentially this infrastructure comes in two halves, an installer to install things in the shared dirctory and a module that can give you access to the files again. In my Module::Intall powered Makefile.PL in order to use Module::Intall::Share I simply had to add the line:
install_share;
This installs everything in the share directory of my distribution (which just contained one file, the choose binary) when you make install. The changes to my module were also very simple:
use File::ShareDir qw(dist_file);
our $executable_path = dist_file("Mac-Choose","choose");

Testing

After those changes, everything worked flawlessly...apart from the test suite! The problem is that until the module is installed the files aren't in the shared directory, and traditionally we test a module before we install them. This isn't insurmountable however, we just need to make some changes to our tests to quietly override File::ShareDir's functionality with that from Test::File::ShareDir:
use File::Spec::Functions qw(:ALL);
use FindBin;
use Test::File::ShareDir (
    -root => catdir($FindBin::Bin, updir),
    -share => { -dist => { "Mac-Choose" => "share" } },
);

Share "Getting Choosy"

Share on: FacebookTwitter

Internet Famous

Did I ever tell you guys the story of how I once made the front page of imgur with a Perl script? As we're all aware, imgur is a imaging sharing network traditionally dedicated to such topics as funny cat photos, internet memes and other recycled content often direct from its sister site reddit.  It's internet fluff in the best possible way, and what I like to browse when I'm eating lunch and I need to disconnect my brain after a morning of coding.  While it's got a minority of hardcore technical members, it's not at all known for it's technical content due to posts being promoted based mostly on up and down votes given by the masses;  As far as I know I'm the only person ever to get to the front page with actual code. So how did I end up posting code of all things to imgur? Well, like most good Internet stories it started like this: One lunchtime I saw a front page post featuring an image claiming that startling was the only nine letter word that you could remove one letter at a time and still get a valid word (i.e. starting, staring, string, sting, sing, sin, in, I.)  I was pretty sure that wasn't right and there must be others. So I did what any Perl programmer does in this situation.  I wrote a Perl program to prove that wasn't true: And then for giggles, I posted the above image back to imgur.  Not exactly my best code, but I wrote it in about five minutes flat.  Five hours later it hit the front page and I had done my duty.

Share "Internet Famous"

Share on: FacebookTwitter

Regexp::Debugger

One of the topics that I had scheduled for the Perl Advent Calendar this year that didn’t finally make the cut was Regexp::Debugger (It might make it next year, but let’s all pretend I never made this post if that’s the case.)

I really wanted to write an article on Regexp::Debugger because it has such wow factor.  You just stick a line in the source code and bam instant visual debugger.  Suddenly I was finding out in minutes what was wrong with the regular expression that I’d spent half an hour figuring out writing numerous tests.  Sometimes there’s no substitute for getting in there and seeing what’s going on. The problem with the article is that that’s pretty much all I could say about the module.  It’s almost too simple to use - there’s just nothing else to comment on.  Go.  Go use it.  You won’t regret it.  Hmm. The other big problem is that it’s almost impossible to describe in words how to use the debugger.  It’s something that works best in a screencast or something similar and we’re not (yet) set up to include those kinds of things in the Perl Advent Calendar.  The article on ack had a couple of animated gifs, but I’m unsure if the one I prepared for Regexp::Debugger really conveys what’s going on without an audio track.  Still, for posterity, here’s the gif I prepared: Regexp::Debugger demo gif

Share "Regexp::Debugger"

Share on: FacebookTwitter

Perl Advent Calendar 2014 Stats

The Perl Advent Calendar is finally done and dusted for another year (apart from the typo corrections - there are always typo corrections.)  Which is lucky, since I committed the last submission just two days before Christmas Eve. Let’s look at some statistics shall we? The advent calendar is a team effort:
  • Total articles: 25 (24 articles + xmas day)
  • Articles written by people than me: 13
  • Total number of authors (including me): 13
  • Number of Pull Requests For Typos (so far): 8
It’s big:
  • Total number of lines: 5,318
  • Total number of words: 27,323
  • Total number of graphics: 7 (including two animated gifs)
  • Longest articleChristmas Eve’s Article (an epic 745 lines)
  • Perl examples: 159
It’s Perl:
  • References to Perl: 171 
  • References to Moo/Moose: 38
  • References to PHP: 2 (in the example about finding and deleting all the php code)
It’s fun
  • References to Santa: 38
  • References to Elves: 44
So, maybe now I’m not writing 2,575 lines (12,310 words) into the advent calendar (and copy editing and adding content to the other articles) I can finally get back on the Perl Ironman bandwagon in time for new year? Jarvis, load my blog editor...

Share "Perl Advent Calendar 2014 Stats"

Share on: FacebookTwitter

Tell me more, tell me more

A new version of Test::DatabaseRow has just escaped onto the CPAN, with a minor new feature verbose data to make working out what went wrong in the database test even easier when the worst happens In a nutshell this feature allows you to output in diagnostics all the results the database returned (not just listing the first thing that didn't match, which is the default thing row_ok does in a similar way to Test::More's is_deeply does.) In other words instead of writing this:
    all_row_ok(
       table => "contacts",
       where => [ cid => 123 ],
       tests => [ name => "trelane" ],
       store_rows => \@rows,
       description => "contact 123's name is trelane"
     ) or diag explain \@rows;
You can just write this:
    all_row_ok(
       table => "contacts",
       where => [ cid => 123 ],
       tests => [ name => "trelane" ],
       verbose_data => 1,
       description => "contact 123's name is trelane"
     );
Or even just write this:
    all_row_ok(
       table => "contacts",
       where => [ cid => 123 ],
       tests => [ name => "trelane" ],
       description => "contact 123's name is trelane"
     );
And turn on verbosity from the command line when you run the tests
TEST_DBROW_VERBOSE_DATA=1 perl mytest.pl

Share "Tell me more, tell me more"

Share on: FacebookTwitter

Mavericks, XCode 5 and WWW::Curl

Sometimes you go through a lot of pain trying to get something to work, and you just need to write it down and put it on the internet. You tell yourself this is useful; You're doing it so that others can find your words and solve the problem too. In reality, it's just cathartic. And who doesn't like a good rant?

So What Broke?

I recently updated my Mac to Mavericks (OS X 10.9). Overall, I like it...but it broke something I care about. It broke libcurl's handling of certificates., and in so doing broke my install of WWW::Curl also (since it's a wrapper around the system libcurl.) This is remarkably hard to diagnose, because it just seems like the servers have suddenly stopped trusting each other (when in reality libcurl has just started effectively ignoring the options you're passing to it.) Now, WWW::Curl (and its working certificate handling) is a dependency of some of the code I'm using. Worse than forcing me to disable certificate security, I actually need to identify myself to the server with my own cert. Just turning them off and running insecurely won't work.

Installing libcurl

I'm not a great fan of installing software from source on my mac after having been bitten by fink and it's kin numerous times in the past. This said, homebrew is actually pretty darn nifty, and can be used to install libcurl:
brew install curl   
Wrrr, clunk, clunk and zap, I have a non broken curl in /usr/local/Cellar/gcc47/4.7.3/bin. Hooray! This one actually deems to listen to the command line options you pass it!

Installing WWW::Curl

Now all we have to do is install WWW::Curl and link it against this libcurl, right? Well, I did this:
wget http://cpan.metacpan.org/authors/id/S/SZ/SZBALINT/WWW-Curl-4.15.tar.gz
gunzip -c WWW-Curl-4.15.tar.gz | tar -xvf -
cd WWW-Curl-4.15
export CURL_CONFIG=/usr/local/Cellar/curl/7.33.0/bin/curl-config
perl Makefile.PL
And things started to go wrong:
The version is libcurl 7.33.0
Found curl.h in /usr/local/Cellar/curl/7.33.0/include/curl/curl.h
In file included from /usr/local/Cellar/curl/7.33.0/include/curl/curl.h:34:
In file included from /usr/local/Cellar/curl/7.33.0/include/curl/curlbuild.h:152:
In file included from /usr/include/sys/socket.h:80:
In file included from /usr/include/Availability.h:148:
/usr/include/AvailabilityInternal.h:4098:10: error: #else without #if
        #else
         ^
/usr/include/AvailabilityInternal.h:4158:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6
         ^
/usr/include/AvailabilityInternal.h:4131:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5
         ^
/usr/include/AvailabilityInternal.h:4108:10: error: unterminated conditional directive
        #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_4
         ^
4 errors generated.
Building curlopt-constants.c for your libcurl version
Building Easy.pm constants for your libcurl version
Building Share.pm constants for your libcurl version
Checking if your kit is complete...
Looks good
Writing Makefile for WWW::Curl
Writing MYMETA.yml and MYMETA.json
That Ladies and Gentlemen is the sound of Apple's compiler sucking. You see, not so long ago I upgraded to XCode 5 and apparently this no longer ships with gcc. Uh oh. (I don't show the errors you get when you make and make test, but be assured it's all downhill from here)

Installing gcc

I suspect I could have just re-downloaded XCode 4 and then used xcode-select. But I didn't. Let me know if that works, okay? This is what I actually did:
brew install gcc47
Then I waited a long time (while doing something else productive instead.) Finally when I was done I had to manually edit the Makefile.PL to use the right preprocessor:
perl -pi -e 's!cpp"!/usr/local/Cellar/gcc47/4.7.3/bin/cpp"!' Makefile.PL
perl Makefile.PL
(yeah, it'd be nice if they'd been some sort of option for that.) Then I altered the resulting Makefile for good measure too:
perl -pi -e 's{CC = cc}{CC = /usr/local/Cellar/gcc47/4.7.3/bin/gcc-4.7}' Makefile
And then I could build it all.
make
make test
Of course, it still got errors. But they're not new errors. So I pretend I didn't see them and install anyway.
make install
I hate computers sometimes.

Share "Mavericks, XCode 5 and WWW::Curl"

Share on: FacebookTwitter

5.18 Hash Key Ordering Changes In A Nutshell

$ perlbrew switch perl-5.16.3
$ perl -E '%h=map {$_=>1} (1..10); say join ",",keys %h for 1..3'
6,3,7,9,2,8,1,4,10,5
6,3,7,9,2,8,1,4,10,5
6,3,7,9,2,8,1,4,10,5
$ perl -E '%h=map {$_=>1} (1..10); say join ",",keys %h for 1..3'
6,3,7,9,2,8,1,4,10,5
6,3,7,9,2,8,1,4,10,5
6,3,7,9,2,8,1,4,10,5
$ perlbrew switch perl-5.18.0
$ perl -E '%h=map {$_=>1} (1..10); say join ",",keys %h for 1..3'
10,2,8,3,6,9,5,4,1,7
10,2,8,3,6,9,5,4,1,7
10,2,8,3,6,9,5,4,1,7
$ perl -E '%h=map {$_=>1} (1..10); say join ",",keys %h for 1..3'
4,6,9,1,10,3,2,7,8,5
4,6,9,1,10,3,2,7,8,5
4,6,9,1,10,3,2,7,8,5
Now each execution has its own hash key ordering (but hash key ordering is the same for the duration of the execution until you insert a new key.) perl-5.18 increases the chance that keys will be reordered when inserts happen:

$ perlbrew switch perl-5.16.3
$ perl -E 'for (1..20) { $h{$_}=1; say join ",",keys %h }'
1
1,2
1,3,2
4,1,3,2
4,1,3,2,5
6,4,1,3,2,5
6,4,1,3,7,2,5
6,3,7,2,8,1,4,5
6,3,7,9,2,8,1,4,5
6,3,7,9,2,8,1,4,10,5
6,11,3,7,9,2,8,1,4,10,5
6,11,3,7,9,12,2,8,1,4,10,5
6,11,3,7,9,12,2,8,1,4,13,10,5
6,11,3,7,9,12,2,14,8,1,4,13,10,5
6,11,3,7,9,12,2,15,14,8,1,4,13,10,5
11,7,2,1,16,13,6,3,9,12,14,15,8,4,10,5
11,7,17,2,1,16,13,6,3,9,12,14,15,8,4,10,5
11,7,17,2,1,18,16,13,6,3,9,12,14,15,8,4,10,5
11,7,17,2,1,18,16,13,6,3,9,12,14,15,8,4,19,10,5
11,7,17,2,1,18,16,13,6,3,9,12,20,14,15,8,4,19,10,5
$ perlbrew switch perl-5.18.0
$ perl -E 'for (1..20) { $h{$_}=1; say join ",",keys %h }'
1
2,1
1,2,3
2,4,1,3
4,1,2,5,3
2,4,1,5,3,6
4,1,2,6,5,7,3
7,8,1,3,5,6,2,4
4,2,6,3,5,8,1,9,7
9,7,8,1,6,3,5,10,4,2
9,7,11,8,1,6,5,10,3,4,2
11,8,1,7,9,2,12,4,5,10,3,6
4,13,12,2,6,3,5,10,8,1,11,9,7
11,14,8,1,7,9,2,12,4,13,5,10,3,6
4,13,12,15,2,6,3,5,10,14,8,1,11,9,7
9,16,7,8,14,3,13,12,15,2,1,11,6,5,10,4
5,10,17,6,4,11,1,3,2,12,15,13,16,7,9,8,14
4,17,6,5,10,1,11,13,12,15,2,18,3,8,14,9,16,7
17,6,5,10,4,1,11,19,3,13,12,15,2,18,9,16,7,8,14
11,1,4,5,10,17,6,8,14,16,7,9,2,18,20,12,15,13,3,19
This can be controlled with the PERL_PERTURB_KEYS environment variable

$ perlbrew switch perl-5.18.0
$ PERL_PERTURB_KEYS=0 perl -E'for (1..20) { $h{$_}=1; say join ",",keys %h }'
1
2,1
3,2,1
3,2,1,4
5,3,2,1,4
5,6,3,2,1,4
5,6,3,2,1,4,7
5,2,3,6,8,1,4,7
5,2,3,6,8,9,1,4,7
5,2,3,6,10,8,9,1,4,7
5,2,3,6,10,11,8,9,1,4,7
5,2,3,6,10,11,12,8,9,1,4,7
5,2,3,6,10,13,11,12,8,9,1,4,7
5,2,3,6,10,13,11,14,12,8,9,1,4,7
5,2,3,6,10,13,11,14,12,8,15,9,1,4,7
3,14,12,16,8,15,9,4,7,5,6,2,10,11,13,1
17,3,14,12,16,8,15,9,4,7,5,6,2,10,11,13,1
18,17,3,14,12,16,8,15,9,4,7,5,6,2,10,11,13,1
18,17,3,19,14,12,16,8,15,9,4,7,5,6,2,10,11,13,1
18,17,3,19,14,12,16,8,15,9,4,7,5,6,2,10,11,13,20,1

Share "5.18 Hash Key Ordering Changes In A Nutshell"

Share on: FacebookTwitter

Another Feature Perl 5 Needs in 2012

In Features Perl 5 Needs in 2012 chromatic asks the question What's on your list?  Well, my list is long.  But the thing at the top is structured core exceptions.

While I don't think Perl 5's exception throwing syntax is completely solved by Try::Tiny (just as it's time for Perl 5 to have it's own MOP it's time for Perl 5 to have its own exception handling syntax to solve that problem in a well-defined way too) that's not the biggest stumbling block to having awesome exception handling in Perl 5.  The main problem is that Perl 5 still throwing plain old strings as exceptions.

Often I find myself wanting to catch all IO errors, but not runtime stupid-coding errors (My error code is going to handle  a dumb filename, but it's totally not going to know what to do if I've typoed a method name.)  This is hard because my code has to parse the exception string - essentially throw a bunch of regular expressions at it - until it can hopefully figure out what's going on.  And, of couse, I say hopefully because the strings can (and should, as improvements are made) change between versions of perl.

The correct solution is for Perl to throw structured exceptions.  Where all IO errors are a subclass of the main IO error and all bad method calls are a subclass of the DispatchError error or somesuch.  Then I only have to check that $@ isa IO Error and the whole problem becomes simple.

Now in theory someone could build a version of Try::Tiny that had all this logic built in - did all this parsing for me - so it would seem like I had structured exceptions for all the inbuilt errors.  And as long as this module was updated for each version of perl this would probably work and maybe just be good enough.  But it's not the correct solution!  It's going to be slow (oh so slow) and brittle and...bad and right.  I want (no, I demand!) a good and right solution.  Enough with the band-aids!

So structured core exceptions goes to the top of my list.  Which of course means very little since I'm not a core Perl hacker (nor do I play one on TV) and I'm not going to have time or skills to do it myself.  But if the TPF wants to take some of that money I'm paying for core development every month and pay someone to do it, I'd be a very happy Perl programmer. 

Share "Another Feature Perl 5 Needs in 2012"

Share on: FacebookTwitter

blog built using the cayman-theme by Jason Long. LICENSE