Just A Summary : http://www.bofh.org.uk/articles.rss en-us 40 Piers Cawley Practices Punditry Class decomposition and a handy delegation pattern <p>There&#8217;s something satisfying about reaching the point when you can&#8217;t decompose an object any further and all your methods are tiny and do one thing &#8211; it&#8217;s especially gratifying when you learn something new in the process. Sadly, it doesn&#8217;t happen as often as I&#8217;d like, there&#8217;s usually annoying bits and pieces where you have to placate the language in some fashion that breaks the flow of what you&#8217;re writing.</p> <p>As I get a better handle on the way <a href="http://search.cpan.org">MooseX::Declare</a> has changed Perl, I&#8217;m finding I have to do much less in the way of placation.</p> <p>Here&#8217;s an example. For context, I&#8217;m writing a traffic shaping tool. The basic client interface needs to look something like:</p> <div class="CodeRay"><pre>$policy->current_weight # => a percentage between 0 and 100</pre></div> <p>Not much of an interface really. The requirements state that we should be able to specify weights with 15 minute granularity for every day of the week. Our problem becomes one of mapping from a time to a number between 0 and <code>7 (days) * 24 (hours) * 4 (quarters) - 1</code> and looking up the weight in an array.</p> <p>Here&#8217;s my first cut:</p> <div class="CodeRay"><pre>use MooseX::Declare; class WeightVector { use DateTime; has vector => ( isa => 'Array[Int]', is => 'ro', required => 1, ); method current_weight { my $now = DateTime->now; my $offset = ($now->wday_0 * 7 * 24 + $now->hours) * 4 + int($now->minutes / 15); return $self->vector->[$offset]; } }</pre></div> <p>Which is, I suppose, perfectly respectable. However, <code>current_weight</code> isn&#8217;t filling me with delight. First it finds the current time, then it converts the time into an offset, then it uses the offset to lookup the weight in the vector. Let&#8217;s introduce a method to find the weight at a specific time<a href="#decomp-motivation"><sup>1</sup></a>, the relevant code becomes:</p> <div class="CodeRay"><pre>method current_weight { $self->weight_at(DateTime->now); } method weight_at (DateTime $time) { my $offset = ($now->wday_0 * 7 * 24 + $now->hours) * 4 + int($now->minutes / 15); return $self->vector->[$offset]; }</pre></div> <p>And again, we could rest here, but again, we&#8217;re doing two things. We&#8217;re converting from a time to an offset, then we&#8217;re looking up the value in the vector. Type conversions tend to happen again and again, so it&#8217;s good if we can specify them separately. We <em>could</em> write a <code>time_to_offset</code> helper method, but we&#8217;re in Mooseland now; there&#8217;s a better way. Let&#8217;s introduce a formal Moose type and define a coercion for it. Here&#8217;s the type definition stanza of the code. I&#8217;ve taken the opportunity to add types which do bounds checking for the vector as well, while I&#8217;m about it.<a href="#decomp-why-inline"><sup>2</sup></a></p> <div class="CodeRay"><pre>use MooseX::Declare; class WeightVector { use DateTime; use Moose::Autobox; use MooseX::Types -declare => [qw(SlotOffset VectorOfWeights PercentageInt)]; use MooseX::Types::Moose qw(ArrayRef Int); use constant TOTAL_SLOTS = 7 * 24 * 4; BEGIN { subtype PercentageInt, as Int, where { 0 <= $_ &#38;&#38; $_ <= 100 }, message { "$_ does not is not an integeter between 0 and 100" }; subtype VectorOfWeights, as ArrayRef[PercentageInt], where { $_->length == TOTAL_SLOTS } message { "Vector must have ".TOTAL_SLOTS." entries, not ".$_->length }; subtype SlotOffset, as Int, where { 0 <= $_ &#38;&#38; $_ < TOTAL_SLOTS }; class_type 'DateTime'; coerce SlotOffset, from 'DateTime', via { ($_->wday_0 * 7 * 24 + $_->hours) * 4 + int($_->minutes / 15) }; # Let's allow clients not to care about using DateTime by allowing # them to simply pass the results of calling 'time()' - It's not like it's # still 1970... coerce SlotOffset from subtype(as => Int, where { $_ > TOTAL_SLOTS } via { to_SlotOffset(DateTime->from_epoch(epoch => $_) }; } has vector => ( is => 'ro', isa => VectorOfWeights, required => 1, } ...</pre></div> <p>Now, if we were programming in plain old Moose, we could rewrite <code>weight_at</code> like so:</p> <div class="CodeRay"><pre>sub weight_at { my $self = shift; my $offset = to_SlotOffset(shift); $self->vector->[$offset] }</pre></div> <p>Which would be pretty sweet, but we&#8217;re using MooseX::Declare; there&#8217;s an even better way:</p> <div class="CodeRay"><pre>method weight_at (SlotOffset $offset does coerce) { $self->vector->[$offset]; }</pre></div> <p>Sweet!</p> <p>We could stop there, but I had an insight. What we&#8217;ve got here is basically a wrapper around a delegation to our vector, and Moose&#8217;s new native types feature let us express the delegation to the vector quite neatly, like so:</p> <div class="CodeRay"><pre>has vector => ( isa => 'VectorOfWeights', is => 'ro', required => 1, traits => ['Array'], handles { weight_at => 'get', }, ); ... around weight_at (SlotOffset $offset does coerce) { $self->$orig($offset); }</pre></div> <p>This could be overkill when vector is a simple <code>ArrayRef</code> as we have here, but the pattern of delegating declaratively in the attribute definition and then munging arguments in an <code>around</code> handler is applicable to more than just argument transformation. A typical delegation pattern involves having the delegating object passing itself in as an argument to the method delegated to. The nature of Moose&#8217;s <code>handles</code> declarations makes that impossible to do within the attribute declaration, but it&#8217;s easy to fix with an around helper:</p> <div class="CodeRay"><pre>around delegated_method (Any @args) { $self->$orig($self, @args); }</pre></div> <p>(If you&#8217;re wrapping more than one method in this fashion, you should probably consider using a plain old Moose style <code>around</code> handler, which lets you wrap multiple methods with <code>around @delegated_methods =&gt; sub {...}</code></p> <p>So, at the end of all that, and after we&#8217;ve extracted our Type declarations into WeightVector::Types, we have:</p> <div class="CodeRay"><pre>use MooseX::Declare; class WeightVector { use WeightVector::Types qw(VectorOfWeights PercentageInt SlotOffset); has vector => ( isa => 'VectorOfWeights', is => 'ro', required => 1, traits => ['Array'], handles { weight_at => 'get', }, ); method current_weight { $self->weight_at(time()); } around weight_at (SlotOffset $offset does coerce) { $self->$orig($offset); } }</pre></div> <p>And we&#8217;ve pushed all knowledge of DateTime off onto our type declarations and gained a boatload of handy bounds checking. We&#8217;ve also got a new tool for handling tricky delegation setups in the <code>handles</code>/<code>around</code> combo.</p> <h3>Notes</h3> <h4>Motivation</h4> <p>I realise that this looks like a radical decomposition of the class with very little motivation, but it was driven by tests and by some other requirements that I&#8217;ve removed from the body of the post. In particular, the type coercions were driven by the need to build particular vectors for testing, a key method being:</p> <div class="CodeRay"><pre>method set_weight (PercentageInt $weight, SlotOffset $from does coerce, SlotOffset $to does coerce) { ... }</pre></div> <h4>Type coercion is wonderful</h4> <p>Generally, I&#8217;m not a fan of static typing. I&#8217;m from the &#8220;duck type all the way&#8221; school of programming,<a href="#decomp-haskell"><sup>3</sup></a> so most of my method declarations have no type declarations. But type declarations, especially ones that coerce, make so much sense on methods that make up the public protocol of a class. I only use type declarations on internal methods when I need a narrower coercion, or if I&#8217;m using <a href="http://search.cpan.org/dist/MooseX-Multimethods">MooseX::Multimethods</a>, which I still haven&#8217;t used for anything but exploration.</p> <h3>Updates</h3> <p>Thanks to Chris Dolan for spotting that I&#8217;d got the <code>SlotOffset</code> coercion completely wrong. The real code&#8217;s doing the right thing, but that&#8217;s what comes of recreating code from memory.</p> <p id="decomp-motivation"><sup>1</sup> This was actually motivated by trying to write tests to verify that the weights were correctly set.</p> <p id="decomp-why-inline"><sup>2</sup> I&#8217;m declaring these in a <code>BEGIN</code> block of the class itself mostly for explanatory purposes &#8211; there&#8217;s a good case for moving them out into a separate file and pulling it in with <code>use</code>.</p> <p id="decomp-haskell"><sup>3</sup> Except during my periodic attempts to learn Haskell. I&#8217;ve learned Haskell at least three times now.</p> Wed, 25 Nov 2009 16:49:00 -0600 urn:uuid:0a03d666-a7cd-44bb-a6cb-1deb0963579c pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/11/25/class-decomposition-and-a-handy-delegation-pattern#comments The Practice of Programming Perl perl practiceofprogramming moosex declare multimethods Moose autobox http://www.bofh.org.uk/2009/11/25/class-decomposition-and-a-handy-delegation-pattern Twice now <p>In Ruby, when you&#8217;re doing division on integers, things can get a little counter intuitive. For instance, <code>6/4</code> is obviously <code>1</code>. At least, it is until you decide that you&#8217;d rather have numbers that behave a little more like &#8216;real&#8217; numbers and you do <code>require 'mathn'</code>, a module in the Ruby standard library (ie, it comes as part of ruby). Then you enter a whole new world of rational maths, where <code>6 / 4</code> returns <code>3/2</code>.</p> <p>Several very fine and useful Ruby gems rely on the workings of <code>mathn</code>, including <code>ruby-units</code>, which is a spiffy tool for avoiding problems when one team is working in kilometres and the other in miles and it&#8217;s no fun at all when your space probe is suddenly incommunicado.</p> <p>Other fine and dandy Ruby gems include <code>ultrasphinx</code> and <code>webrat</code>. Both of these two (and no doubt others) rely on the the fact that <code>302/100 == 3</code>.</p> <p>Hmm&#8230; can you see my problem?</p> <p>Please, if you&#8217;re working on a gem that you intend to publish widely, then adopt the practice of <em>never</em> trusting that dividing an integer by another integer will return a third integer. You&#8217;re not even making yourself a hostage to some other gem, you&#8217;re making yourself a hostage to the standard library. Always do <code>(an_integer / another).to_i</code> and your code will be so much more robust.</p> <p>I&#8217;ve got a pull request and lighthouse ticket in for webrat and, once I&#8217;ve hit &#8216;publish&#8217; on this post, I shall be doing the same thing for ultrasphinx, but I&#8217;m sure there are other gems out there with the same problems. Please people, check your assumptions.</p> Wed, 25 Nov 2009 16:49:00 -0600 urn:uuid:1f3f94da-bb77-48e6-8192-fe4c0781b8ed pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/11/25/twice-now#comments The Practice of Programming Ruby practiceofprogramming ruby robustness http://www.bofh.org.uk/2009/11/25/twice-now Test::Class::Sugar 0.3, no, 0.4 <p><strong>tap</strong> <strong>tap</strong>... Is this thing on?</p> <p>So, I recently noticed that <a href="http://search.cpan.org.uk/dist/Test-Class-Sugar">Test::Class</a> 0.33 got released, which means that <a href="http://search.cpan.org/dist/Test-Class-Sugar">Test::Class::Sugar</a> no longer needs to depend on a development release, and I also noticed that it was embarrassingly easy to throw Test::Class::Sugar into an infinite loop by forgetting which way the <code>&gt;&gt;</code> goes when you want to specify the number of subtests in a test method.</p> <p>So, I&#8217;ve done a quick fix of the infinite loop problem as well and uploaded version 0.3 to <span class="caps">PAUSE</span>, so now you can write your tests like:</p> <div class="CodeRay"><pre>testclass exercises ClassUnderTest { test creation of the class under test { lives_and { isa_ok ClassUnderTest->new, $test->subject; } } ... }</pre></div> <p>without having to jump through the hoops of downloading a development version of Test::Class or worry about accidental infinite loops&#8230;</p> <p>Next up, fix the syntax to either allow both <code>&lt;&lt;</code> and <code>&gt;&gt;</code> as test count specifiers, or come up with a more memorable way of separating the count from the test name.</p> <h3>Update:</h3> <p>Shortly after I released 0.3, Joel Bernstein asked if I&#8217;d be interested in a topic branch to make Test::Class::Sugar work with perl 5.8.</p> <p>&#8220;Of course!&#8221; I said.</p> <p>One day later, there it was. Thank you to Joel and to his employers, <a href="http://www.net-a-porter.com/"><span class="caps">NET</span>-A-PORTER</a> for sponsoring his work. So now, Test::Class::Sugar 0.4 is winging its way to <span class="caps">CPAN</span> and now I have no excuse for not using it at work.</p> Thu, 05 Nov 2009 02:12:00 -0600 urn:uuid:47300df1-2965-4bdb-8747-a8e20948bc1c pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/11/05/test-class-sugar-0-3#comments Perl test class Sugar perl testing release http://www.bofh.org.uk/2009/11/05/test-class-sugar-0-3 Perl 5, version 10.1 <p>At last! Start your compilers everybody.</p> Mon, 24 Aug 2009 02:29:00 -0500 urn:uuid:5a15a958-232f-4d6f-9b06-c731bea3e601 pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/08/24/perl-5-version-10-1#comments Perl perl perl5.10.1 perl5 http://www.bofh.org.uk/2009/08/24/perl-5-version-10-1 Perl: Not just a hobby any more <p>Back in January, I wrote that I was <a href="http://www.bofh.org.uk/2009/01/12/choosing-a-language-for-2009">choosing Perl</a> to revisit as my language for recreational programming.</p> <p>Quite a bit has happened since then. I gave <a href="http://www.bofh.org.uk/2009/05/13/london-pm-presentation">a talk</a> on <a href="http://search.cpan.org/dist/MooseX-Declare">MooseX::Declare</a> to the London Perl Mongers and will be delivering an <a href="http://yapceurope2009.org/ye2009/talk/1802">extended version</a> at this year&#8217;s <a href="http://yapceurope2009.org/ye2009"><span class="caps">YAPC</span>::Europe</a> in August.</p> <p>Then, a week ago, I accepted an offer to work full time as a Perl programmer again. In London.</p> <p>Sing &#8220;Ho!&#8221; for the life of the long distance commuter.</p> Wed, 27 May 2009 00:11:00 -0500 urn:uuid:4da68d0a-290b-4b54-8667-de05363b6b90 pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/05/27/perl-not-just-a-hobby-any-more#comments Perl perl perljob http://www.bofh.org.uk/2009/05/27/perl-not-just-a-hobby-any-more London.pm Presentation Video <p>Back in (crikey) February, I gave a talk at the London Perl Mongers&#8217; technical meeting about Moose for Ruby Programmers and wrote it up <a href="http://www.bofh.org.uk/2009/02/22/moose-for-ruby-programmers">here</a>. Mike Whittaker was in the front row of the audience with his iPhone and, a couple of minutes in, started a voice recording and gave me a copy.</p> <p>So&#8230; finally&#8230; I&#8217;ve taken the time I should have been using to write another article for <a href="http://h-online.com">The H</a> and wrestled the slides and the audio into something like sync and uploaded the results to <a href="http://www.vimeo.com/">Vimeo</a> for your viewing pleasure.</p> <object width="400" height="300"><param name="allowfullscreen" value="true" /><param name="allowscriptaccess" value="always" /><param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=4627327&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" /><embed src="http://vimeo.com/moogaloop.swf?clip_id=4627327&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"></embed></object><p><a href="http://vimeo.com/4627327">An introduction to MooseX::Declare</a> from <a href="http://vimeo.com/user1158507">Piers Cawley</a> on <a href="http://vimeo.com">Vimeo</a>.</p> Wed, 13 May 2009 08:28:00 -0500 urn:uuid:fdbcf204-b3d6-428b-b0df-27db9358f314 pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/05/13/london-pm-presentation#comments The Practice of Programming Perl Ruby presentation moosex declare Moose perl ruby practiceofprogramming http://www.bofh.org.uk/2009/05/13/london-pm-presentation Another conference season, another dumb sexist <blockquote> <p>Mum was often the only women [at British Leyland sales conferences]. In those days it was apparently common for presenters to slip the occasional naked lady into the slides – “just to keep everyone awake”. When this happened, there’d be slightly embarrassed laughter and a few heads would turn to look at mum. Who ignored it. It doesn&#8217;t happen so often any more</p> </blockquote> <p>That was me writing about <a href="http://www.bofh.org.uk/2005/11/02/women-in-open-source">Women in Open Source</a> in 2005. This is <a href="http://www.ultrasaurus.com/sarahblog/2009/04/gender-and-sex-at-gogaruco/">Sarah Allen</a> writing about Matt Aimonetti&#8217;s talk, &#8220;CouchDB + Ruby: Perform like a Pr0n star&#8221; which:</p> <blockquote> <p>If he had left it [the dodgy images] at a few introductory jokes, I would be writing a very different post. Instead the porn references continued with images of scantily-clad women gratuitously splashed across technical diagrams and intro slides. As he got into code snippets, he inserted interstitial images every few slides (removed from the slides below). The first time it happened, he mentioned that he wanted to keep everyone’s attention.</p> </blockquote> <p>Apparently, the difference between 80s truck salesmen and Matt&#8217;s audience is that at least 80s salesmen had the grace to look embarrassed.</p> Tue, 28 Apr 2009 18:14:00 -0500 urn:uuid:bd70cf9f-5607-4899-b5a5-082bfd72705f pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/04/28/another-conference-season-another-dumb-sexist#comments feminism sexism rubyonrails http://www.bofh.org.uk/2009/04/28/another-conference-season-another-dumb-sexist Freedom is in Peril <p>I&#8217;ve just written my first &#8216;real&#8217; real post for the new <a href="http://www.freedomisinperil.org.uk/">Freedom is in Peril</a> website. I&#8217;ll try and keep the political stuff on that blog from now on, but if you&#8217;re at all concerned about the erosion of what has traditionally, if cornily, been called &#8220;British Liberty&#8221;, then I hope you&#8217;ll swing by, and link to, the new site.</p> Fri, 10 Apr 2009 01:18:00 -0500 urn:uuid:0f881377-88fb-4d97-a23c-0efdec36bbc3 pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/04/10/freedom-is-in-peril#comments freedomisinperil http://www.bofh.org.uk/2009/04/10/freedom-is-in-peril Test::Class::Sugar released <p>I&#8217;ve just pushed the second version of <a href="http://search.cpan.org/dist/Test-Class-Sugar">Test::Class::Sugar</a> (first discussed <a href="http://www.bofh.org.uk/2009/03/11/writing-parsers-for-fun-and-convenience">here</a>). It&#8217;s pretty much as discussed in the original article, but after some discussion with <a href="http://www.justatheory.com/">David Wheeler</a>, I&#8217;ve dropped the <code>+uses</code> clause from the <code>testclass</code> declaration in favour of less DWIMmy (and thus easier to explain behaviour).</p> <p>I&#8217;ve also introduced a <code>defaults</code> argument at <code>use</code> time. The only working default in this release is one which lets you specify the prefix used to derive the test class name from the class/module under test. I&#8217;ve documented a couple of extra and so far unimplemented defaults as well.</p> <p>Have a play, you might like it.</p> Mon, 06 Apr 2009 05:15:00 -0500 urn:uuid:874d6a86-64ae-46a8-a02d-a9aad4e865f3 pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/04/06/test-class-sugar-released#comments Perl test class Sugar perl testing devel declare http://www.bofh.org.uk/2009/04/06/test-class-sugar-released Keep calm and carry on my eye <p>This seems like a more appropriate poster somehow:</p> <p style="text-indent: 0;;"><a href="http://xrl.us/beqnw4"><img src="/images/freedom.jpg" title="Freedom Is In Peril Defend It With All Your Might" alt="Freedom Is In Peril Defend It With All Your Might" /></a></p> <p>Can we have this in mugs, t-shirts and other formats please?</p> <p>Thanks to <a href="http://alanfleming.org/2009/03/freedom-is-in-peril/">Alan Fleming</a> for pointing it out.</p> Sun, 22 Mar 2009 16:40:00 -0500 urn:uuid:f353a49c-8673-4ec4-9e10-5cc71fff0cf2 pdcawley@bofh.org.uk (Piers Cawley) http://www.bofh.org.uk/2009/03/22/keep-calm-and-carry-on-my-eye#comments politics posters graphicdesign freedomisinperil http://www.bofh.org.uk/2009/03/22/keep-calm-and-carry-on-my-eye