Wednesday, August 26, 2009

Moose: adding attributes to a base class

I'm using FormHandler for an example here, but the technique is general.

FormHandler forms are Perl classes with a number of attributes including an array of Field classes with another (probably too large) number of attributes. A few of the field attributes are validation and data related, but a lot of the other ones are related to producing HTML for display. Despite the too many attributes in the field classes, users still want yet more attributes. One of the FormHandler users was developing forms that used a javascript form package and wanted to interface the FormHandler forms to the javascript forms. To do this, he wanted an additional attribute in the fields to store information that would be used by extJs.

One possibility would be to subclass every last single field and add an additional attribute. This does not sound like either fun or a good idea. A much better alternative was to use Moose to add attributes to the base class by applying a role containing the attributes.

So I started out by creating a small role containing a single attribute:

   package MyApp::Field::Extra;
   use Moose::Role;
   has 'my_extra_attribute' => (is => 'rw', isa => 'Str' );
   1;

Now I needed someplace to apply the role. The BUILD method of the user form looks like a good place. Like good Moose classes, all of the fields have '__PACKAGE__->meta->make_immutable' in them. So in order to apply a role we have to temporarily make the class mutable and then make it mutable again. So I make the class mutable, apply the role using Moose::Util, then make it immutable again:

   my $class = 'HTML::FormHandler::Field';
   $class->meta->make_mutable;
   Moose::Util::apply_all_roles( $class->meta, ('MyApp::Field::Extra'));
   $class->meta->make_immutable;

Using a test file I make a form class using this code in the BUILD method, create an instance of the form, and find that the fields now have an additional attribute that I can retrieve and set.

This looks good until I try to set the new attribute in a 'has_field' declaration:

   has_field 'my_field' => ( my_extra_attribute => 'some_value' );

Ooops. This doesn't work. I'd forgotten that the fields are constructed in the base class BUILD method which fires before my form class's BUILD method. So now I need someplace else to move my role setting that will happen before the base class BUILD. Maybe after BUILDARGS...

   after 'BUILDARGS' => sub {
      my $class = 'HTML::FormHandler::Field';
      $class->meta->make_mutable;
      Moose::Util::apply_all_roles( $class->meta, ('MyApp::Field::Extra'));
      $class->meta->make_immutable;
   };

This works. Now I can treat the new attribute just like an original field attribute. And it's a lot easier than subclassing every field...

There are other ways of achieving the same thing. You could add an attribute instead of applying a role. But roles are more general purpose and flexible, so I'm satisfied with this solution for now.

And I definitely <3 the flexibility that comes with Moose.

Sunday, August 2, 2009

Fields: Moose attributes or arrays of objects?

Moose attributes are lovely things. They're a part of Moose that helps to make programming fun again. But having a magical, Dr. Who screwdriver doesn't mean that you can lose all of your other tools. If you have a nail, a good old-fashioned hammer is the right tool.

Moose type constraints are great things to catch programmer errors, typos, incorrect objects. Throwing errors for this kind of type failure is a good thing. You want the programmer to notice that something has gone wrong even if he hasn't had his morning coffee. I first did object oriented programming with C++. I still remember all the hoops that we had to jump through to handle construction time errors (many C++ compilers didn't have exceptions yet). The problem of what to do if you can't create a valid object is the problem that Moose attributes are designed to solve, and throwing errors is the proper thing to do.

But in my opinion, when the task is validation, not construction of a valid object, throwing errors isn't always the best way. You want to take an input string, hand it to the validator which examines it and hands it calmly back saying "good" or "not good", or some more specific error message which can be presented to a user--a user who is not the programmer. Handing it to a construct which instead has a tantrum and throws it back at your head - picture the programmer frantically dodging this way and that, trying to catch the message and de-cipher the problem to present some reasonable message to the user - introduces unnecessary difficulties.

When I started doing the work that turned into HTML::FormHandler I looked at the possibility of turning the field objects into Moose attributes, and saw a number of problems. One of the problems was simply names. Any attribute in the form that was not a field became a name that was not allowed as a field name. This sucked. Of course the programmer could pick some other name and have some additional attribute trait that specified the "real" field name. Yuck, but I suppose it would have worked.

There was a lot of functionality in the field class already, so it seemed to make most sense to have the attributes be of a field object type. But then the Moose attribute type validation doesn't actually apply to the job of the field classes -- validation -- but instead to whether or not this is a valid field, which is appropriate but not the same thing at all. Moving all of the attributes in the field class into some Moose attribute metaclass didn't appeal at all. I didn't see the point. The fields worked fine as objects. If you didn't ever want to provide assistance in constructing HTML or allow validation using simple object methods or an endless list of other things, I suppose you could go in that direction. But I already had working code and working functionality that I liked that I wasn't willing to give up for no reason other than ... um, fashion? Mooseish purity?

So there would be a bunch of Moose attributes that had a field class type. Then you need to be able specify all of the attributes to use to construct the fields. This is not insurmountable, but to do it in a non-irritating way (for me) would require some Moose-ish sugar. Fine, that would work. The next problem is that the fields are really a kind of set and you often need to iterate over them. So there would need to be some kind of array (or other collection) pointing to all of these objects. This was starting to look suspiciously like the array of field objects that I already had.

At this point I started wondering what advantage there was to making the fields Moose attributes. Sure, you could do $form-> sometimes. If the field name didn't conflict with other attributes. But I was already planning to have nested sub-fields and arrays of repeatable fields and that model didn't fit well at all with the Moose attribute idea. It's not like it was necessary to be able to do method modifiers or other Moose-ish things on the fields. They were objects. You could do whatever you wanted in them already. You could already use method modifiers (etc) on all of the parts of the form validation process.

I'm sure there would have been some way of making fields-as-Moose-attributes work. But this is Just A Program. The idea is to make it work in as simple a way as possible (but no simpler), not to make sure that it uses the latest shiny thing whether it provides any advantage or not. (Well, that's my goal anyway. Your goal may be playing with shiny new technologies and tools. :) )

The Collection::Array of field objects was working fine. It's still working fine. I still don't see any advantage to turning the fields into Moose attributes that isn't canceled out by some other disadvantage or complication. The current architecture, which I'm happy with, would be just silly shoehorned into a bunch of Moose attributes.

I guess I won't win the Most Unnecessary Moose Metaprogramming (MUMM) award (which it sometimes seems to me that the Mooserati are competing for). Shrug.