Docendo discimus

$self->explain

  • Calendar

    September 2017
    M T W T F S S
    « Sep    
     123
    45678910
    11121314151617
    18192021222324
    252627282930  
  • Archives

  • Recent Posts

  • Bling

Posts Tagged ‘separation of concerns’

Catalyst is easy – do the wrong thing

Posted by brunorc on July 13, 2009

Someone landed here while searching for the phrase get catalyst object in model. Before I wrote in one of previous entries:

methods of the model don’t have access to the Catalyst context object, because, in fact, they shouldn’t have it

I first searched for the possibilities of doing it. And there are such possibilities, which shouldn’t be surprising, as Catalyst allows one to write all the crap one wants to write – it will just help to write it faster.

So if you really want to use the context object inside your Model (or View), take a look at the ACCEPT_CONTEXT component – and please read the WARNING WARNING WARNING section. In many cases passing some arguments should be sufficient. While being able to do absolutely any crap you can fancy is a nice possibility by itself, it also pays to read the manual of your chainsaw before the first launch. At least you can take advantage of turning the pages with any of your hands.

Having said this, I’d like to point that ACCEPT_CONTEXT even made its way to Catalyst Advent Calendar. It means that sharp tools can be also useful.

P.S. I planned to write about the installation of Catalyst on Windows XP, however it has been postponed (but not forgotten).

Advertisements

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

Catalyst is easy – rondo alla API

Posted by brunorc on June 21, 2009

I’ve already shown that Catalyst is able to process some user input. Really! And not only process, it can even discover if the form was really filled. And since you’re going to write web applications, you probably know that there is no spoon, no browser and no user on the other side of the request. Or, at least, there may be no user.

And if there is no user, what’s the sense of showing the form? There may be another action, designed especially for non-human users. Or, to avoid being accused of racism – some formless users.

This action needs to make use of another attribute – Args – because it needs to receive one argument, which would be the string that is expected to be added to the list. The minimal code for this action could look like this:

sub add :Local :Args(1) {
    my ( $self, $c, $newitem ) = @_;

    push @items, $newitem;
}

except it wouldn’t work, because every action has to have distinct name; api_add looks like a nice name, so after renaming the action we can try.

We can try to see the error message.

OK, that was intentional. But if we are here, why not to spend a while looking at it and try to understand it? First there’s an error message, that informs us that Catalyst could not find a view to forward to. But the application has no view at the moment, so it is not strange Catalyst couldn’t have found it. Anyway, in case it could help us resolving the error, Catalyst shows us four important objects it keeps in its context: Request and Response (which were mentioned in the previous part) along with Stash and Config. While Config sounds rather obvious – it apparently holds the configuration data for the application – Stash requires some explanation: it is just a big bag we can use for the data used while creating the Response.

This problem can be solved if there’s some output, passed to the body:

sub api_add :Local :Args(1) {
    my ( $self, $c, $newitem ) = @_;

    push @items, $newitem;

    $c->res->body("OK");
}

Now it works, but the code pushing the new item into list is doubled. What if there is a new requirement which says “no underscore in item names, and treat spaces as separators”? This code should be isolated, and both actions should only act as wrappers. As always, Catalyst makes it easy:

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

    if ( $c->stash->{newitem} =~ s/_//g ) {
        $c->stash->{message} = 'Removed illegal underscore(s). ';
    }

    my @new_items = split / /, $c->stash->{newitem};

    push @items, @new_items;

    $c->stash->{count} = @new_items;
}

There is a new action attribute: Private. It means that this action cannot be called directly by using URL, it is reachable only from other actions. And since those actions call the _add action, they have to pass some parameters and – last but not least – they would probably like to know what happened. Hence the use of stash; action gets its parameter newitem from the stash, and then populates the stash with an optional message and the information about the count of added items.

sub api_add :Local :Args(1) {
    my ( $self, $c, $newitem ) = @_;

    $c->stash->{newitem} = $newitem;

    $c->forward('_add');

    $c->res->body( $c->stash->{count} ? "OK" : "ER" );
}

Now the api_add action doesn’t do much: it just packs the $newitem into stash and then creates some response, based on the count of added objects. It also uses the forward method to call the private action. The same will happen to the add action:

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

    my $optional;

    if ( $c->req->params->{newitem} ) {
        $c->stash->{newitem} = $c->req->params->{newitem};

        $c->forward('_add');

        if ( $c->req->params->{addit} eq 'Add it!' and $c->stash->{count} ) {
            my $count = $c->stash->{count};
            $optional = "<h4>Thank you for adding $count items!</h4>";
        }

        if ( $c->stash->{message} ) {
            $optional .= '<h4>' . $c->stash->{message} . '</h4>';
        }
    }

    my $template = qq[
<html>
    <head>
        <title>Add new item</title>
    </head>
    <body>
        <div align="center">
            <h3>Add something!</h3>
            <form method="get">
                <input type="text" size="24" id="newitem" name="newitem">
                <input type="submit" id="addit" name="addit" value="Add it!">
            </form>
            $optional
        </div>
    </body>
</html>
];

    $c->response->body( $template );
}

