+# we don't want DESTROY to fallback into AUTOLOAD
+sub DESTROY {}
+
+our $AUTOLOAD;
+sub AUTOLOAD {
+ my $self = shift;
+
+ use bytes;
+
+ my $sock = $self->{sock} || die "no server connected";
+
+ my $command = $AUTOLOAD;
+ $command =~ s/.*://;
+
+ warn "## $command ",Dumper(@_) if $self->{debug};
+
+ unshift @_, uc($command);
+
+ my $send
+ = "*".(scalar @_)
+ . "\r\n"
+ . join("", map { "\$". length($_) ."\r\n". $_ ."\r\n" } @_)
+ ;
+
+ warn ">> $send" if $self->{debug};
+ print $sock $send;
+
+ if ( $command eq 'quit' ) {
+ close( $sock ) || die "can't close socket: $!";
+ return 1;
+ }
+
+ my $result = <$sock> || die "can't read socket: $!";
+ Encode::_utf8_on($result);
+ warn "<< $result" if $self->{debug};
+ my $type = substr($result,0,1);
+ $result = substr($result,1,-2);
+
+ if ( $command eq 'info' ) {
+ my $hash;
+ foreach my $l ( split(/\r\n/, $self->__read_bulk($result) ) ) {
+ my ($n,$v) = split(/:/, $l, 2);
+ $hash->{$n} = $v;
+ }
+ return $hash;
+ }
+
+ if ( $type eq '-' ) {
+ confess "[$command] $result";
+ } elsif ( $type eq '+' ) {
+ return $result;
+ } elsif ( $type eq '$' ) {
+ return $self->__read_bulk($result);
+ } elsif ( $type eq '*' ) {
+ return $self->__read_multi_bulk($result);
+ } elsif ( $type eq ':' ) {
+ return $result; # FIXME check if int?
+ } else {
+ confess "unknown type: $type", $self->__read_line();
+ }