Docendo discimus

$self->explain

  • Calendar

    July 2009
    M T W T F S S
     12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
  • Archives

  • Recent Posts

  • Bling

Archive for July, 2009

Catalyst is easy – something useful

Posted by brunorc on July 30, 2009

Recently I haven’t had much time for writing, because I found a new toy. Somewhat demanding and time consuming toy. But even this toy couldn’t have completely attract all my attention, so some small part of it was still left in the dark corner of my consciousness labelled “Perl”. Soon I discovered that using Perl – and, of course, Catalyst – I can have even more fun playing with my new toy.

Now a short digression: the toy itself is not very new, only I was successfully introduced to it lately. It is the collectible card game, Magic: The Gathering. You may like it, you may hate it, or you may just have no clue about it – it doesn’t matter, as I still want to keep the profile of this blog: Perl, Catalyst, Moose and crap. Well, you can count Magic into the “crap” part if it makes you feel better.

Making long thing short, all I need is a small application, that would serve as a “life counter”. The game is based on the idea of the duel of two wizards. One must die, and that happens when his life counter reaches 0. When the game starts, both counters have the value of 20. As far as I know there is no upper limitation, so in the course of the game wizards can gain even more life. Now you may see why the game is so popular amongst programmers: they are often accused of having no life, and here they start with 20 and can gain even more!

Although there is the possibility of doing Magic in the multiplayer mode, we’re going to start with simple duel of two wizards. Let’s summarize the requirements:

  • two players – we will probably like to know her names
  • every player has a counter, that starts with 20 (with no upper limit); the game ends when one counter reaches 0
  • during the game we want to change the state of both counters, regardless of player’s turn; that’s because there’s a lot of possibilities in Magic, for instance a player can be dealt the damage caused by his own spell that has been redirected

I’m going to keep things simple, so there is no state, no authorization, nothing fancy. Everything will be put in the Root controller and there will be one model to keep the state of the game. I start with creating the application:

catalyst.pl MagiCount

Notice the advise, added recently:

Change to application directory and Run "perl Makefile.PL" to make sure your install is complete

It is probably a good idea to do this. Now I’m going to create my favourite View, and then the brand new Model:

script/magicount_create.pl view TTS TTSite
# output skipped
script/magicount_create.pl model Game

Now I’m ready to create the code for the Controller:

sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
    
    $c->detach( $c->model('Game')->game ? 'play' : 'create' );
}

I want my actions to be controlled solely by the state of the game. So if the game is defined, I will be able to play – otherwise there’s a need to create one.

sub create :Private {
    my ( $self, $c ) = @_;

    my $p1 = $c->req->params->{player1};
    my $p2 = $c->req->params->{player2};
    my $ct = $c->req->params->{counter};

    if ( $p1 and $p2 ) {
        if ( $p1 eq $p2 ) {
            $c->stash->{errmsg} .= 'Names should be different';
        }
        else {
            $c->model('Game')->init(
                [ $p1, $p2 ],
                $ct ? [ $ct, $ct ] : undef
            );

            $c->detach('play');
        }        
    }

    $c->stash->{template} = 'create.tt2';
}

In lines 4-6 I’m going to catch the params if they are sent from the form. Then I check them against the obvious condition – players names should be different, otherwise they would start to quarrel about the score. If anything is OK, I intialize the game with the optional parameter – initial value of the counter. It is doubled and passed as an array reference. In that case I’m ready to play, so I use the detach method to exit this action and go directly to the action named play.

Apart from detach there’s also forward, which returns after the action the flow was forwarded to is completed.

Anyway, if the request doesn’t contain any parameters, I’m going to show the template with appropriate form.

sub play :Private {
    my ( $self, $c ) = @_;

    if ( $c->req->params->{close} ) {
        $c->model('Game')->close;

        $c->detach('create');
    }
    
    my $c1 = $c->req->params->{counter1};
    my $c2 = $c->req->params->{counter2};

    $c->stash->{game} = $c->model('Game')->game;

    if ( $c1 or $c2 ) {
        $c->stash->{game}->update( [ $c1, $c2 ] );
    }

    $c->stash->{template} = 'play.tt2';
}

Now I start from the end: I close the game if requested, and then immediately go to create a new one. But if that’s not the case, I’m going to fetch the parameters from request, get the game object, and then update its state. Now the game is ready to be shown, so the only missing thing is the template.

