Prev: FAQ 4.68 Why does passing a subroutine an undefined element in a hash create it?
Next: FAQ 7.9 How do I create a module?
From: Frédéric Perrin on 21 Apr 2010 04:24 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 21 Apr 2010 11:50 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 21 Apr 2010 16:15
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__ |