God hath given you one face and you make yourselves another.

In the continuing story of Moose and D&D I left off with what I think was a slick use of Moose:Util to load in a Character's race.

As all long term D&D players now you will eventually run into a player who says the following;


Well in my present 'Character' class I allow this as my race is just a string.

  has 'race' =>(
	is		=>'ro',
	isa		=>'Str',

So this post will look into how to handle this sort of sin against nature.

In the old days it was simple, each race was a class and if you tried something like this

  my $character = HalfOrger->new(...);

You would just die. I could try and set my 'race' attribute to allow only a valid 'Race' class.

has 'race' =>(
	is		=>'ro',
	isa		=>'Race',

but then I loose the advantage of race being a role and in essence it would just be the same as my traditional solution.

I could also set up the attribute with a custom type so I can limit the choices to a list of races or I could do that in the Character "BEGIN".

I could of course just do what we did 25 years ago and set up the UI so one will never be able to select such a oddity.


But then again these are the days of that funny thing called the 'web' with everyone and their sister is creating restful APIs all over the place so gone are the days where my UI could save me from validating some data input.

Also while playing this game you hear over and over again.


My regular DM lets me do that??

So when validating a race it would be nice to allow whoever is working with the game code to add new races as required and not to have update a fixed magic list of races or some value in a 'Class'

So where in Moose do we do this? We have seen 'BEGIN' which is nice but it is called 'after' an instance is created which is not exactly what I want. I could also try something like this

around BUILDARGS => sub {
	my $orginal = shift;
	my $class    = shift;

but at that point I do not have a class, or anything really. This call seems to be mostly used to munge up init arguments that are in a different format than Moose or the class are expecting. So back to BEGIN I go;

There is 'Moose::Util::find_meta' but that one just checks the current object for the role or class in question so not much use. I guess I just let it die if it doesn't find the race? However, I will get something like this back;

Can't locate HalfOrger.pm in @INC (@INC contains: D:logs D:\....\c  C:/Dwimperl ) at C:/Dwimperl/perl/site/lib/Module/Runtime.pm line 317.

Which is somewhat useful, to other programers, as it is telling me I do not have a 'HalfOrge.pm' file in my path. Does expose my path and this is not a Moose::Exception so it does not get across the point that you are missing a role/race not just a PM file?

Fortunately Moose::Util lets me throw a Moose exception. Of course, when one is playing like this one has to be careful not to throw an exception that is way off base. (Unless you are a chaotic evil programmer) so in the end I have

package Character;

use Moose;
use Moose::Util qw(apply_all_roles  throw_exception );

sub BUILD {
      my $self = shift;
      my ($attr) = @_;
		apply_all_roles($self, $self->race());
      if ($@){
      	throw_exception( "NeitherRoleNorRoleNameIsGiven", {message=>"Race: ".$self->race()." is Invalid! Perhaps ".$self->race().".pm in not in the Path?"})
		    	unless (blessed $@->isa());
		die $@;

So I have a Moose exception that is close to what I and gets the point across that a Race is missing, I also kept the at least the suggestion to check the path in the message.

Note as well If my 'eval' returns some sort of Object (ie another Moose::Exception) I do not want my kludged error but but rather the actual error from moose.

The old Adage 'with great power comes great responsibility' comes to mind here.

Not something one would do everyday but interesting none the less


Why is race an attribute at all? Why not just a role? Something like this?

Leave a comment

About byterock

user-pic Long time Perl guy, a few CPAN mods allot of work on DBD::Oracle and a few YAPC presentations