Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Friday, June 5, 2009

Flexible extensions with Moose method modifiers

When constructing an API for a library intended to be subclassed by users, you run into the classic tension between power and simplicity. Too many ways to call your code, too many different methods and the user is overwhelmed. Yet if you don't have enough power, the user may by stymied when he reaches some level of complexity.

Moose method modifiers provide a lot of flexibility that doesn't necessarily have to be provided by an explicit API. In some cases they may provide stopgap ways of extending, in other cases they may actually be better than creating more complexity by providing more explicit hooks.

The main processing method for HTML::FormHandler is pretty straightforward:
sub process
{
   my $self = shift;

   $self->clear if $self->processed;
   $self->_setup_form(@_);
   $self->validate_form if $self->has_params;
   $self->update_model if $self->validated;
   $self->processed(1);
   return $self->validated;
}
The above method is called when a form is processed:
   my $form = MyApp::Form::User->new;
   $form->process( item => $user, params => $c->req->params );
The form is something like:
   package MyApp::Form::User;
   use HTML::FormHandler::Moose;
   extends 'HTML::FormHandler::Model::DBIC';

   has_field 'username' ( required => 1 );
   has_field 'some_attr';
A common need is to have particular fields be required in some circumstances and not in others. So maybe it would be nice to have some kind of callback to determine whether the field is required or not... But a simple Moose method modifier on one of the methods called in the 'process' routine will do the trick just fine:
   before 'validate_form' => sub {
      my $self = shift;
      $self->field('some_attr')->required(1)
         if( ...some condition... );
   };
Now maybe there's some magical API syntax that could achieve the same thing, but really this is pretty straightforward and easy. Maybe there's some additional database update that you want to do that's not directly related to the fields, such as recording that the user updated his record. Another Moose method modifier comes to the rescue:
   before 'update_model' => sub {
      shift->item->user_updated;
   };
...where 'user_updated' is a method on your DBIx::Class result source that sets a flag or an updated time or whatever you want. The same result could be achieved by subclassing the methods of course, but the method modifiers can also be used in Moose roles, making it possible to split up your form pieces into nice chunks that can be reused in multiple forms.
   package MyApp::Form::Options;
   use HTML::FormHandler::Moose::Role;

   has_field 'opt_in';
   has_field 'something_else';
   after 'validate' => sub {
      ...do some specific cross validation...
   };
   before 'update_model' => sub {
      ...some db processing...
   };
And then a form class could just be a collection of roles:
   package MyApp::Form::User;
   use HTML::FormHandler::Moose;
   extends 'HTML::FormHandler::Model::DBIC';
   with 'MyApp::Form::Options';
   with 'MyApp::Form::Login';
   1;
So the power and flexibility of HTML::FormHandler's API are increased without extra code simply by using Moose and its method modifiers. When I was originally hired to program in Perl I wasn't that happy about it, partly because the native Perl OO features are weak and kludgy. But with Moose that's a thing of the past. Perl + Moose are as good as or better than any other object oriented language I've worked with.

Tuesday, May 19, 2009

Complexity happens

I've heard a programmer's job described as 'managing complexity'. People who like programming tend to like other complex systems, like, say D&D. (They also like fantasy in general... no doubt there's some clever comment there that my cold-fogged brain can't work out.)

And yet, one of the primary goals in a programming project is to keep it simple. (Otherwise known as KISS.) So there's this continual tension between simplicity and complexity. Simplicity in one area may require complexity in another. Creating a simple, easy-to-use API often requires more underlying complexity than a non-intuitive but straightforward interface. Though there are occasionally golden moments when things fall into place and you can achieve both greater simplicity of interface AND greater code simplicity. Just don't hold your breath waiting for them...

Problems that seem simple to start with acquire complexity when you add features, when you handle more use cases. Simple, stupid things like the fact that you don't get anything in CGI parameters for an un-checked checkbox introduce irregularities that flow through to surprising corners of code. Decisions made about what it means to not have a particular parameter or have it set to empty cascade through formerly pristine and clean lines of code.

I'm not quite sure whether this is a complaint or simply a report. Sometimes the logical complexity is fascinating. You poke something to see what happens; you try some new way of factoring to see if that magical moment of greater order occurs... And then sometimes you can hardly stay awake and certainly can't concentrate, and you find yourself surfing Amazon for some new fantasy novel that's a lot more your speed.

Or desperately trying to think of something to write that's somehow remotely related to Perl programming. Because you foolishly committed to DOING THAT in a moment of insanity.

Let me just repeat that key phrase a couple of more times, in case foolish sites that count dumb things and claim they mean something aren't paying attention: Perl programming Perl programming Perl programming

Thanks. I'm done blathering now. I think I've got enough of a word count.