Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

gav (2710)

  (email not shown publicly)
AOL IM: flufflegavin (Add Buddy, Send Message)

Hacker in NYC.

Journal of gav (2710)

Tuesday July 12, 2005
02:41 PM

Back from the dead!

So it's been six months without a journal entry, it doesn't seem like that long.

I've been suffering from a career crisis, one so bad I was too busy to notice it. Now that I've made the decision to seek employment elsewhere, I feel more positive about being a Perl programmer again. I just have to solve the minor problem of finding that 'elsewhere'.

Tuesday January 18, 2005
10:07 AM

Hackers & Painters

I'd been flicking through Paul Graham's Hackers & Painters over the weekend when I came to this paragraph:

Not every kind of hard is good. There is good pain and bad pain. You want the kind of pain you get from going running, not the kind you get from stepping on a nail. A difficult problem could be good for a designer, but a fickle client or unreliable materials would not be.

Strangely enough I was trying to explain the same thing last week.

Wednesday January 05, 2005
05:39 PM

rsync include/exclude

I'm a big fan of rsync and use it on a daily basis, but I just can't get my head around the include/exclude syntax. It seems to work in an opposite way to how I think. For example, to recursively find all files beginning with 'foo':

find . -name 'foo*'

But with rsync you can't really tell it what you want, just what you don't want and end up with this madness:

rsync --include='*/' --include='foo*' --exclude='*' . /dest

Tuesday December 28, 2004
01:13 PM

i don't trust you

Tuesday December 21, 2004
04:43 PM

Javascript woes

Given the following form:

<form id="test" action="" method="post">
<input type="hidden" name="action" value="example">

What will document.forms.test.action and document.forms.test.getAttribute('action') be?

The former is the hidden field, the latter is the form's action.

The problem was that I couldn't change the HTML and had to pass the form to somebody else's javascript which assumed that form.action would work. After a bit of head scratching, I found the workaround isn't too painful:

var form = new Object();
form.action = test.getAttribute('action');
test.setAttribute('action', form.action);

Monday December 13, 2004
06:03 PM

Paging Modules on CPAN

There's three modules I've used from CPAN for paging, Data::Page, Data::Pageset, and Data::SpreadPagination (the later two are subclasses of the first). One of the advantages of these modules is they work really well as something to pass into Template Toolkit.

These are three useful modules to add to your CPAN toolkit, each one does things a little differently and solves what at first looks like a simple problem, but saves you from both typing and embarassing off-by-one errors.

Data::Page by Leon Brocard

my $page = Data::Page->new($total_entries, $entries_per_page, $current_page);

[% FOREACH page IN [page.first_page .. page.last_page] %]
    [% IF page == current_page %]
        <b>[% page %]</b>
    [% ELSE %]
        <a href="?page=[% page %]">[% page %]</a>
    [% END %]
[% END %]

With total_entries = 100, entries_per_page = 10, current_page = 3, you'll get something looking like:

1 2 3 4 5 6 7 8 9 10

Data::Pageset by Leo Lapworth

One of the issues I ran into with Data::Page is that if you have a large number of pages then you have to somehow control the set of pages you output. Data::Pageset takes care of this for you. In this example $pages_per_set are the number of pages you want to display at any one time.

my $pageset = Data::Pageset->new({
    total_entries    => $total_entries,
    entries_per_page => $entries_per_page,
    current_page     => $current_page,
    pages_per_set    => $pages_per_set,

[% IF pageset.previous_set %]
    <a href="?page=[% page.previous_page %]">&lt;</a>
[% END %]
[% FOREACH page IN pageset.pages_in_set %]
    [% IF page == current_page %]
        <b>[% page %]</b>
    [% ELSE %]
        <a href="?page=[% page %]">[% page %]</a>
    [% END %]
[% END %]
[% IF pageset.next_set %]
    <a href="?page=[% page.next_set %]">&lt;</a>
[% END %]

With total_entries = 1000, entries_per_page = 10, current_page = 13, pages_per_set = 10, you'll get something looking like:

< 11 12 13 14 15 16 17 18 9 20 >

Data::SpreadPagination by Jody Belka

This works in similar way to Data::Pageset, in that you're going to be showing a subset of the total number of pages. The difference is that Data::SpreadPagination allows you to output paging that allows the user to jump to the start and end of the results easily.

my $pagination = Data::SpreadPagination->new({
    totalEntries     => 1000,
    entriesPerPage   => 10,
    currentPage      => 13,
    maxPages         => 10

[% FOREACH page IN pagination.pages_in_spread %]
    [% IF !page.defined %]
    [% ELSIF page == current_page %]
        <b>[% page %]</b>
    [% ELSE %]
        <a href="?page=[% page %]">[% page %]</a>
    [% END %]
[% END %]

With the same data as before, you'll end up with:

1 2 ... 10 11 12 13 14 15 16 ... 99 100

Wednesday December 08, 2004
02:36 PM


I've been finding more and more uses for Hash::AsObject recently, it's great when you need an object, but you don't really care what that object is. For example today I had to write some reporting code, and call some existing code that expected a "cart" object to be passed in. It did something like:

foreach my $item ($cart->contents) {
    printf "%d of %s for \$%.2f\n",
        $item->qty, $item->code, $item->unit_price;

The problem was that I didn't have a "cart" to pass it. One of the great things about dynamic languages is that it doesn't matter if it's a "cart" or not, just that it looks like a cart:

my @cart = map {
    $_->{unit_price} = defined $_->{sale_price}
        ? $_->{sale_price} : $_->{price};
} @{ $dbh->selectall_arrayref(
        'SELECT qty, code, price, sale_price FROM cart',
        { Slice => {} }

my $cart = Class::Object->new;
$cart->sub('contents', sub { return @cart });

Class::Object is useful in this case to get around the issue of not being able to return a list.

Tuesday December 07, 2004
12:39 PM

scrolling in screen

I'm a big fan of screen, but something happened from version 3.09.11 (RH 7.3) to version 4.00.02 (FC3) that stopped putty being able to scroll the window. After some poking I found that adding:

termcapinfo xterm* ti@:te@

To my .screenrc fixes the problem.

Tuesday November 30, 2004
11:09 PM

Domain Specific Testing

One of the things I do is implement tax and shipping rules for clients. As an interesting aside, we use a early web services technique invented at Yahoo! where we recieve a POST of all the data and return the results in custom headers. Anyway, as you can expect, it's often a challenge to capture the exact rules required and they tend to be pretty complicated.

I'd just been working on adding rules for AK and HI for a client that only previously shipped to the 48 contiguous states and the added complexity made me worried that I made a mistake. I wanted to write tests, but I wanted something at a higher level than just writing Perl code, and more importantly I wanted a test suite that the client can add to. I came up with the following format:

product: 9848rdc
quantity: 1
options: Size: 148
product: 9852rdc
quantity: 2
options: Size: 152
state: AK
country: US
method: Ground
cost: 24.95
product: bk7ma
product: 9848rdc
options: Size: 148
state: AK
cost: 39.95
method: Ground

I then wrote a script that reads this in, turns each section into three tests (using Test::More): we were able to POST, we didn't get back an error, and we got back the right cost. We've now got a format that allows non-programmers to write tests, and they're often the best people to write them because they know what they expect the system to do.

Thursday November 25, 2004
11:45 PM

World of Warcraft (day 2)

I finally get to play and spend an enjoyable few hours getting the hang of things and getting my character to level 7. Then the difficulty changes from "I can kill most things without too much of a problem" to "I can't kill anything without a problem and if I meet two or more monsters I'm fucked".

Then I find out that while it may be cool that the world is huge but when I die and become a ghost and have to spend five minutes running back to my body to respawn with 50% health, and then die, then spend five minutes running back, that this isn't fun. Pairing up with a wizard I found that I can die just as quickly but only get half the XP for killing anything.