AnyEvent-FCP

 view release on metacpan or  search on metacpan

FCP.pm  view on Meta::CPAN

   sending_to_network          => sub { $_[1]{sending_to_network}     = $_[2] }, # get
   compatibility_mode          => sub { $_[1]{compatibility_mode}     = $_[2] }, # get
   expected_hashes             => sub { $_[1]{expected_hashes}        = $_[2] }, # get
   expected_mime               => sub { $_[1]{expected_mime}          = $_[2] }, # get
   expected_data_length        => sub { $_[1]{expected_data_length}   = $_[2] }, # get
   get_failed                  => sub { $_[1]{get_failed}             = $_[2] }, # get
   data_found                  => sub { $_[1]{data_found}             = $_[2] }, # get
   enter_finite_cooldown       => sub { $_[1]{enter_finite_cooldown}  = $_[2] }, # get
);

sub recv {
   my ($self, $type, $kv, @extra) = @_;

   if (my $cb = $PERSISTENT_TYPE{$type}) {
      my $id  = $kv->{identifier};
      my $req = $_[0]{req}{$id} ||= {};
      $cb->($self, $req, $kv);
      $self->recv (request_changed => $kv, $type, @extra);
   }

   my $on = $self->{on};
   for (0 .. $#$on) {
      unless (my $res = $on->[$_]($self, $type, $kv, @extra)) {
         splice @$on, $_, 1 unless defined $res;
         return;
      }
   }

   if (my $cb = $self->{queue}[0]) {
      $cb->($self, $type, $kv, @extra)
         and shift @{ $self->{queue} };
   } else {
      $self->default_recv ($type, $kv, @extra);
   }
}

sub default_recv {
   my ($self, $type, $kv, $rdata) = @_;

   if ($type eq "node_hello") {
      $self->{node_hello} = $kv;
   } elsif (exists $self->{id}{$kv->{identifier}}) {
      $self->{id}{$kv->{identifier}}($self, $type, $kv, $rdata)
         and delete $self->{id}{$kv->{identifier}};
   }
}

=back

=head2 FCP REQUESTS

The following methods implement various requests. Most of them map
directory to the FCP message of the same name. The added benefit of
these over sending requests yourself is that they handle the necessary
serialisation, protocol quirks, and replies.

All of them exist in two versions, the variant shown in this manpage, and
a variant with an extra C<_> at the end, and an extra C<$cb> argument. The
version as shown is I<synchronous> - it will wait for any replies, and
either return the reply, or croak with an error. The underscore variant
returns immediately and invokes one or more callbacks or condvars later.

For example, the call

   $info = $fcp->get_plugin_info ($name, $detailed);

Also comes in this underscore variant:

   $fcp->get_plugin_info_ ($name, $detailed, $cb);

You can thinbk of the underscore as a kind of continuation indicator - the
normal function waits and returns with the data, the C<_> indicates that
you pass the continuation yourself, and the continuation will be invoked
with the results.

This callback/continuation argument (C<$cb>) can come in three forms itself:

=over 4

=item A code reference (or rather anything not matching some other alternative)

This code reference will be invoked with the result on success. On an
error, it will invoke the C<on_failure> callback of the FCP object, or,
if none was defined, will die (in the event loop) with a backtrace of the
call site.

This is a popular choice, but it makes handling errors hard - make sure
you never generate protocol errors!

If an C<on_failure> hook exists, it will be invoked with the FCP object,
the request type (the name of the method), a (textual) backtrace as
generated by C<Carp::longmess>, and arrayref containing the arguments from
the original request invocation and the error object from the server, in
this order, e.g.:

   on_failure => sub {
      my ($fcp, $request_type, $backtrace, $orig_args, $error_object) = @_;

      warn "FCP failure ($type), $error_object->{code_description} ($error_object->{extra_description})$backtrace";
      exit 1;
   },

=item A condvar (as returned by e.g. C<< AnyEvent->condvar >>)

When a condvar is passed, it is sent (C<< $cv->send ($results) >>) the
results when the request has finished. Should an error occur, the error
will instead result in C<< $cv->croak ($error) >>.

This is also a popular choice.

=item An array with two callbacks C<[$success, $failure]>

The C<$success> callback will be invoked with the results, while the
C<$failure> callback will be invoked on any errors.

The C<$failure> callback will be invoked with the error object from the
server.

=item C<undef>

This is the same thing as specifying C<sub { }> as callback, i.e. on
success, the results are ignored, while on failure, the C<on_failure> hook
is invoked or the module dies with a backtrace.

This is good for quick scripts, or when you really aren't interested in
the results.

=back

=cut

our $NOP_CB = sub { };

sub _txn {
   my ($name, $sub) = @_;

   *{$name} = sub {
      my $cv = AE::cv;

      splice @_, 1, 0, $cv, sub { $cv->croak ($_[0]{extra_description}) };
      &$sub;
      $cv->recv
   };

   *{"$name\_"} = sub {
      my ($ok, $err) = pop;

      if (ARRAY:: eq ref $ok) {
         ($ok, $err) = @$ok;
      } elsif (UNIVERSAL::isa $ok, AnyEvent::CondVar::) {
         $err = sub { $ok->croak ($_[0]{extra_description}) };
      } else {
         my $bt = Carp::longmess "AnyEvent::FCP request $name";
         Scalar::Util::weaken (my $self = $_[0]);
         my $args = [@_]; shift @$args;
         $err = sub {
            if ($self->{on_failure}) {
               $self->{on_failure}($self, $name, $args, $bt, $_[0]);
            } else {
               die "$_[0]{code_description} ($_[0]{extra_description})$bt";
            }
         };
      }

      $ok ||= $NOP_CB;

      splice @_, 1, 0, $ok, $err;
      &$sub;
   };
}



( run in 1.443 second using v1.01-cache-2.11-cpan-140bd7fdf52 )