Stories
Slash Boxes
Comments

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

rjbs (4671)

rjbs
  (email not shown publicly)
http://rjbs.manxome.org/
AOL IM: RicardoJBSignes (Add Buddy, Send Message)
Yahoo! ID: RicardoSignes (Add User, Send Message)

I'm a Perl coder living in Bethlehem, PA and working Philadelphia. I'm a philosopher and theologan by training, but I was shocked to learn upon my graduation that these skills don't have many associated careers. Now I write code.

Journal of rjbs (4671)

Monday May 12, 2008
10:08 PM

all hail chocolateboy (and autobox)

The autobox pragma first showed up a few years ago. It lets you do something like this:

use autobox;

[ qw(wonderful is very autobox) ]
  ->sort->map(sub { ucfirst })->join(q{ })->print;

...to print: Autobox Is Very Wonderful

At first, it was pretty neat, but it required patches to perl. By 2005, it had been rewritten to require no patch, but was still pretty scary and experimental, at least to me. Over the last few years, I've looked over toward autobox a few times, itching to use it all over the place, but never quite willing to do so because of a few significant limitations.

First of all, this didn't work: $array_ref->$method_name

Method names needed to be literals, meaning that it was more difficult to pick a method at runtime with autoboxed values than with standard objects.

More importantly, this didn't work: @array->method

This was important because this wouldn't work either: \@array->method

The precedence of -> is higher than \, so it took a reference to the result of @array->method, which was equivalent (as I recall) to:

(my $x = @array)->method

...so, not very useful.

Over the last few weeks, these two bugs have been addressed. The only thing that I'm still not entirely sold on is that this does not do the right thing:

my @new = grep { ... } @old->sort;

Sure, I could write @old->sort->flatten, but that's not the point. I want the result of sort to be usable as a flat list, the same was @old was. Coding that would require knowing that the invocant of the autoboxing class's method was not a reference to begin with, and that information isn't available.

Still, it's not bad at all. This morning I released a new Moose::Autobox, adding a flatten method to both Array and Hash. I think I see a lot of autoboxing in my future!

Sunday May 11, 2008
11:23 PM

abe.pm's first hacking session

Quite a while ago, I suggested to ABE.pm that we should get together and do some group hacking. When someone said, "HACKATHON??" I said, "Good grief, no!" I just wanted to do some messing about and have fun, without worrying about goals or accomplishments. Also, "hackathon" sounds like the hacking equivalent of running 26.2 miles: gruelling. I wanted to make sure it was clear that the point was to hang out, and that hacking was just the entertainment.

At any rate, since I knew we had our technical venue booked for this past week and I knew that I didn't have any good talks prepared and I knew I wasn't going to get a guest speaker with no notice, I declared that it was time for hacking. For a project, I decided we'd play with a game I was working on earlier in the year. It still has oodles of things that need to be done, ranging from the trivial to the really complex. It uses lots of fun new stuff, and it's just sort of a fun project in general.

I installed Debian (sid) on a virtual machine on my Mac, installed all the prerequisites, git, and some other essentials, and briefly refamiliarized myself with the code we'd be working on. Unfortunately, not all of this happened before the event began -- but that's fine. We still had a good time. A few bugs got fixed, some new ones got revealed, and I think everyone had a pretty good time.

Next time, we should be able to get started much faster. I've made a lot of improvements to the virt's setup based on our experiences that night, and I've learned a few good things about git and some other involved tools.

I'm itching to have another hacking session, but I'll have to wait for July. Our next meeting, in June, is a social meeting. While I'm very tempted to declare that we'll be doing a tech meeting again, that would mean that we would not go to McGrady's for hot wings and a Fahy burger. I don't think I can get behind that kind of trade-off.

10:58 PM

rubric now running under fastcgi

In case anyone has been thinking, "Gosh, I haven't seen anything from rjbs lately," the reason is ridiculous. It's not that I've been busy (but I have) or lazy (but I am). It's that I've been stupid.

