Chouette

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN


            $c->task('db')->selectrow_hashref(q{ SELECT * FROM sometable WHERE id = ? },
                                              undef, $id, sub {
                my ($dbh, $row) = @_;

                die $c->respond($row);
            });

        Checkout options can be passed after the task name:

            $c->task('db', timeout => 5)->selectrow_hashref(...);

        See AnyEvent::Task for more details.

EXCEPTIONS
    Assuming you are familiar with asynchronous programming, most of
    Chouette should feel straightforward. The only thing that might be
    unfamiliar is how exceptions are used.

  ERROR HANDLING
    The first unusual thing about how Chouette uses exceptions is that it
    uses them for error conditions, in contrast to many other asynchronous
    frameworks.

    Most asynchronous frameworks are unable to use exceptions to signal
    errors since an error may occur in a callback being run from the event
    loop. If this callback throws an exception, there will be nothing to
    catch it, except perhaps a catch block installed by the event loop. Even
    if the event loop does catch it, it won't know which connection the
    exception is for, and therefore won't be able to send a 500 error to
    that connection or add a message to that connection's log.

    Consider the AnyEvent::DBI library. This is how its error handling
    works:

        $dbh->exec("SELECT * FROM no_such_table", sub {
            my ($dbh, $rows, $rv) = @_;

            if ($#_) {
                # success
            } else {
                # failure. error message is in $@
            }
        });

    Even if "exec" failed, the callback still gets called. Whether or not it
    succeeded is indicated by its parameters. You can think of this as a
    sort of "in-band" signalling. The fact that there was an error, and what
    exactly that error was, needs to be indicated by the callback's
    parameters in some way. Unfortunately every library does this slightly
    differently. Another alternative used by some libraries is to accept 2
    callbacks, one of which is called in the success case, and the other in
    the failure case.

    But with both of these methods, what should the callback do when it is
    notified of an error? It can't just "die" because nothing will catch the
    exception. With the EV event loop you will see this:

        EV: error in callback (ignoring): failure: ERROR:  relation "no_such_table" does not exist

    Even if you wrap an "eval" or a Try::Tiny "try {} catch {}" around the
    code the same thing happens. The try/catch is in effect while installing
    the callback, but not when the callback is called.

    As a consequence of all this, asynchronous web frameworks usually cannot
    indicate errors with exceptions. Instead, they require you to respond to
    the client from inside the callback:

        $dbh->exec("SELECT * FROM no_such_table", sub {
            my ($dbh, $rows, $rv) = @_;

            if (!$#_) {
                $context->respond_500_error("DB error: $@");
                return;
            }

            # success
        });

    There are several down-sides to this approach:

    *   The error must be handled locally in each callback, rather than once
        in a catch-all error handler.

    *   Everywhere an error might occur needs to have access to the context
        object. This often requires passing it as an argument around
        everywhere.

    *   You might forget to handle an error (or it might be too inconvenient
        so you don't bother) and your success-case code will run on garbage
        data.

    *   Perhaps most importantly, if some unexpected exception is thrown by
        your callback (or something that it calls) then the event loop will
        receive an exception and nothing will get logged or replied to.

    For these reasons, Chouette uses Callback::Frame to deal with
    exceptions. The idea is that the exception handling code is carried
    around with your callbacks. For instance, this is how you would
    accomplish the same thing with Chouette:

        my $dbh = $c->task('db');

        $dbh->selectrow_arrayref("SELECT * FROM no_such_table", undef, sub {
            my ($dbh, $rows) = @_;

            # success

            # Even if I can die here and it will get routed to the right request!
        });

    The callback will only be invoked in the success case. If a failure
    occurs, an exception will be raised in the dynamic scope that was in
    effect when the callback was installed. Because Chouette installs a
    "catch" handler for each request, an appropriate error will be sent to
    the client and added to the Chouette logs.

    Important note: Libraries like AnyEvent::Task (which is what "task" in
    the above example uses) are Callback::Frame-aware. This means that you
    can pass "sub {}" callbacks into them and they will automatically
    convert them to "fub {}" callbacks for you.



( run in 2.351 seconds using v1.01-cache-2.11-cpan-8f98c5d2c55 )