From: Frédéric Perrin on
Hello,

I want to subclass Net::SSH::Expect, by adding a couple of fields to
it. Using "perldoc fields" as a guide, I did the following:

---------------- 8< ----------------

#!/usr/bin/perl

{
package essai;
use base 'Net::SSH::Expect';
use fields qw/ new_field /;
use Data::Dumper;

sub new {
my $class = shift;
my $self = fields::new($class);
$self->SUPER::new(host => "server.invalid",
password => "password");
print Dumper $self;
$self->run_ssh;
$self->{new_field} = "something";
return $self;
}
}

package main;

my $ssh = essai->new();

---------------- 8< ----------------

But when $self->run_ssh is called, the ->run_ssh method croaks with:

> croak(ILLEGAL_STATE . " field 'host' is not set.") unless $host;

We see that the pseudo-hash has all its fields, including new_field,
but there is no data.

Looking at the Net::SSH::Expect->new method, I see that it doesn't
initiate the object in the same way as "perldoc fields" does. I fixed
that by patching the new method in the following way (indentation was
already broken):

# diff -C1 Expect.pm.orig Expect.pm
*** Expect.pm.orig 2010-04-21 09:44:29.000000000 +0200
--- Expect.pm 2010-04-21 09:46:34.000000000 +0200
***************
*** 25,29 ****
sub new {
! my $type = shift;
my %args = @_;
! my Net::SSH::Expect $self = fields::new(ref $type || $type);

--- 25,31 ----
sub new {
! my Net::SSH::Expect $self = shift;
my %args = @_;
! unless (ref $self) {
! $self = fields::new($self);
! }

Is this a genuine bug in Net::SSH::Expect, or am I doing something
wrong?

--
Fred
From: Ben Morrow on

Quoth =?utf-8?B?RnLDqWTDqXJpYw==?= Perrin <fred(a)resel.fr>:
> Hello,
>
> I want to subclass Net::SSH::Expect, by adding a couple of fields to
> it. Using "perldoc fields" as a guide, I did the following:
>
> ---------------- 8< ----------------
>
> #!/usr/bin/perl
>
> {
> package essai;

Don't use lowercase names for your own modules. They are reserved for
pragmas (modules like warnings and strict, which affect how Perl parses
your program).

> use base 'Net::SSH::Expect';
> use fields qw/ new_field /;

This is really unsafe. You have no way of knowing if the next version
of NSE will add a new field that happens to be called 'new_field'. Use
an inside-out object instead:

use Scalar::Util qw/refaddr/;

my %new_field;

sub new {
my $class = shift;
my $self = $class->SUPER::new(...);
$new_field{refaddr $self} = "something";
}

Be aware that this will fail if your program uses threads. If you can
depend on 5.10, you can use the core Hash::Util::Fieldhash to fix this
(and also avoid the 'refaddr'). You can also use Object::InsideOut,
which works on older perls but is a little heavy for my taste.

> use Data::Dumper;
>
> sub new {
> my $class = shift;
> my $self = fields::new($class);
> $self->SUPER::new(host => "server.invalid",
> password => "password");

This is wrong. Net::SSH::Expect->new is a class method, not an object
method, so you should be calling it on the class.

my $self = $class->SUPER::new(...);

I believe (though I haven't tested it) that this will fix your original
problem, as well, since N::S::E->new will now call fields::new with your
classname.

Ben

From: Mumia W. on
On 04/21/2010 03:24 AM, Fr�d�ric Perrin wrote:
> Hello,
>
> I want to subclass Net::SSH::Expect, by adding a couple of fields to
> it. Using "perldoc fields" as a guide, I did the following:
>
> ---------------- 8< ----------------
>
> #!/usr/bin/perl
> [...]

Ben Morrow gave you good advice; however, if inside-out objects are not
your thing, Class::Struct makes it easy to create accessor methods which
can be inherited into other classes, e.g.:

#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use IO::File;

my $file = NewFile->new;
$file->new_field(91);
say $file->new_field();

package NewFile;
use Class::Struct NewFileBase => { new_field => '$' };
use base 'NewFileBase';
use base 'IO::File';

__HTH__