When I relocated some of my hosted services to Linode (who, by the way, are awesome), I decided that running Rubric as a CGI script was insane. It's always been slow and inefficient, and I knew I could make it much, much faster by making the process persistent. On my previous host, where TIMS also ran, I saw how much faster Rubric could be under mod_perl, but I had some minor problems and wasn't interested in futzing with Apache, which I vaguely dislike.

On my new host, I switched everything to lighttpd, and I figured I'd use FastCGI. I thought this would be trivial, but the state of the art for moving CGI things to FastCGI seemed to be lousy. Someone finally pointed me at Stevan Little's fantastic FCGI::Engine. It did exactly what I wanted, making Rubric work with no code changes at all... or so I thought.

First, query parameters stopped changing. The first query to hit the daemon would have its QUERY_STRING parsed and nothing else would ever get looked at. I fixed that by shoving an initialize_globals call in Rubric's run method. Things seemed alright until I tried to log in to post. No matter what, HTTP POST requests were coming through with no content. I stared at it until I gave up, and then I couldn't post anything new because I couldn't log in.

That was that, until tonight. I checked on my Hiveminder todo list and saw 78 things to do. (Good grief!) The second one is a reminder to get new App::Cmd stuff done by a week ago, and I thought, "Gosh. I should get that done, and then post an explanation of why the changes are so good."

Then I thought, "I can't post anything new, my Rubric won't let me log in!"

Then I thought, "I need to fix Rubric under FastCGI before I start on any of the other 78 things in my todo list." I guess this is a story about being lazy and busy, too.

I resorted to looking through the source of Rubric, CGI::Application, FCGI, FCGI::Engine, and other things. I finally noticed that FCGI::Engine passed a CGI::Simple query into the code it called -- a fact that was not documented. Once I added the dozen-or-so characters needed to tell Rubric to use that query instead of a newly-built one, everything just worked. Awesome! Once again, it goes to show that reading the source is always a good strategy.

I've filed a ticket suggesting that the docs should mention the parameter.

Now I need to get to work posting old news!

10:41 PM

planned presentation digressions

Here's an idea that occurs to me frequently when making slides for conference tutorials. Sometimes I make a slide and I know that it might be too advanced, but it's a crap shoot. If the vast majority of the audience understands the slide, I can save several slides by not explaining it. If enough people look confused, I want to display those slides. I don't mind making the slides up front, but I'd like to be able to only show them if I think it's needed.

I wonder if there's some way to make good Keynote triggers that would let me say something like: "if you want to explain why 1 + 1 = 1, press F1; otherwise, press Next."

Choose your own slideshow.

I also wonder whether the Visor program that I saw ages ago is triggerable during a slide show. It looks good, and it would be convenient to keep a giant-font terminal ready to slide down to demonstrate answers to hard questions.

Tuesday April 22, 2008
12:11 AM

muxtape.com, itunes, perl, and making mixtapes

muxtape.com is a site where you can build mix tapes by uploading mp3 files. You can find other people's uploads and listen to them. It's the flea market tape swape of 2008. I like it.

A lot of people seem to be using it (I think) like a very, very high-latency radio station. Once in a while they upload a new song, bumping an old one. That's cool, but it's hardly a mix tape. I'd rather put together a collection of songs that play well together, then replace my whole tape with those.

The uploading interface requires that I delete and upload tracks one at a time. It also wants them in reverse order, since new tracks get the first position in the tape. Blech!

What I really want is to build a mix in iTunes, where I can drag things around, preview, and re-think selections. Then, I want to upload that whole playlist at once. I wrote a program to do that. Its source is listed below. The muxtape.com API isn't published or clearly-defined, so who knows how long this will work.

My new muxtape, the first I uploaded with mux-up, is now online.

use strict;
use warnings;

use WWW::Mechanize;
use Mac::Glue qw(:glue);

my $title = $ARGV[0];

my $iTunes = Mac::Glue->new('iTunes');

my $playlist = $iTunes->obj(
  playlist => whose(name => equals => $title)
);

my @tracks = @{ $playlist->obj('tracks')->get || [] };
die "couldn't find tracks in playlist <$title>" unless @tracks;

die "too many tracks" if @tracks > 12;

for (@tracks) {
  die "track too large" if $_->prop('size')->get > 10_000_000;
}

my $mech = WWW::Mechanize->new;

$mech->get('http://muxtape.com/login');

$mech->submit _form(
  with_fields => {
    name => 'USERNAME',
    pass => 'PASSWORD',
  },
);

$mech->get('http://muxtape.com/organize');

my @ids = $mech->content =~ m{li id="(song[0-9a-f]{32})"}g;

for my $id (@ids) {
  $id =~ s/^song//;
  print "deleting $id\n";

  $mech->post(
    'http://muxtape.com/organize/ajax',
    Content => "command=delete&args=$id",
  );
}

for my $track (reverse @tracks) {
  $mech->get('http://muxtape.com/upload');
  my $path = $track->prop('location')->get;
  print "uploading $path\n";
  $mech->submit_form(with_fields => { file => $path });
}

my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my @lt    = localtime;
my $date  = "$month[ $lt[4] ] $lt[3]";

$title =~ s/.+: //;
$mech->post(
  'http://muxtape.com/settings/ajax',
  Content => qq{command=bannercaption&args="none"&banner=$date: $title},
);

print $mech->content;
Friday April 18, 2008
11:26 AM

fixing automounted fat16 fs permissions

The old FAT filesystem, still used by many USB mass storage devices like my new USB key drive, does not have a good concept of permissions. That is: not even as good a concept as UNIX. It's common that the default behavior when mounting a FAT filesystem on unix is to set everything a+rwx. I'd rather not have files mounted a+x, so normally I could set a mask in my fstab options. On OS X, though an automounter is used that doesn't seem to be configurable on a per-filesystem-type level.

Is there no good way to reduce the maximum mode of a FAT filesystem's files?

Thursday April 17, 2008
11:15 PM

syncing all my git repositories

Inspired by my recent adventures in on-plane USB flash drive git repository swapping, I ordered a USB storage device. It seemed like getting a micro SD reader would be fairly multipurpose and expandable, so I ordered a teeny tiny micro SD reader that came with a 2 GB card and adapters for mini and regular SD. This thing is tiny. If I put a quarter on top of it, you can barely see it.

The next task, of course, was to make it really easy to clone all my work onto it, both for easy access and for use as a swappable git repository in dire situations, like the flight to YAPC. I threw this program together. It's rough around the edges, but I imagine that it will be very useful in the future, and that I might expand its features a bit for more general use.

#!/usr/local/bin/perl
use strict;
use warnings;
use Path::Class;

my $ROOT = dir("/Volumes/RJBS-KEY");

print "key drive not mounted; skipping\n", exit unless -d $ROOT;

my %remote = (
  'code/projects' => [ 'git.codesimply.com'   => '/git/*'        ],
  'rjbs/code'     => [ 'git.rjbs.manxome.org' => 'git/code'      ],
  'rjbs/conf'     => [ 'git.rjbs.manxome.org' => 'git/conf'      ],
  'rjbs/talks'    => [ 'git.rjbs.manxome.org' => 'git/talks/*'   ],
  'rjbs/writing'  => [ 'git.rjbs.manxome.org' => 'git/writing/*' ],
);

sub clone_from_to {
  my ($remote, $path) = @_;

  my ($remote_leaf) = $path =~ m{/?([^/]+)/?\z};

  my ($local_root, $local_leaf) = $remote =~ m{(.+)/([^/]+)\z};

  my $is_starry = $remote{$remote}->[1] =~ m{\*\z};

  my $clone_in = $is_starry
               ? $ROOT->subdir($remote)
               : $ROOT->subdir($local_root);

  my $pull_in = $clone_in->subdir($remote_leaf);

  my $prefix = $pull_in->relative($ROOT);

  if (-d $pull_in) {
    chdir $pull_in;
    print "$prefix: $_" for `git pull -q`;
  } else {
    $clone_in->mkpath;
    chdir $clone_in;
    my $host = $remote{$remote}->[0];
    my $root = $path =~ m{^/} ? '' : '/~rjbs/';
    $path =~ s/ /\\ /g;
    my $url = "ssh://$host$root$path";
    print "$prefix: $_" for `git clone -q $url`;
  }
}

for my $remote (keys %remote) {
  my ($host, $path) = @{ $remote{$remote} };

  my @dirs;
  if ($path =~ m{(.+/)\*\z}) {
    $path = $1;
    @dirs =`ssh $host find $path -type d -mindepth 1 -maxdepth 1`;
    chomp @dirs;
  } else {
    @dirs = $path;
  }

  clone_from_to($remote, $_) for sort @dirs;
}
Monday April 14, 2008
02:06 PM

minor hate: autoload and universal

I just spend twenty minutes or so on this nonsense.

A number of our objects get decorated by Mixin::ExtraFields::Hive, which lets us say:

$object->hive->some->random->path->to->datum->SET(1);

It's a nice convenient way to store arbitrary hierarchical data about stuff.

I was trying to store the "moniker" of the test object being stored:

$object->hive->test->moniker->SET($moniker);

This kept failing, saying I couldn't call "SET" on "hive." What?

Well, UNIVERSAL::moniker was getting in the way. Argh! I hate UNIVERSAL. I will never abuse it outside of Acme again.

10:27 AM

yard work continues

Yesterday, Kip (and the rest of his clan) came over and we finished Phase II of Project Better Back Yard. I'd already dug up quite a bit of the earth around the two posts. I was "thrilled" to find that just beside the post nearer the house, there was another post! It had probably been cut off before the still-there post was planted, but I was still gobsmacked. It had a much more reasonable base, only a few inches across, and the concrete was awful. I broke out most of it with a hand sledge.

Kip estimated that each concrete anchor for the remaining posts would weigh about four hundred pounds, and guessed that trying to remove them was just asking for trouble. I agreed. We dug out a small trench beside each anchor and laid them sideways and buried them about a foot underground. I'll have to make a note, on the deed, that future owners should beware. Maybe I'll just make a treasure map and hide it in the cistern in an antique scroll case.

Anyway, now the back yard is not totally flat, but it's all soil and grass on top, so the next step is to get a roto tiller, turn everything over, flatten everything out, and lay down new grass and plants. Hopefully that will happen in two weeks and then we'll have a yard that MJ can run around in and that won't be the second-worst eyesore among all the shared yards in our lot.

Saturday April 12, 2008
07:30 PM

addex, mutt, and ascii

After my last set of poking at making Addex do 7-bitization of funny characters, I did some more poking around and learned some things that helped me get everything working just fine. I've made a new release of Addex and the Apple address book plugin, so now anyone can have his friend José show up with an alias of jose by updating his Addex config to include:

[App::Addex::Output::Mutt]
unidecode = 1

This replaced my lousy unicode-to-ascii conversion with Sean M. Burke's presumably awesome Text::Unidecode. It catches every case I caught, and probably a lot more that I haven't had to worry about yet. I saw the tests spitting bizarre southeast Asian characters to the screen and was glad I never had to think about it.

I also found out that the simple rule with Mac::Glue is that you either get back Unicode or MacRoman. From looking at the source, I think the Unicode will always be in a string that is_utf8, so I just decode any non-is_utf8 string from MacRoman.

Having done that, without using Text::Unidecode, mutt was still confused. Then I realized that it was all my fault: I was not setting the write filehandle to utf8 mode, so the bytes were getting mangled. Once I did that, the file made a lot more sense. Of course, it still meant that I would have to type in funny characters if they were in the "before I hit tab to tab complete" part of a name, so I still wanted to downgrade to the English alphabet. Text::Unidecode quietly replaced my code, and then I added a config option and it's done. Anyone using Addex with contacts who have silly foreign names should update. My mutt is a lot nicer to look at now.