Although the forward method passes the @_ list as well as the whole $c object, it is a good idea to extract the newitem and store it into stash, instead of doing it inside the _add action. Again, this is the separation of concerns – wrapper unwraps (isn’t it logical?) the parameters, while the actual action just acts on well defined content of the stash.

Coda

The whole thing about Args attribute and the API stuff were quite nice, but pretty wrong. Now the application has an action that changes its state (in other words – the action has some side effects), and this action is accessible using the GET request. What if someone bookmarks this URL and then use it repeatedly? It can of course be solved by not allowing to insert the same item twice (how?), but that way we handle the effect, not the cause. That said, the action can be improved easily:

sub api_add :Local :Args(1) {
    my ( $self, $c, $newitem ) = @_;

    if ( $c->req->method eq 'POST' ) {
        $c->stash->{newitem} = $newitem;

        $c->forward('_add');

        $c->res->body( $c->stash->{count} ? "OK" : "ER" );
    }
    else {
        $c->res->status(405);
        $c->res->body('ER');
    }
}

Status code 405 means “Method not allowed”, so it is perfect for this situation. To check the if the code works as expected we need something more than a browser. There is a command line utility called lwp-request, which comes with Perl LWP module and even has some documentation, so I’ll just use it:

$ lwp-request -se http://localhost:3000/api_add/beer          
405 Method Not Allowed
Connection: close
Date: Sun, 21 Jun 2009 17:06:41 GMT
Content-Length: 2
Content-Type: text/html; charset=utf-8
Client-Date: Sun, 21 Jun 2009 17:06:41 GMT
Client-Peer: 127.0.0.1:3000
Client-Response-Num: 1
Status: 405
X-Catalyst: 5.80005

ER

Fails as expected! Now the proper incantation (when lwp-request asks for entering content, just press Ctrl D):

$ lwp-request -se -m POST http://localhost:3000/api_add/beer
Please enter content (application/x-www-form-urlencoded) to be POSTed:
200 OK
Connection: close
Date: Sun, 21 Jun 2009 17:06:53 GMT
Content-Length: 2
Content-Type: text/html; charset=utf-8
Client-Date: Sun, 21 Jun 2009 17:06:53 GMT
Client-Peer: 127.0.0.1:3000
Client-Response-Num: 1
Status: 200
X-Catalyst: 5.80005

OK

This is rather coarse way of testing the application. But Catalyst also provides one with sophisticated testing tools, however it is a different topic.

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

Catalyst is easy – less scary interlude

Posted by brunorc on June 10, 2009

Last time I was showing the famous, dreadfully obfuscated Perl code. It even included the horribly cryptic $_ variable! Now the code should look like this:

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

    my $list = join( "\n",
                map { "<li>$_</li>" }
                    qw/beer bacon pierogi/ );

    $c->response->body( <<END );
<html>
    <head>
        <title>My items</title>
    </head>
    <body>
        <ul>
        $list
        </ul>
    </body>
</html>
END
} 

The <<END tells Perl print everything until you reach the line starting with END. This technique is known as “here doc”. But Perl prints even more, since there is no “$list” printed – instead the variable is interpolated, which means the content of the variable will be seen. The same would happen if the here doc was written like <<"END".

But what if we wanted to print the string “$list”? We could escape the $ sign, using backslash: \$list. Otherwise we could use single quotes, writing <<'END'. In this mode everything would be printed ‘as-is’. If we wanted to mix variables with actual $ signs we should use double quotes and escaping, since there’s no tricky escape which would cause the variable to be interpolated inside single quotes (at least I don’t know it).

With this knowledge and qq operator the code can be transformed into something more clean:

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

    my @items = ( 'beer', 'bacon', 'pierogi' );
    my $list = '';

    foreach my $item ( @items ) {
        $list .= "<li>$item</li>\n";
    }

    my $template = qq[
<html>
    <head>
        <title>My items</title>
    </head>
    <body>
        <ul>
        $list
        </ul>
        <p>Only \$5!</p>
    </body>
</html>
];

    $c->response->body( $template );
}

The qq operator with its delimiters works exactly the same way as a pair of double quotes (can you imagine how the q[] operator would work?).

Now the code has two parts: lines 4-9 prepare the data, while lines 11-23 take care of presentation. Such separation of concerns can be quite useful and is often called Model-View-Controller (MVC). And although Catalyst is very MVC oriented, it allows one to decide by oneself how much separation should be used.

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