Skip to content
Browse files

add front-triggering and infinite-timer-resetting + tests

  • Loading branch information...
1 parent f7ccf9d commit 7e27ae8306779ce0323a6cd6e20ad2063551b807 @jrockway committed Feb 27, 2011
Showing with 172 additions and 30 deletions.
  1. +70 −14 lib/AnyEvent/Debounce.pm
  2. +102 −16 t/basic.t
View
84 lib/AnyEvent/Debounce.pm
@@ -1,18 +1,35 @@
package AnyEvent::Debounce;
+# ABSTRACT: condense multiple temporally-nearby events into one
use Moose;
use AnyEvent;
+has 'front_triggered' => (
+ is => 'ro',
+ isa => 'Bool',
+ default => 0,
+ documentation => 'if true, trigger immediately after the first event is received',
+);
+
+has 'always_reset_timer' => (
+ is => 'ro',
+ isa => 'Bool',
+ default => 0,
+ documentation => 'if true, reset the timer after each event',
+);
+
has 'delay' => (
- is => 'ro',
- isa => 'Num',
- default => 1,
+ is => 'ro',
+ isa => 'Num',
+ default => 1,
+ documentation => 'number of seconds to wait before considering events separate',
);
has 'cb' => (
- is => 'ro',
- isa => 'CodeRef',
- required => 1,
+ is => 'ro',
+ isa => 'CodeRef',
+ required => 1,
+ documentation => 'coderef to run when debounced events are ready',
);
has '_queued_events' => (
@@ -22,7 +39,7 @@ has '_queued_events' => (
default => sub { [] },
lazy => 1,
clearer => 'clear_queued_events',
- handles => { 'queue_event' => 'push' },
+ handles => { 'queue_event' => 'push', 'event_count' => 'count' },
);
has 'timer' => (
@@ -42,27 +59,37 @@ sub _build_timer {
sub send_events_now {
my $self = shift;
my $events = $self->queued_events;
+ my $count = $self->event_count;
$self->clear_timer;
$self->clear_queued_events;
- $self->cb->(@$events);
+ $self->cb->(@$events) if $count > 0;
return;
}
sub send {
my ($self, @args) = @_;
- $self->queue_event([@args]);
+
+ my $timer_running = $self->has_timer;
+ $self->clear_timer if $self->always_reset_timer;
$self->timer; # resets the timer if we don't have one
+
+ if($self->front_triggered && !$timer_running){
+ $self->cb->([@args]);
+ }
+ elsif(!$self->front_triggered){
+ $self->queue_event([@args]);
+ }
+ else {
+ # warn "discarding event"
+ }
+
return;
}
1;
__END__
-=head1 NAME
-
-AnyEvent::Debounce - wait a bit in case another event is received
-
=head1 SYNOPSIS
Create a debouncer:
@@ -110,11 +137,40 @@ Each "event" is an arrayref of the args passed to C<send>.
The time to wait after receiving an event before sending it, in case
more events happen in the interim.
+=head1 always_reset_timer
+
+Normally, when an event is received and it's the first of a series, a
+timer is started, and when that timer expires, all events are sent.
+If you set this initarg to a true value, then the timer is reset after
+each event is received.
+
+For example, if you set the delay to 1, and ten events arrive at 0.5
+second intervals, then with this flag set to true, you will get one
+event after 5 seconds. With this flag set to false, you will get an
+event once a second for 5 seconds.
+
+By default, this is false, because setting it to true can lead to
+events never being sent. (Imagine you set delay to 10 seconds, and
+someone sends an event ever 9.9 seconds. You'll never get any
+events.)
+
+=head1 front_triggered
+
+This flag, when set to true, causes an event to be sent immediately
+upon receiving the first event. Then, you won't get any events for
+C<delay> seconds, even if they occur. These events are lost, you will
+never see them.
+
+By default, this is false.
+
+If you also set C<always_reset_timer> to true, the same timer-reset
+logic as described above occurs.
+
=head1 METHODS
=head1 send
-Send an event; the handler will get everything you pass in.
+Send an event; the handler callback will get everything you pass in.
=head1 REPOSITORY
View
118 t/basic.t
@@ -5,21 +5,107 @@ use Test::More;
use AnyEvent;
use AnyEvent::Debounce;
-my $sent = 0;
-my $done = AnyEvent->condvar;
-my $d = AnyEvent::Debounce->new(
- delay => 2,
- cb => sub { $done->send([@_]) },
-);
-
-my $sender; $sender = AnyEvent->timer( after => 0, interval => 0.1, cb => sub {
- $d->send($sent);
- undef $sender if ++$sent > 9;
-});
-
-my $result = $done->recv;
-
-is $sent, 10, 'got 10 events before cb was called';
-is_deeply $result, [map { [$_] } 0..9], 'got the events we expected';
+{
+ my $sent = 0;
+ my $done = AnyEvent->condvar;
+ my $d = AnyEvent::Debounce->new(
+ front_triggered => 0,
+ always_reset_timer => 0,
+ delay => 2,
+ cb => sub { $done->send([@_]) },
+ );
+
+ my $sender; $sender = AnyEvent->timer( after => 0, interval => 0.1, cb => sub {
+ $d->send($sent);
+ undef $sender if ++$sent > 9;
+ });
+
+ my $result = $done->recv;
+
+ is $sent, 10, 'got 10 events before cb was called';
+ is_deeply $result, [map { [$_] } 0..9], 'got the events we expected';
+}
+
+{
+ my $sent = 0;
+ my $done = AnyEvent->condvar;
+ my $d = AnyEvent::Debounce->new(
+ front_triggered => 0,
+ always_reset_timer => 1,
+ delay => 0.15,
+ cb => sub { $done->send([@_]) },
+ );
+
+ my $sender; $sender = AnyEvent->timer( after => 0, interval => 0.1, cb => sub {
+ $d->send($sent);
+ undef $sender if ++$sent > 9;
+ });
+
+ my $result = $done->recv;
+
+ is $sent, 10, 'got 10 events before cb was called';
+ is_deeply $result, [map { [$_] } 0..9], 'got the events we expected';
+}
+
+
+{
+ my $sent = 0;
+ my $got = 0;
+ my $done = AnyEvent->condvar;
+
+ my $d = AnyEvent::Debounce->new(
+ front_triggered => 1,
+ always_reset_timer => 0,
+ delay => 1.5,
+ cb => sub { $got++ },
+ );
+
+ $done->begin;
+ my $waiter = AnyEvent->timer( after => 2, cb => sub { $done->end } );
+
+ $done->begin;
+ my $sender; $sender = AnyEvent->timer( after => 0, interval => 0.1, cb => sub {
+ $d->send($sent);
+ if(++$sent > 9){
+ undef $sender;
+ $done->end;
+ }
+ });
+
+ $done->recv;
+
+ is $sent, 10, 'sent 10 events';
+ is $got, 1, 'got 1 event';
+}
+
+{
+ my $sent = 0;
+ my $got = 0;
+ my $done = AnyEvent->condvar;
+
+ my $d = AnyEvent::Debounce->new(
+ front_triggered => 1,
+ always_reset_timer => 1,
+ delay => 0.15,
+ cb => sub { $got++ },
+ );
+
+ $done->begin;
+ my $waiter = AnyEvent->timer( after => 2, cb => sub { $done->end } );
+
+ $done->begin;
+ my $sender; $sender = AnyEvent->timer( after => 0, interval => 0.1, cb => sub {
+ $d->send($sent);
+ if(++$sent > 9){
+ undef $sender;
+ $done->end;
+ }
+ });
+
+ $done->recv;
+
+ is $sent, 10, 'sent 10 events';
+ is $got, 1, 'got 1 event';
+}
done_testing;

0 comments on commit 7e27ae8

Please sign in to comment.
Something went wrong with that request. Please try again.