AE-AdHoc
view release on metacpan or search on metacpan
lib/AE/AdHoc.pm view on Meta::CPAN
This module is NOT for building other modules, it's for running them with
minimal typing.
=head1 SYNOPSIS
Suppose we have a subroutine named C<do_stuff( @args, $subref )>
that is designed to run under AnyEvent. As do_stuff may have to wait for
some external events to happen, it does not return a value right away.
Instead, it will call C<$subref-E<gt>( $results )> when stuff is done.
Now we need to test do_stuff, so we set up an event loop. We also need a timer,
because a test that runs forever is annoying. So the script goes like this:
use AnyEvent;
# set up event loop
my $cv = AnyEvent->condvar;
my $timer = AnyEvent->timer(
after => 10, cb => sub { $cv->croak("Timeout"); }
);
do_stuff( @args, sub{ $cv->send(shift); } );
# run event loop, get rid of timer
my $result = $cv->recv();
undef $timer;
# finally
analyze_results( $result );
Now, the same with AE::AdHoc:
use AE::AdHoc;
my $result = ae_recv {
do_stuff( @args, ae_send );
lib/AE/AdHoc.pm view on Meta::CPAN
if (!$timeout or !looks_like_number($timeout));
# find out where we are
$iter++;
my @caller = caller(0);
local $where = "ae_recv[$iter] at $caller[1]:$caller[2]";
my $on_timeout = $opt{soft_timeout}
? sub { $cv->send }
: sub { $cv->croak("Timeout after $timeout seconds"); };
my $timer;
$timeout > 0 and $timer = AnyEvent->timer( after => $timeout,
cb => $on_timeout,
);
_clear_goals();
$code->();
return $cv->recv;
# on exit, $timer is autodestroyed
# on exit, $cv is restored => destroyed
};
=head2 ae_send ( [@fixed_args] )
Create callback for normal event loop ending.
Returns a sub that feeds its arguments to C<$cv-E<gt>send()>. Arguments given to
the function itself are prepended, as in
C<$cv-E<gt>send(@fixed_args, @callback_args)>.
lib/AE/AdHoc.pm view on Meta::CPAN
=cut
sub goals { return \%goals; };
sub results { return \%results; };
=head1 ADDITIONAL ROUTINES
=head2 ae_action { CODE } %options
Perform CODE after entering the event loop via ae_recv
(a timer is used internally).
CODE will NOT run after current event loop is terminated (see ae_recv).
Options may include:
=over
=item * after - delay before code execution (in seconds, may be fractional)
=item * interval - delay between code executions (in seconds, may be fractional)
lib/AE/AdHoc.pm view on Meta::CPAN
my $myiter = $iter;
my @caller = caller(0);
my $exact = "ae_action at $caller[1]:$caller[2] from $where";
$opt{after} ||= 0;
my $count = $opt{count};
my $inf = !$count;
my $n = 0;
my $timer;
my $cb = sub {
if (!$cv) {
undef $timer;
return _error( "Leftover $exact called outside ae_recv" );
};
$myiter == $iter or undef $timer;
$inf or $count-->0 or undef $timer;
$timer and $code->($n++);
};
$timer = AnyEvent->timer(
after=>$opt{after}, interval=>$opt{interval}, cb=>$cb);
return;
};
=head1 ERROR HANDLING
Dying within event loop is a bad idea, so we issue B<warnings> and write
errors to magic variables. It is up to the user to check these variables.
=over
t/10-basic.t view on Meta::CPAN
} "plain ae_send is fine";
throws_ok {
ae_send;
} qr(outside), "outside = no go";
throws_ok {
ae_begin;
} qr(outside), "outside = no go";
my $timer;
throws_ok {
ae_recv {
$timer = AnyEvent->timer( after => 0.1, cb => ae_send );
note "timer ref = $timer";
} 0.01;
} qr(Timeout), "Start rotten timer test";
# check that later-on callback generates a warning
{
my @warn;
local $SIG{__WARN__} = sub { push @warn, @_ };
throws_ok {
ae_recv { ; } 0.2;
} qr(Timeout), "Rotten timer didn't spoil later tests:";
is (scalar @warn, 1, " - 1 warning issued");
like ($warn[0], qr(Leftover), " - It was about 'Leftover': $warn[0]");
ok (ref $timer, " - Rotten timer still alive at this point (but harmless): $timer");
};
t/11-begin-end.t view on Meta::CPAN
#!/usr/bin/perl -w
use strict;
use Test::More;
use Test::Exception;
use AE::AdHoc;
my @timers;
plan tests => 5;
lives_ok {
my $timer;
ae_recv {
ae_begin;
$timer = AnyEvent->timer( after => 0.01, cb => ae_end );
} 1;
} "A simple begin/end example works";
throws_ok {
my $timer;
ae_recv {
ae_begin;
ae_begin;
$timer = AnyEvent->timer( after => 0.01, cb => ae_end );
} 0.02;
} qr(Timeout), "A simple example with extra begin dies";
my @trace;
my $val;
lives_ok {
ae_recv {
my $tm;
my $iter;
my $attimer;
$attimer = sub {
push @trace, ++$iter;
ae_end->();
$tm = AE::timer 0.01, 0, $attimer;
};
$tm = AE::timer 0.01, 0, $attimer;
ae_begin( sub { ae_send->(++$val) } ) for (1,2);
} 1;
} "More complex example lives";
is ($val, 1, "Begin's callback executed once");
is_deeply(\@trace, [1, 2], "end->() executed twice");
t/15-goals.t view on Meta::CPAN
use AE::AdHoc;
my $result;
throws_ok {
ae_goal("foo");
} qr(outside), "no ae_recv = no go";
note $@;
# We use sub {} in timers here because timer passes random args to its
# callback. See L<::AnyEvent> timer section.
lives_ok {
my ($t1, $t2);
$result = ae_recv {
$t1 = AnyEvent->timer( after => 0,
cb => sub { ae_goal("task1")->() }
);
$t2 = AnyEvent->timer( after => 0,
cb => sub { ae_goal("task2", "fixed")->() }
);
} 1;
} "No timeout - goals complete";
note "Got: ".Dumper($result);
is_deeply ($result,
{ task1 => [], task2 => [ "fixed" ]},
"Results as expected (sans timer callback args)"
);
is_deeply (AE::AdHoc->results(), $result, "AE::AdHoc::results consistent");
is_deeply (AE::AdHoc->goals(), {}, "AE::AdHoc::goals is empty (all complete)");
t/17-goals-leftover.t view on Meta::CPAN
#!/usr/bin/perl -w
use strict;
use Test::More tests => 3;
use Data::Dumper;
use AE::AdHoc;
$AE::AdHoc::warnings = 0;
my $timer;
ae_recv {
$timer = AnyEvent->timer( after => 0, cb => ae_goal("pollute") );
ae_send->("return right now");
} 0.1;
my $timer2;
ae_recv {
$timer2 = AnyEvent->timer( after => 0.1, cb => ae_goal("clean") );
} 0.2;
my @keys = sort keys %{ AE::AdHoc->results };
is_deeply( \@keys, ["clean"], "AE results are clean" );
note "Results: ".Dumper( AE::AdHoc->results );
is (scalar @AE::AdHoc::errors, 1, "Exactly 1 error");
like ($AE::AdHoc::errstr, qr(^Leftover.*ae_goal), "Leftover error present");
note "Error was: $AE::AdHoc::errstr";
( run in 0.586 second using v1.01-cache-2.11-cpan-49f99fa48dc )