Musings of a Software Inventor, or, THE LACK THEREOF

Home


2010.07.10 Hotel Yorba

I love this song so much, and it is as fun to play as I imagined.

(mp3 file)

0 Add Comment


2010.07.10 HTML-FormHandler, KiokuDB, and Continuity

I saw some article about HTML::FormHandler, and decided I'd give it a try. I'm probably not really using it as intended... and I'm also not pushing this confluence of tech nearly as far as it can go.

#!/usr/bin/perl

use strict;
use MooseX::Declare;
use KiokuDB;
use Continuity;

First we just pull in some libraries. We'll use MooseX::Declare for fun :) . Now let's make a lovely base class for our forms, to set up some good defaults an an updater.

# Base class for all forms
class Form
extends HTML::FormHandler {
  has owner => (is => 'rw');
  our $form_count = 0;
  has '+name' => ( default => sub { return "f-" . $form_count++ } );
  has '+html_prefix' => ( default => 1 );
  method update {
    if($self->validated) {
      my $v = $self->value;
      foreach my $field (keys %$v) {
        eval { # ignore validation issues :)
          $self->owner->$field($v->{$field})
            if $self->owner->can($field);
        }
      }
    }
  }
}

That stuff about the form name is so we can display multiple forms on a page, each with it's own unique name. The update method ties my form to my actual object. I ignore errors for now... though really that's losing a big part of what HTML::FormHandler provides.

class BasicPersonForm
extends Form
with HTML::FormHandler::Render::Table {
  has '+field_list' => ( default => sub {[
    name => 'Text',
    birthday => 'Text',
    save => { type => 'Submit', value => 'Save' },
  ]});
}

Here I'm building on my Form base, making a form specific for my Person class. This is declaratively specifying which fields to show and in what order, along with a lovely save button. In theory I'll be able to expand this into subforms and compose them and so on.

Though I'm roughly OK with listing inputs for the form, it annoys me to have to specify the types. That, at at least a basic level, should be readable from the fields to which this form is connected. So one thing I'm noodling is using the rendering and validation, but importing the types from the data class. I think the Form::Processor::Model::DBIC does this a bit.

class Person {
  use MooseX::Types::DateTimeX qw( DateTime );

  has name => ( is => 'rw', default => sub { '' } );
  has birthday => ( is => 'rw', isa => DateTime, coerce => 1 );

  method basic_form { BasicPersonForm->new( owner => $self, init_object => $self ) }
}

My Person class is straightforward. I used the DateTimeX thingie to auto coerce stuff magically. And that basic_form method is just a helper to create a form for a given instance.

sub main {
  my $r = shift;
  my $db = KiokuDB->connect(
    'dbi:SQLite:phone.db',
    create => 1
  );
  my $scope = $db->new_scope;

  # This is a new person, pending some data
  my $new_person = Person->new;

  while(1) {

    my @people = $db->grep(sub { ref($_) eq 'Person' })->all;
    my @forms = map { $_->basic_form } @people;
    foreach my $form (@forms) {
      $r->print($form->render);
    }
    $r->print("<h3>Add person:</h3>");
    my $new_person_form = $new_person->basic_form;
    $r->print($new_person_form->render);

    $r->next;
    my $params = { $r->params };

    foreach my $form (@forms) {
      $form->process( $params );
      $form->update;
      $db->update( $form->owner );
    }

    $new_person_form->process( $params );
    $new_person_form->update;
    if($new_person->name) {
      $db->insert( $new_person );
     
      # And now queue up another new one
      $new_person = Person->new;
    }
  }
}

This is a little Continuity app, so main gets called and passed $r, which is the request handle. After setting up our KiokuDB (with SQLite backend), I enter an endless loop of showing the forms and the processing the results. Oh, and I create a special Person instance for holding a new person.

In the loop, I grab all the people objects in the DB at once (this is a demo, so I skipped iterator/paging stuff), make a form for each, and then render the forms. I also render the new-person object in case they want to add a new one.

Next I call '$r->next', which waits for the user to submit the forms. This is Continuity magic, which inverts the web app to make it more like an interactive command line prompt. You can kinda think of this line as "$input = <STDIN>". Once that comes back I put the submitted data into a variable and use it to update each of the forms (and updating each back into the database).

Finally I check to see if they submitted data for the New-Person, and if there is anything I insert it into the DB.

# Get things going
Continuity->new->loop;

Last but not least, we start the Continuity loop, which starts looking for requests and calling main() for each new session.

So I found this all pretty interesting, and it overlaps with some of my other object-rendering experiments. I could probably add to this for pulling field metadata from objects into forms and organizing subforms and so on. Maybe next time :)

0 Add Comment


2010.06.04 Mercurial Bisect

So at work we're switching over to Mercurial (hg), and I'm trying out some of the more advanced features. One which I've been looking forward to for a while is 'bisect' (which git and others have also).

