20 July 2012

A wild Moose appears!

Going to be occupied with basic architecture stuff for a while, and won't have much to show off. But I want to retain as much momentum as possible, so here's a short post about some cool Moose features!

Method Modifiers

Moose makes it really easy for a subclass, role, or other part of your class structure modify the methods you would ordinarily get. The manual defines method modifiers better than I can, so instead I'll show you an example of where I'm using them.

I'm making a library class called "Decurse::DecurseApp" that forms the basis of a curses-based perl application with a main loop and widgets and input handling and all that sort of thing. I want to be able to use that in application code that I write, and have that code just add the extra main loop business it needs on top of the DecurseApp structure.

In other object systems, there's plenty of patterns you can use to allow for 'hooks' into DecurseApp code, but Moose provides a neat, simple way of doing it. Here's the basics of DecurseApp:-

package Decurse::DecurseApp;
use utf8;
use Moose;

# Set $self->exit(1) for graceful exit from the main loop.
has 'should_exit' => (is => 'rw', isa => 'Bool', writer => 'exit');

# Augmentable mainloop.
sub mainloop
{
   my $self = shift;

   while ( ! $self->should_exit()) {
      # Render everything that needs to be rendered
      $self->draw();

      # Wait a moment to see if there's any input.
      $self->handle_input();

      # User application specifics can go here by augmenting mainloop.
      inner();
   }
}

sub draw
{
   my $self = shift;
   # Do all the basic widget-drawing infrastructure here.
}

sub handle_input
{
   my $self = shift;
   # Do the complicated keyboard-reading stuff here.
}

1;


Once the class is fleshed out, users of the code could define a perfectly functional DecursesApp class just by writing:-

package MyApp;
use utf8; 
use Moose; 

extends 'Decurse::DecurseApp';

1;


... but that's no fun. The whole point of structuring things like this is so that MyApp can do things inside the main loop that are specific to the application we want to write. We can use Moose's method modifiers to augment the mainloop method with our own code at the point where inner() gets called, and we can also define things that should happen before or after the usual draw() and handle_input() methods.

package MyApp;
use utf8;
use Moose; 

extends 'Decurse::DecurseApp';

augment 'mainloop' => sub
{
   my $self = shift;
   # Game event processing goes here!
};

after 'draw' => sub
{
   my $self = shift;
   # Just my own little test junk for now.
   Decurse::Palette::set_colour("bold red on black");
   Curses::addstr "This text is red on black.\n";
};

before 'handle_input' => sub
{
   my $self = shift;
   # Since the real handle_input isn't actually implemented yet...
   Curses::getch();
   $self->exit(1);
};

1;


If the syntax looks a little awkward, there's the MooseX::Declare module that enhances things with keywords like 'class', 'role' and 'method'. I'm not using that yet since I'm only just learning the Moose basics, and want to see how it works with the vanilla perl5 syntax first.

Random side-note

The Blogger interface kinda sucks when trying to put <code> or <pre> tagged text in here. Surely there's a better way?