sub default :Path {
    my ( $self, $c ) = @_;
    $c->response->body( 'Page not found' );
    $c->response->status(404);
}

That is the standard default action. Nothing fancy.

Now to the Model. I decided to build a simple, singleton class. It will have one arrayref for the names, and then arrayref of arrayrefs for the state of the counters. Maybe it is a little bit convoluted, but will be easier to use in the View later.

my $game;

sub init {
    my ( $class, $names, $counters ) = @_;
    
    $game = {
        finished => 0,
        players  => $names,
        counters => [ $counters || [ 20, 20 ] ],
    };

    return bless $game, ( ref $class );
}

First I create my singleton, which becomes a hash reference in line 6, and then – in line 12 – is blessed into the full-fledged class. I pass my names and counters as array references. This is the best way if I want to pass multiple arrays to a function; Perl 6 will have support for names, types and sizes of parameters, but in Perl 5 we have to use array references.

sub update {
    my ( $self, $updates ) = @_;

    push @{ $game->{counters} },
        [ map { $game->{counters}->[-1]->[$_] + $updates->[$_] } (0, 1) ];
    
    $game->{finished} = 1
        if grep { $_ <= 0 } @{ $game->{counters}->[-1] };
}

This method gets the array reference of modifiers and then applies them to the current state. I treat the $game->{counters} as an array, so I can push into this. Then I fetch the last item from this array (using the -1 index), and update its elements with respective values. Result of this operation is then pushed.

After this I check if both sorcerers are still alive. If one has deceased, I consider the game as finished. Again, I use the negative index to get the most up-to-date values.

sub close {
    undef $game;
}

sub game {
    return $game;
}

sub players {
    return $game->{players};
}

sub counters {
    return $game->{counters};
}

sub finished {
    return $game->{finished};
}

Closing the game means putting the singleton in the undefined state. The rest is just a bunch of accessors.

Now comes the View. No style, just pure HTML.

<h3>Create your game</h3>
<div style="color: red">[% errmsg %]</div>
<form action="" method="post">
      <label for="player1">Name of the first player</label>
       <input type="text" name="player1" size="12" /><br />
      <label for="player2">Name of the second player</label>
       <input type="text" name="player2" size="12" /><br />
       <label for="counter">Initial counter state</label>
       <input type="text" name="counter" size="2" /><br />
       <input type="submit" name="go" value="GO!" />
</form>

And the same for playing:

<form action="" method="post">
<table>
        <tr>
                <th><strong>[% game.players.0 %]</strong></th>
                <th><strong>[% game.players.1 %]</strong></th>
        </tr>
[% FOREACH state IN game.counters %]
         <tr>
                <td>[% state.0 %]</td><td>[% state.1 %]</td>
         </tr>
[% END %]
[% IF game.finished %]
         <tr>
                <td colspan="2">
                    <input type="submit" name="close" value="GAME OVER!" />
                 </td>
          </tr>
[% ELSE %]
        <tr>
                <td>
                        <label for="counter1">Modify</label>
                        <input name="counter1" size="3" />
                </td>
                <td>
                        <label for="counter2">Modify</label>
                        <input name="counter2" size="3" />
                </td>
         </tr>
         <tr>
                <td colspan="2">
                    <input type="submit" name="go" value="GO!" />
                 </td>
          </tr>
[% END %]
</table>
</form>

That’s all. Take out your decks!

Posted in Catalyst for intimidated | Tagged: , , , , | Leave a Comment »

Top Searches (July)

Posted by brunorc on July 27, 2009

Update: Diego pointed out something very important, so this post had to be significantly updated.

Someone came here searching for catalyst mason variables stash. It brought me to the question: how do I access stash from the Mason view? The answer is: the same way as in TT(Site) view. Mason page is just passed the stash content, so it’s enough to declare variables we want to use in our view.

A small example: let’s put something like this in our controller:

sub uruk_hai :Local :Args(0) {
    my ( $self, $c ) = @_;

    $c->stash->{message} = 'We will feast on manflesh!';

    $c->stash->{template} = 'uruk_hai.html';
}

Now if we want to use TT(Site), our template will look like this:

<h2>[% message %]</h2>

In Mason it looks like this:

<%args>
# don't die if there's no message
$message => undef
</%args>
<h2><% $message %></h2>

Moreover, the Catalyst::View::Mason module declares some useful globals, like $c, $base and $name, so we can choose between Mason and TT without sacrificing anything.

Posted in Catalyst for intimidated | Tagged: , , , , , , , , | 2 Comments »

Catalyst is easy – installation on Windows

Posted by brunorc on July 19, 2009

Perl introduces itself

Strawberry Perl introduces itself on Windows

Some of us use Windows. That’s OK, there are cases when this platform is useful. It can be also useful for Catalyst development. There are at least two solutions:

  • install VMWare, then Ubuntu, then go to the relevant post
  • install Strawberry Perl on Windows, and then install Catalyst

Good news are: if you like Windows, you can use it for Catalyst development (I wouldn’t use it for deployment, though).

First of all – Windows does NOT come with Perl. Really! And the same goes for Python and Ruby. Quite strange… but easy to overcome. There is a nice Perl distribution – Strawberry Perl – prepared especially for Windows. It has nice no-brain installer, so everything you have to do is to go the website and pick the version you like.

Yes, there are two. The only problem with choosing one is that while 5.10 is more modern and has some goodies, it may affect the ease of your Catalyst development – it is known as the “unknown error” bug. The 5.10.1 should be released soon, but at the moment 5.8.9 would be a safe bet.

Anyway, I assume you picked one, downloaded it, installed and you are ready to get Catalyst. It will require some tampering with command line, so get ready.

CPAN shell in action

CPAN shell in action

First open the command line window and run the command perl -V. This will show you all the information about the installed Perl interpreter (see the picture – in my case it is 5.10.0 version, but the 5.8 should be very similar). When you are sure you have Perl installed on your system, go to the Start -> Programs -> Strawberry Perl -> CPAN client. You will get another command line window with CPAN shell running inside. It starts with downloading some bits of necessary information, chewing it and when it’s ready, it welcomes you with cpan > prompt. Now type

install Catalyst::Runtime

and press Enter. As your system is quite empty, you will have to install some packages Catalyst depends on. It takes some time and you may have to answer some questions or confirm some actions now and then. When this operations finishes you will be able to run Catalyst applications on your Windows machine.

CPAN shell asking for dependencies

CPAN shell asking for dependencies

But the point is to develop such applications. For this we need the Catalyst::Devel package, but before we go into this, we have to fight with some minor glitch. The problem is, that Template Toolkit installation breaks on Windows. It breaks only on building the XS module (the pure C backend, which is faster than the Perl one; we can sacrifice it, as on development machine we don’t need to be blazingly fast), and the good news are, that we are asked if we want to use it, and we can say “no”. However the bad news are, it tries to use it during the test phase of the installation, then fails, and then refuses to install.

So, my suggestion is to overcome it that way:

cpan > force install Template

Now we will be able to install it even if tests fail. Use it sparingly, cause most of the time tests should determine if package is ready to be installed, and passing the test suite is a good measure for this readiness. Also remember to answer “no” while being asked for XS – otherwise the build process will break and even the force option won’t help.

Catalyst::Devel asking about "unknown bug"

Catalyst::Devel asking about 'unknown bug'

After that we are ready to finish the installation:

  • install Catalyst::View::TT
  • install Catalyst::Devel – if you decided to use 5.10, remember to answer yes for the “unknown error” question (default is no)
    Update: I checked it on Strawberry Perl 5.10.0.5 (from April 2009) and it is not fixed in this version

Now you are ready to begin your adventure with Catalyst on Windows. You have to remember two things, though:

  • use catalyst.bat instead of catalyst.pl for creating your application
  • while using the scripts created by Catalyst, like script/myapp_create.pl or script/myapp_server.pl, you have to specify that they should be run as under Perl interpreter, so you have to use the perl script/myapp_server.pl
  • don’t use script/myapp_fastcgi.pl – at the moment there’s no easy support for running Perl webapplications using FastCGI on Windows

That’s all. The 99% of the Catalyst development should be OS agnostic. And every other Perl module you would need can be installed using CPAN shell. Enjoy!

Posted in Catalyst for intimidated | Tagged: , , , , , , , , , , | 10 Comments »