The idea is straightforward. I have a unit test that fails. I know that it worked like a month ago or something. Someone committed something between then and now that made it fail. But what was it? Bisect will systematically help find it -- start in the middle, and if it was good there go back half of the remaining time. If bad, go forward. In just a few chops like this it will narrow down to the exact commit that caused an issue.

This works well in Mercurial (and Git) because it is really really fast to revert to a previous version of your code. By far the longest part of the process in the process was executing the unit test itself!

Here are the steps I went through. I'm using Mercurial 4.3, by the way.

bwilcox@wickedwitch:~/dev-rel172$ hg bisect -r

First I "reset" the bisect. That just means, let's begin!

bwilcox@wickedwitch:~/dev-rel172$ hg bisect -b

Now I marked the current revision as "bad", since my test fails.

bwilcox@wickedwitch:~/dev-rel172$ hg bisect -g 51303
Testing changeset 51497:3f631045f30d (196 changesets remaining, ~7 tests)
124 files updated, 0 files merged, 26 files removed, 0 files unresolved

I looked back at history before I started this and decided that this test definitely passed at revision 51303 -- that was just before our last release and all the tests passed. So here I'm saying that I know for _sure_ that it was "good" at revision 51303.

Now comes the fun part.

bwilcox@wickedwitch:~/dev-rel172$ hg bisect -c 'bin/test_suite.pl bin/t/default/pnr_phase1/14-task_acquisition_approval.t | grep PASS'
Changeset 51497:3f631045f30d: bad
Result: PASS
Changeset 51404:8b0f3381e3f5: good
Changeset 51449:f3b668e05928: bad
Changeset 51415:ebc770d284d3: bad
Result: PASS
Changeset 51409:dfd5706dc765: good
Result: PASS
Changeset 51412:e33660e38ea0: good
Result: PASS
Changeset 51413:d1390b75856f: good
Changeset 51414:d39f58b490ab: bad
The first bad revision is:
changeset:   51414:d39f58b490ab
user:        chiggins
date:        Tue May 18 21:34:51 2010 +0000
summary:     RT: https://rt.liquidation.com/Ticket/Display.html?id=733702

In case it isn't clear, the command that I ran is:

hg bisect -c 'bin/test_suite.pl bin/t/default/pnr_phase1/14-task_acquisition_approval.t | grep PASS'

This instructs Mercurial to keep going, and use my test_suite.pl command to decide whether a particular revision is "good" or "bad". The "-c" parameter runs what you give it and looks at the exit status to decide if it is good or bad. It just so happens that the "grep" command returns exit status depending on whether it found something :) . So I'm grepping for the "PASS" that comes at the end of a standard perl test suite to indicate that the current revision is good.

And then off it runs! Test... bad. Test... good. Test... bad. And so on, narrowing it down to the exact revision which was the first to fail the test!

From there I took a look at that revision, and immediately saw what the problem was. Easily fixed, and now my test suite passes with a clean bill of health.

Aw hells yes.

Comments on TLT - 2010.06.04 - Mercurial Bisect

2 Comments.

Hmmm, the documentation on mercurials wiki seems to be out of date. http://mercurial.selenic.com/wiki/BisectExtension?action=show

When you run the test through -c is that using a static script from the initial revision or does that test need to be in all the revisions, possibly altering the script if it was changed in a previous revision? I would think it uses a static script, in which case it would mean that you don't necessarily have to have the test in the 'good' revision but can create it after the fact.

-- lungching 2010-06-09 12:32 UTC


In the example I provided, my test case is actually located inside of my repository. Thus each execution is indeed running the version of the test from the currently selected revision. In this case I like that, because the test itself might have broken and I'd like to detect that case.

If, however, you need a static test, you can just point to something outside of your repository and it should work the way you describe.

-- awwaiid 2010-06-09 16:11 UTC

2 Add Comment


2010.02.07 Cultured Perl

Tags: Perl

You might have stumbled upon some IBM developerWorks articles titled "Cultured Perl" while googling for random things. I think it's awesome and that it isn't linked to enough. Just a few weeks ago the latest article came out, Cultured Perl: Storage management on Amazon S3, which is well written and up-to-date with the latest technologies.

Thank you, Cultured Perl!

0 Add Comment


2009.12.16 Better Creox Example

So I was right, I completely messed up the previous example and it wasn't electrified by Creox at all. But I've rectified this situation, and for your listening pleasure (pain?), here is a smaller snippet of just the guitar part of the song, before-and-after.

Original acoustic guitar (mp3 file)
Electrified by Creox (mp3 file)

0 Add Comment

More...

META INFORMATION: This is the technical blog and wiki of Brock Wilcox (awwaiid). Entries focus on my current projects, interests, and sometimes life events. If you'd like you can check out the list of All Entries or the RSS Feed. I also have a LiveJournal syndication feed for LJ friends.