How to reclassify Perl object - perl

I'm working with a few Perl packages, we'll call them Some::Parser and Some::Data. A Some::Parser object has methods to return objects of type Some::Data. I have written a class that extends the Some::Data class, let's call it My::Data. Objects of class My::Data are really just objects of class Some::Data, but with additional methods that make it easier to work with.
My problem is that I want to continue to use the Some::Parser class to do the hard work of parsing the data. As I said earlier, Some::Parser objects give me Some::Data objects. Once I have a Some::Data object in hand, is there any way to reclassify it as a My::Data object? How would I do this?
I'm totally willing to change my approach, assuming someone can suggest a better way of doing what I want to do, but writing my own parser is not something I'm interested in doing!

This smells like a bit of a kludge. It might be time to rethink your strategy. For example, maybe you should write My::Parser which returns My::Data objects.
But if you don't want to do that, you can manually use bless to change an object's class:
my $obj = Some::Data->new;
bless $obj, 'My::Data';
See bless in perldoc.

Probably the best way to handle something like this is for Some::Parser to provide a way to specify the class it should be using for data objects. For example, HTML::TreeBuilder provides the element_class method. If you want TreeBuilder to produce something other than HTML::Element nodes, you subclass HTML::TreeBuilder and override element_class to return your desired node class. (The actual code in TreeBuilder is a bit more complex, because there was a different mechanism for doing this prior to HTML-Tree 4, and the new maintainer didn't want to break that.)
I take it that you didn't write Some::Parser, but perhaps it has this capability already. If not, maybe its maintainer will accept a patch. It should be a fairly simple change. You'd just add a data_class method (sub data_class { 'Some::Data' }), and then change Some::Data->new to $self->data_class->new. Then you can subclass Some::Parser to create My::Parser, and just override data_class.

You can rebless anything.
Inheritance in Perl 5 is nothing more than searching #ISA.

You can re-bless the returned object to whatever your heart desires:
#!/usr/bin/perl
package Some::Data;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub a { $_[0]->{a} }
package My::Data;
use strict; use warnings;
use base 'Some::Data';
sub a_squared {
my $self = shift;
my $v = $self->a;
return $v * $v;
}
package Some::Parser;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub parse { return Some::Data->new(a => 3) }
package main;
use strict; use warnings;
my $data = Some::Parser->new->parse;
bless $data => 'My::Data';
printf "%.1f\t%.1f\n", $data->a, $data->a_squared;
Alternatively, you can use #cjm's idea:
#!/usr/bin/perl
package Some::Data;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub a { $_[0]->{a} }
package My::Data;
use strict; use warnings;
use base 'Some::Data';
sub a_squared {
my $self = shift;
my $v = $self->a;
return $v * $v;
}
package Some::Parser;
use strict; use warnings;
sub new { my $class = shift; bless { #_ } => $class }
sub parse {
my $self = shift;
return $self->data_class->new(a => 3);
}
sub data_class { $_[0]->{data_class} }
package main;
use strict; use warnings;
my $data = Some::Parser->new(data_class => 'My::Data')->parse;
printf "%.1f\t%.1f\n", $data->a, $data->a_squared;

I'd consider re-blessing venturesome. Once you have an object you can't really tell if it was created using its constructor ( usually Foo::new() ), or someone re-blessed some other object.
The problem is, some constructors are fat, this means they do a whole lotta more than just blessing something:
sub new {
my $pkg = shift;
my ($required) = #_;
croak "Bad call" unless defined $required;
_do_something_magic ($required);
my $self = { 'foo' => $required };
return bless $self, $pkg;
}
In this case your re-blessed object might not be the one you'll expect later in code.
One may consider constructors with "re-blessing" functionality build in. But such "object converters" will make the design even more complicated.
Stick to the basic definition: "An object is an instance of the class. Forever.".

Related

Catching undefined value accessing in Perl

How can I catch access to member variables?
$Class1->{Class2}
If the Class2 field doesn't exist, is is possible to catch this from an internal function?
You can, but you probably shouldn't. The problem here is - if you access a variable within a class directly... then you just can. You can prevent this with a couple of workarounds - and this is where things like Moose come in.
And there's a couple of slightly hacky tricks like inside-out objects (which I think aren't common practice any more - Perl Best Practice advocated them some years back) or using anonymous hashes to hold state.
But failing that - why not use an accessor, and auto-generate one using 'AUTOLOAD'.
#!/usr/bin/env perl
package MyClass;
use strict;
use warnings;
use vars '$AUTOLOAD';
sub AUTOLOAD {
my ( $self ) = #_;
my $subname = $AUTOLOAD =~ s/.*:://r;
if ( $self -> {$subname} ) {
return $self -> {$subname};
}
warn "Sub called $subname was called\n";
return "$subname";
}
sub new {
my ( $class ) = #_;
my $self = {};
bless $self, $class;
}
package main;
use strict;
use warnings;
my $object = MyClass -> new;
$object -> {var} = "fleeg";
print "Undef fiddle was: ", $object -> fiddle,"\n";
print "But 'var' was: ", $object -> var,"\n";
This has the same problem, in that changing method names might cause things to break. However it has the advantage that you can handle 'invalid' method calls however you like.
But really - explicit 'get' and 'set' methods are better choices for most use-cases.
You do this by providing proper getter/setter methods that wrap around your class/instance variables. The internals should never be accessed directly, particularly from outside of the class itself (it's wise to not do so within the class either, except for the actual method that maintains that specific attribute. Here's a very basic example:
use warnings;
use strict;
package A;
sub new {
my ($class, %args) = #_;
my $self = bless {}, $class;
$self->x($args{x});
$self->y($args{y});
return $self;
}
sub x {
my ($self, $x) = #_;
$self->{x} = $x if defined $x;
return $self->{x} // 1;
}
sub y {
my ($self, $y) = #_;
$self->{y} = $y if defined $y;
return $self->{y} // 2;
}
package main;
my $obj = A->new(x => 5, y => 3);
print $obj->x ."\n";
print $obj->y ."\n";
Now, you could just as easily do print $obj->{x}, but that's where your problem is. What happens when the code is much more complicated than this, and for some reason you want to change the x attribute name to foo, but retain the x() method? $obj->{x} will now be undef as its never set.
Always use the provided methods for accessing attributes of a class/object. Encapsulation such as this is a staple of OO programming.

Perl + moose: Can't call method "x" on an undefined value

I'm just trying to do this: http://modernperlbooks.com/mt/2011/08/youre-already-using-dependency-injection.html. Really not deviating too much at all from that example code.
Here's what I've got:
package M;
use Moose;
use Exporter;
use Data::Dumper;
sub new {
print "M::new!\n";
my $class = shift;
return bless {}, $class;
}
sub x {
my ($self, $stuff) = #_;
print Dumper($stuff);
}
#################################
package Foo;
use Moose;
use Exporter;
our #ISA = qw(Exporter);
our #EXPORT = ();
has 'mS', is => 'ro', default => sub { M->new };
sub new {
my $class = shift;
return bless {}, $class;
}
sub bar {
my ($self, $data) = #_;
# do stuff here...
# ...
my $foo = $self->mS;
# this...
$foo->x($data);
# ...causes "Can't call method "x" on an undefined value at Foo.pm line 45."
}
1;
It's worth noting that the M::new! message never appears, so I'm guessing that it's never reached. What's going on?
With Moose, you shouldn't write sub new. Moose provides the constructor for you.
Also, using Exporter makes no sense with object-oriented modules. The following program works for me:
#!/usr/bin/perl
{ package M;
use Moose;
use Data::Dumper;
sub x {
my ($self, $stuff) = #_;
print Dumper($stuff);
}
}
{ package Foo;
use Moose;
has mS => ( is => 'ro', default => sub { 'M'->new } );
sub bar {
my ($self, $data) = #_;
my $foo = $self->mS;
$foo->x($data);
}
}
my $foo = 'Foo'->new;
$foo->bar('test');
You have a solution - don't write your own new() method when you're using Moose. But there's one other little point that might be worth making.
The constructor that Moose will give you for your Foo class will work pretty well as a drop-in replacement for your new() method. But the one that Moose gives you for your M class will be missing a feature - it won't print your "M::new!\n" message. How do we get round that?
In Moose, you can define a BUILD() method which will be called immediately after new() has returned a new object. That's a good place to put any extra initialisation that your new object needs. It would also be be a good place for your print() call (although it happens after object construction, not before - so it's not an exact replacement).

Perl: How to make sure overridden method is called when accessed from within the base class

I have a base class which calls a method which is overridden in a child class, in Perl. Currently, it still calls the base class version of the method, but I want it to call the base if there is one. Here is a simplified rendition of my code:
package Test;
use strict;
use warnings;
sub Main
{
my $self = shift;
return $self->SomeFunc();
}
sub SomeFunc
{
my $self = shift;
#...
return 1;
}
package Test2;
use strict;
use warnings;
use base qw(Test);
sub SomeFunc
{
my $self = shift;
#...
return 0;
}
package main;
use Test2;
my $test = new Test2();
print $test->Main();
and I am getting a 1 when I run this!
PS my apologies, I'm not used to creating examples in working perl code, please forgive the obvious errors.
The problem would be in your constructor, but you don't have one so your code doesn't even do what you say it does
You have probably written something like
sub new {
bless {};
}
which blesses an empty hash into the current package. Instead you need to take the class name from the first parameter passed to the constructor, like this
You should also avoid using capital letters in your lexical identifiers as they are reserved for global identifiers like package names. If you must use CamelCase then at least makeSureTheFirstLetterIsLowerCase. The standard for both Perl and Python is to use the much_clearer_snake_case
Test.pm
package Test;
use strict;
use warnings;
sub new {
my $class = shift;
bless {}, $class;
}
sub main {
my $self = shift;
$self->some_func();
}
sub some_func {
my $self = shift;
'Test::some_func';
}
Test2.pm
package Test2;
use strict;
use warnings;
use parent 'Test';
sub some_func {
my $self = shift;
'Test2::some_func';
}
main.pl
use strict;
use warnings;
my $test = Test->new;
print $test->main, "\n";
$test = Test2->new;
print $test->main, "\n";
output
Test::some_func
Test2::some_func
new doesn't mean anything in perl unless you make a function with that method name.
You need to bless an object
You can either directly bless an object
my $test = { };
bless $test, "Test2";
or make a new method that does the blessing for you:
sub new{
my $class = shift;
my $test = { };
bless $test, $class;
}

How to get parent class instance in Perl

I've used/created lots of classes in Perl and one common thing I need to do in them is to access certain properties of a parent object. For example, let's say I have Class_A and Class_B and they are as simple as this:
Class_A.pm
package Class_A;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
}
Class_B.pm
package Class_B;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
}
Class_A can use Class_B and create an instance as a property. Both have the property history_data, but if Class_B is an instance created by an instance of Class_A, I want Class_B to use hitory_data in its parent object.
So, what I've done all this time is to just pass a reference when I create the child instance, like this:
#!/usr/bin/perl
use strict;
use warnings;
use Class_A;
use Class_B;
my $class_a = new Class_A;
$class_a->{instance_of_B} = new Class_B parent => $class_a;
I'm doing that very simple, but that instance could be created by some method inside Class_A, when needing to use Class_B. The thing here is that, although Class_B has it's own properties, I want it to use the properties of Class_A when it is its parent.
That works well, but I've several times wondered if it exists a way to do that without passing the parent reference to the child. something like an already implemented way in Perl to call the parent object.
So, that's my question. Is there a way for Class_B to access the instance of its parent Class_A without receiving the reference directly?
Thanks. :-)
EDIT: Another way to put it is this:
Is there a way for Class_B, without having a reference passed, to say "I'm currently an instance of Class_B, living in a property of Class_A, which currently has x, y and z values on its own properties"?
As Michael Carman correctly notes, if you want the Class B object to know which Class A object it is part of, you have to tell it that explicitly.
What you can at least do, however, is to simplify the user interface by having Class A take care of creating its Class B components, e.g. like this:
package Class_A;
use strict;
use warnings;
sub new {
my $class = shift;
my $this = {
history_data => [],
};
bless $this, $class;
$this->{instance_of_B} = Class_B->new( parent => $this );
return $this;
}
Ps. Note that, if the Class A and Class B objects both hold a reference to each other, then what you've just created is a circular reference. Perl only has simple reference-counting garbage collection, which cannot automatically detect such reference circles; this means that, if you then let both objects go out of scope without explicitly breaking at least one link in the circle, the objects will never be garbage-collected (until your program ends) and your code ends up leaking memory.
One way to work around this issue is to weaken one of the references — normally the one from B to A, assuming that the Class A object is the one actually referenced by external code — like this:
package Class_B;
use strict;
use warnings;
use Scalar::Util qw(weaken);
sub new {
my $class = shift;
my $this = { #_ };
weaken $this->{parent};
bless $this, $class;
}
Note that, as a side effect, if someone grabs a reference directly to the Class B object, lets its parent Class A object go out of scope and then tries to call the Class B object's methods directly, the Class B object may find that its parent object has simply disappeared and the reference to it has become undefined. This is, unfortunately, an unavoidable side effect of dealing with Perl's method of garbage collection.
No. An object only knows about it's parent object if you define and set an attribute for it. (A hash entry, in your case.) Perl doesn't track who created whom in the background.
There seems to be a design issue somewhere here. However, see if Aspect::Library::Wormhole might help.
Depending on the exact circumstances, using a dependency injection framework such as Beam::Wire might help.
Update
I am not advocating the code below as a "good" solution or even "good" practice, but here is how Wormhole might help:
package A;
use strict; use warnings;
sub new {
my $class = shift;
bless {
b => undef,
history_data => [],
} => $class;
}
sub set_b {
my $self = shift;
$self->{b} = B->new;
return $self->{b};
}
package B;
use strict; use warnings;
sub new {
my $class = shift;
my $owner = shift;
bless {
history_data => $owner->{history_data} // [],
} => $class;
}
sub add_event {
my $self = shift;
push #{ $self->{history_data} }, [ #_ ];
return;
}
package main;
use strict; use warnings;
use Aspect;
use YAML;
aspect Wormhole => 'A::add_b', 'B::new';
my $x = A->new;
my $y = $x->set_b;
$y->add_event(Horizon => 180, 0, 'Terminal');
print Dump $x;
print Dump $y;

How should I access instance data in a Perl subclass?

I am extending a module and I want some tips on good practices. Specially namespace conflicts: what exactly are they and how to avoid them.
When extending, should I not access variables in the SUPER class and only alter its state through accessors or object methods? What to do in case there are no (or limited) accessors? Am I "allowed" to access these object variables directly?
Cheers!
It is best to only access things through accessors because this prevents changes in the implementation of the superclass from affecting the subclasses. You should stay far away from anything that starts with an underbar. Those things are private to the class. Try to stay away from anything that is not documented. Relying on those things will get you into trouble. Also, consider using has-a versus is-a relationship.
Let's imagine a widget class. This class has name and price members (note, none of this is particularly good code, I have just tossed of a version with out thinking about it for the sake of an example):
package Widget;
use strict;
use warnings;
sub new {
my $class = shift;
my %args = #_;
return bless {
price => $args{price} || 0,
name => $args{name} || "unkown",
}, $class;
}
sub price { shift->{price} }
sub name { shift->{name} }
1;
You decide to subclass widget to add a weight member:
package Widget::WithWeight;
use strict;
use warnings;
use base 'Widget';
sub new {
my $class = shift;
my %args = #_;
my $self = $class->SUPER::new(%args);
$self->{weight} = $args{weight} || 0;
return bless $self, $class;
}
sub weight { shift->{weight} }
sub price_per_pound {
my $self = shift;
return $self->{price}/$self->{weight};
}
1;
Now imagine the author of the first module changes his/her mind about how to store the price. Perhaps it was stored as a floating point number and the author realized that storing it as an integer number of pennies would be better:
package Widget;
use strict;
use warnings;
sub new {
my $class = shift;
my %args = #_;
if ($args{price}) {
$args{price} =~ s/[.]//;
}
return bless {
price => $args{price} || "000",
name => $args{name} || "unkown",
}, $class;
}
sub price {
my $self = shift;
my $price = $self->{price};
substr($price, -2, 0) = ".";
return $price;
}
sub name { shift->{name} }
1;
Suddenly, your tests will start failing, but if you had used the price accessor instead, you would have been insulated from that change.
Namespace conflicts can happen if you inherit from two modules into one and they both provide (export) the same sub.
I suggest you have a look at Moose, an extension to Perl that provides you with classes and roles. You can avoid many conflicts if you use roles. See http://www.iinteractive.com/moose/
Moose also makes automatic accessors for the class variables, making it safer to access them from inheriting classes.