Skip to content

Commit

Permalink
added delay helper
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Jul 1, 2014
1 parent 47819c7 commit 0ec70a3
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 22 deletions.
1 change: 1 addition & 0 deletions Changes
@@ -1,5 +1,6 @@

5.11 2014-07-01
- Added delay helper to Mojolicious::Plugin::DefaultHelpers.
- Improved error method in Mojolicious::Validator::Validation to return
field names when called without arguments.

Expand Down
14 changes: 0 additions & 14 deletions lib/Mojo.pm
Expand Up @@ -130,20 +130,6 @@ L<Mojo::UserAgent> object.
# Perform blocking request
say $app->ua->get('example.com')->res->body;
# Perform concurrent non-blocking requests
Mojo::IOLoop->delay(
sub {
my $delay = shift;
$app->ua->get('http://example.com' => $delay->begin);
$app->ua->get('https://example.com' => $delay->begin);
},
sub {
my ($delay, $first, $second) = @_;
say $first->res->body;
say $second->res->body;
}
)->wait;
=head1 METHODS
L<Mojo> inherits all methods from L<Mojo::Base> and implements the following
Expand Down
11 changes: 4 additions & 7 deletions lib/Mojolicious/Guides/Cookbook.pod
Expand Up @@ -393,19 +393,19 @@ latency backend web services.
</html>

Multiple events such as concurrent requests can be easily synchronized with
L<Mojo::IOLoop/"delay">, which can help you avoid deep nested closures and
memory leaks that often result from continuation-passing style.
L<Mojolicious::Plugin::DefaultHelpers/"delay">, which can help you avoid deep
nested closures and memory leaks that often result from continuation-passing
style.

use Mojolicious::Lite;
use Mojo::IOLoop;
use Mojo::URL;

# Search MetaCPAN for "mojo" and "mango"
get '/' => sub {
my $c = shift;

# Prepare response in two steps
Mojo::IOLoop->delay(
$c->delay(

# Concurrent requests
sub {
Expand All @@ -425,9 +425,6 @@ memory leaks that often result from continuation-passing style.
});
}
);

# Handle exceptions in steps
$delay->catch(sub { $c->render_exception(pop) });
};

app->start;
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojolicious/Lite.pm
Expand Up @@ -876,7 +876,7 @@ L<Mojo::JSON> and L<Mojo::DOM> this can be a very powerful tool.
# Concurrent non-blocking
get '/titles' => sub {
my $c = shift;
Mojo::IOLoop->delay(
$c->delay(
sub {
my $delay = shift;
$c->ua->get('http://mojolicio.us' => $delay->begin);
Expand Down
24 changes: 24 additions & 0 deletions lib/Mojolicious/Plugin/DefaultHelpers.pm
Expand Up @@ -3,6 +3,7 @@ use Mojo::Base 'Mojolicious::Plugin';

use Mojo::ByteStream;
use Mojo::Collection;
use Mojo::IOLoop;
use Mojo::Util qw(dumper sha1_sum steady_time);

sub register {
Expand Down Expand Up @@ -34,6 +35,7 @@ sub register {
$app->helper(content_for => \&_content_for);
$app->helper(csrf_token => \&_csrf_token);
$app->helper(current_route => \&_current_route);
$app->helper(delay => \&_delay);
$app->helper(dumper => sub { shift; dumper(@_) });
$app->helper(include => sub { shift->render_to_string(@_) });
$app->helper(ua => sub { shift->app->ua });
Expand Down Expand Up @@ -77,6 +79,13 @@ sub _current_route {
return $endpoint->name eq shift;
}

sub _delay {
my $self = shift;
my $delay = Mojo::IOLoop->delay(@_);
my $tx = $self->render_later->tx;
$delay->catch(sub { $self->render_exception(pop) and undef $tx })->wait;
}

sub _url_with {
my $c = shift;
return $c->url_for(@_)->query($c->req->url->query->clone);
Expand Down Expand Up @@ -208,6 +217,21 @@ Get CSRF token from L</"session">, and generate one if none exists.
Check or get name of current route.
=head2 delay
$c->delay(sub {...}, sub {...});
Disable automatic rendering and use L<Mojo::IOLoop/"delay"> to manage
callbacks and control the flow of events, which can help you avoid deep nested
closures and memory leaks that often result from continuation-passing style.
Call L<Mojolicious::Controller/"render_exception"> if an error occured in one
of the steps, breaking the chain.
# Longer version
$c->render_later;
my $delay = Mojo::IOLoop->delay(sub {...}, sub {...});
$delay->catch(sub { $c->render_exception(pop) })->wait;
=head2 dumper
%= dumper {some => 'data'}
Expand Down
29 changes: 29 additions & 0 deletions t/mojolicious/longpolling_lite_app.t
Expand Up @@ -222,6 +222,22 @@ get '/too_long' => sub {
Mojo::IOLoop->timer(5 => sub { $c->write('dy plain!') });
};

my $steps;
helper steps => sub {
my $c = shift;
$c->delay(
sub { Mojo::IOLoop->next_tick(shift->begin) },
sub {
Mojo::IOLoop->next_tick(shift->begin);
$c->param('die') ? die 'intentional' : $c->render(text => 'second');
$c->res->headers->header('X-Next' => 'third');
},
sub { $steps = $c->res->headers->header('X-Next') }
);
};

get '/steps' => sub { shift->steps };

my $t = Test::Mojo->new;

# Stream without delay and finish
Expand Down Expand Up @@ -439,6 +455,19 @@ is $tx->res->code, 200, 'right status';
is $tx->error->{message}, 'Inactivity timeout', 'right error';
is $buffer, 'how', 'right content';

# Transaction is available after rendering early in steps
$t->get_ok('/steps')->status_is(200)->content_is('second');
Mojo::IOLoop->one_tick until $steps;
is $steps, 'third', 'right result';

# Event loop is automatically started for steps
my $c = app->build_controller;
$c->steps;
is $c->res->body, 'second', 'right content';

# Exception in step
$t->get_ok('/steps?die=1')->status_is(500)->content_like(qr/intentional/);

done_testing();

__DATA__
Expand Down

0 comments on commit 0ec70a3

Please sign in to comment.