MooX-Async-Console

 view release on metacpan or  search on metacpan

lib/MooX/Async/Console/TCP.pm  view on Meta::CPAN

before _remove_from_loop => sub {
  if ($_[0]->children) {
    $_[0]->_logger->warningf('TCP Console closed with %u active client[s]', scalar $_[0]->children);
    $_[0]->_detach_client($_) for $_[0]->children;
  }
};

=pod

C<_init>, which is used during the parent class
L<IO::Async::Listener>'s own construction, replaces its C<$args> with
a single entry of C<handle_constructor>.

=cut

around _init => sub {
  my $orig = shift;
  my $self = shift;

=pod

C<handle_constructor> contains a coderef to attach the client
implemented by L<MooX::Async::Console::TCPClient> and handle its
L<on_line> and L<on_close> events.

=cut

  my $line  = sub { unshift @_, $self; goto \&__on_line };
  my $close = sub { unshift @_, $self; goto \&__on_close};
  %{$_[0]} = (handle_constructor => sub {
    MooX::Async::Console::TCPClient->new(on_close => $close, on_line => $line);
  });
  $self->$orig(@_);
};

use namespace::clean '__close';
sub __on_close {
  my $self = shift;
  $self->invoke_event(on_terminate =>);
  $self->_logger->informf('Client disconnected from %s:%s', $_[0]->address, $_[0]->port);
  $self->_detach_client($_[0]);
}

=head3 Client's on_line event handler

For the present this is extremely simple. The client types in a line
of text and ends it with newline. That line is broken up into a list
on whitespace and the first word in the list is the command name, the
rest its args.

Only one command may be running at a time. This is enforced by the
C<$state> variable.

=cut

use namespace::clean '__line';
sub __on_line {
  my $self = shift;
  my $client = shift;
  my ($cmd, @args) = split ' ', shift;
  my $state;            # for now - false nothing, true busy;
  die 'One command at a time for now' if $state;
  $state++;
  my $quit;
  $self->_logger->debugf('Received command %s %s', $cmd, \@args);

=pod

The L</on_command> event handler is invoked with a new L<Future>.

=cut

  my $future = $self->loop->new_future;
  $self->adopt_future(
    $future->followed_by(sub {
      # Why is this useful?
      return Future->fail($_[0]->failure) if $_[0]->failure;
      my $command_future = $_[0]->get;
      return Future->done($command_future->get) if $command_future->is_done;

=pod

Disconnecting the client is treated specially so that everything is
shutdown in an orderly manner.

If the L<Future> which is given to the command handler is failed with
the word C<quit> then this is flagged using C<$quit> and the L<Future>
is replaced with a done L<Future> with an appropriate message
substituted.

=cut

      return Future->fail($command_future->failure) if $command_future->failure ne 'quit';
      $self->_logger->debugf('Requested disconnect');
      $quit = 1;
      return Future->done('disconnecting...');

=pod

After the L<Future> completes succesfully a message is returned to the
client containing its result.

=cut

    })->then(sub {
      my $r = $self->invoke_event(on_success => @_);
      @_ = $r->get if blessed $r and $r->DOES('Future');
      my $extra = @_ ? ' - ' . (join ' ', ('%s')x@_) : '';
      # TODO: Figure out a better way to do this
      $client->say(sprintf "OK$extra", Log::Any::Proxy::_stringify_params(@_));

=pod

If the C<$quit> flag is true the client is detached.

=cut

      if ($quit) {
        $self->_logger->informf('Client disconnecting from %s:%s', $client->address, $client->port);
        $self->_detach_client($client);
      }



( run in 2.485 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )