What should I do with an object that should no longer be used in Perl? - perl

I am writing a class that is linked to an external resource. One of the methods is a delete method that destroys the external resource. No further method calls should be made on that object. I was thinking of setting a flag and die'ing inside of all of the methods if the flag is set, but is there a better, easier way? Something involving DESTROY maybe?
So far, I am really liking Axeman's suggestion, but using AUTOLOAD because I am too lazy to recreate all of the methods:
#!/usr/bin/perl
use strict;
use warnings;
my $er = ExternalResource->new;
$er->meth1;
$er->meth2;
$er->delete;
$er->meth1;
$er->meth2;
$er->undelete;
$er->meth1;
$er->meth2;
$er->delete;
$er->meth1;
$er->meth2;
$er->meth3;
package ExternalResource;
use strict;
use warnings;
sub new {
my $class = shift;
return bless {}, $class;
}
sub meth1 {
my $self = shift;
print "in meth1\n";
}
sub meth2 {
my $self = shift;
print "in meth2\n";
}
sub delete {
my $self = shift;
$self->{orig_class} = ref $self;
return bless $self, "ExternalResource::Dead";
}
package ExternalResource::Dead;
use strict;
use Carp;
our $AUTOLOAD;
BEGIN {
our %methods = map { $_ => 1 } qw/meth1 meth2 delete new/;
}
our %methods;
sub undelete {
my $self = shift;
#do whatever needs to be done to undelete resource
return bless $self, $self->{orig_class};
}
sub AUTOLOAD {
my $meth = (split /::/, $AUTOLOAD)[-1];
croak "$meth is not a method for this object"
unless $methods{$meth};
carp "can't call $meth on object because it has been deleted";
return 0;
}

Is there a problem with simply considering the object in an invalid state. If the users hang on to it, isn't that their problem?
Here are some considerations:
Have you already decided whether it's worth dying over?
Chances are that if you have a function that is encapsulated enough, you really don't want to have the users parse through your code. For that purpose, you probably wouldn't like to use what I call the Go-ahead-and-let-it-fail pattern. 'Can't call method "do_your_stuff" on an undefined value' probably won't work as well for encapsulation purposes. Unless you tell them "Hey you deleted the object!
Here are some suggestions:
You could rebless the object into a class whose only job is to indicate an invalid state. It has the same basic form, but all symbols in the table point to a sub that just says "Sorry can't do it, I've been shut down (You shut me down, remember?)."
You could undef $_[0] in the delete. Then they get a nice 'Can't call method "read_from_thing" on an undefined value' from a line in their code--provided that they aren't going through an elaborate decorating or delegation process. But as pointed out by chaos, this doesn't clear up more than one reference (as I've adapted by example code below to show).
Some proof of concept stuff:
use feature 'say';
package A;
sub speak { say 'Meow!'; }
sub done { undef $_[0]; }
package B;
sub new { return bless {}, shift; }
sub speak { say 'Ruff!' }
sub done { bless shift, 'A'; }
package main;
my $a = B->new();
my $b = $a;
$a->speak(); # Ruff!
$b->speak(); # Ruff!
$a->done();
$a->speak(); # Meow!
$b->speak(); # Meow! <- $b made the switch
$a->done();
$b->speak(); # Meow!
$a->speak(); # Can't call method "speak" on an undefined value at - line 28

Ideally, it should fall out of scope. If, for some reason, a proper scope can't be deliniated, and you're worried about references accidentally keeping the resource active, might consider weak references (Scalar::Util is core in at least 5.10).

You could make the users only get weakrefs to the object, with the single strong reference kept inside your module. Then when the resource is destroyed, delete the strong reference and poof, no more objects.

Following on from the comments in my first answer here is "one way" to amend an object behaviour with Moose.
{
package ExternalResource;
use Moose;
with 'DefaultState';
no Moose;
}
{
package DefaultState;
use Moose::Role;
sub meth1 {
my $self = shift;
print "in meth1\n";
}
sub meth2 {
my $self = shift;
print "in meth2\n";
}
no Moose::Role;
}
{
package DeletedState;
use Moose::Role;
sub meth1 { print "meth1 no longer available!\n" }
sub meth2 { print "meth2 no longer available!\n" }
no Moose::Role;
}
my $er = ExternalResource->new;
$er->meth1; # => "in meth1"
$er->meth2; # => "in meth2"
DeletedState->meta->apply( $er );
my $er2 = ExternalResource->new;
$er2->meth1; # => "in meth1" (role not applied to $er2 object)
$er->meth1; # => "meth1 no longer available!"
$er->meth2; # => "meth2 no longer available!"
DefaultState->meta->apply( $er );
$er2->meth1; # => "in meth1"
$er->meth1; # => "in meth1"
$er->meth2; # => "in meth2"
There are other ways to probably achieve what you after in Moose. However I do like this roles approach.
Certainly food for thought.

With Moose you can alter the class using its MOP underpinnings:
package ExternalResource;
use Moose;
use Carp;
sub meth1 {
my $self = shift;
print "in meth1\n";
}
sub meth2 {
my $self = shift;
print "in meth2\n";
}
sub delete {
my $self = shift;
my %copy; # keeps copy of original subref
my #methods = grep { $_ ne 'meta' } $self->meta->get_method_list;
for my $meth (#methods) {
$copy{ $meth } = \&$meth;
$self->meta->remove_method( $meth );
$self->meta->add_method( $meth => sub {
carp "can't call $meth on object because it has been deleted";
return 0;
});
}
$self->meta->add_method( undelete => sub {
my $self = shift;
for my $meth (#methods) {
$self->meta->remove_method( $meth );
$self->meta->add_method( $meth => $copy{ $meth } );
}
$self->meta->remove_method( 'undelete' );
});
}
Now all current and new instances of ExternalResource will reflect whatever the current state is.

Related

Can I associate a CODE reference with a HASH reference that contains it in Perl?

I want to create a hash reference with code references mapped to scalars (strings) as its members.
So far I have a map reference that looks something like this:
my $object;
$object = {
'code1' => sub {
print $_[0];
},
'code2' => sub {
return 'Hello, World!';
},
'code3' => sub {
$object->{code1}->($object->{code2}->());
}
};
$object->{code3}->();
I would like to be able to "bless" the 'code3' reference in $object with $object, so I can do something like:
my $object;
$object = {
'code1' => sub {
print $_[0];
},
'code2' => sub {
return 'Hello, World!';
},
'code3' => sub {
$self = shift;
$self->{code1}->($self->{code2}->());
}
};
$object->{code3}->();
However, bless only works with packages, rather than hash tables.
Is there a way to do this in Perl 5 version 22?
Note: now that I think of it, it's better to pass $object to the method explicitly, as it solves JavaScript's "this" problem. I am just too used to Java's "this" which makes sense in Java where everything is a class and therefore all methods have a "this", but in scripting, it really helps to know if the "this" is actually passed, or is it just called as a function(and you end up accidentally polluting global scope or triggering strict warning) passing $self explicitly makes it clear that you are not calling it as a function, but as a method.
You are doing sub calls (not method calls), so you simply forgot to pass $self as a parameter.
my $object = {
code1 => sub {
print $_[0];
},
code2 => sub {
return 'Hello, World!';
},
code3 => sub {
my $self = shift;
$self->{code1}->( $self, $self->{code2}->($self) );
}
};
$object->{code3}->($object);
But I think you're trying to create JavaScript-like objects. You can start with the following:
package PrototypeObject;
sub new {
my $class = shift;
my $self = bless({}, $class);
%$self = #_;
return $self;
}
sub AUTOLOAD {
my $self = shift;
( my $method = our $AUTOLOAD ) =~ s/^.*:://s;
return $self->{$method}->($self, #_);
}
1;
use PrototypeObject qw( );
my $object = PrototypeObject->new(
code1 => sub {
print $_[1];
},
code2 => sub {
return 'Hello, World!';
},
code3 => sub {
my $self = shift;
$self->code1( $self->code2() );
}
);
$object->code3();
Note that this will slow down your method calls as it must call AUTOLOAD before calling your method. This could be addressed by overloading the method call operator.
Check on CPAN. Someone might already have a more complete implementation.
This is not the exact syntax you want, but Perl 5 supports many ways of making method calls, including method calls via strings. So you could say:
#!/usr/bin/perl
{ package Foo;
use strict;
use warnings;
sub new { bless {}, shift }
sub code1 { my $self = shift; print "$_[0]\n" };
sub code2 { "Hello, World!" }
sub code3 {
my $self = shift;
my $method1 = "code1";
my $method2 = "code2";
$self->$method1($self->$method2);
}
}
use strict;
use warnings;
my $o = Foo->new;
print "normal call\n";
$o->code3;
print "via string\n";
my $method = "code3";
$o->$method;
Also, remember that a package's symbol table is a hash: %Foo::, so you can always go spelunking in there yourself:
#!/usr/bin/perl
{ package Foo;
use strict;
use warnings;
sub new { bless {}, shift }
sub code1 { my $self = shift; print "$_[0]\n" };
sub code2 { "Hello, World!" }
sub code3 {
my $self = shift;
my $method1 = "code1";
my $method2 = "code2";
$self->$method1($self->$method2);
}
}
use strict;
use warnings;
print $Foo::{code2}->(), "\n";
However, I would suggest having a really code reason for these techniques as it can make maintenance a nightmare (eg imaging trying to find all of the code calling Foo::approved, you can't just grep for "->approved" because the actual call is ->$state()).
I just read the comments and noticed you said
my concern with packages is that I can't seem to create packages at runtime, but I can create hash tables at runtime
Perl 5 does allow you to create packages at runtime. In fact, depending on how you define runtime, you can do anything at runtime with string eval as it reenters compile time when it is called. But there is also a pure-runtime method of manipulating the symbol tables with typeglobs:
#!/usr/bin/perl
{ package Foo;
use strict;
use warnings;
sub new { bless {}, shift }
}
use strict;
use warnings;
my $o = Foo->new;
# here we add functions at runtime to the package Foo
{
no warnings "once";
*Foo::code1 = sub { my $self = shift; print "$_[0]\n" };
*Foo::code2 = sub { "Hello, World!" };
*Foo::code3 = sub {
my $self = shift;
my $method1 = "code1";
my $method2 = "code2";
$self->$method1($self->$method2);
};
}
$o->code3;
Because Perl 5 is object oriented (and not object based like JavaScript) these methods are attached to all Foo objects. If you want individual objects have their own symbol tables, then I am there are certainly ways to do that. Off the top of my head, AUTOLOAD comes to mind:
#!/usr/bin/perl
{ package Foo;
use strict;
use Carp;
use warnings;
sub new {
bless {
symtab => {}
}, shift
}
sub AUTOLOAD {
my $self = shift;
our $AUTOLOAD;
my $method = $AUTOLOAD =~ s/.*:://r;
my (undef, $file, $line) = caller();
die "$method does not exist at $file line $line"
unless exists $self->{symtab}{$method};
$self->{symtab}{$method}->($self, #_);
}
sub DESTROY {} # prevent DESTROY method from being hijacked by AUTOLOAD
}
use v5.22;
use warnings;
my $o1 = Foo->new;
my $o2 = Foo->new;
$o1->{symtab}{inc} = sub { my $self = shift; $self->{i}++; };
$o1->inc;
$o1->inc;
$o1->inc;
say "inc called on o1 $o1->{i} times";
$o2->inc; #dies because we haven't defined inc for $o2 yet
Perl 5 is very flexible and will let you do just about anything you want (after all the motto is TIMTOWTDI), but you should always keep in mind the future programmer tasked with maintaining your code who may want to hunt you down and wear your skin for doing some of these tricks.
This question has a definite XY problem feel. It seems like you are trying to solve a problem in Perl 5 the same way you would have solved it in JavaScript. While Perl 5 will let you do that (as I have demonstrated), there may be a more idiomatic way of achieving the same effect. Can you describe what you are trying to do (not how you want to do it) in a different question and we can suggest the ways in which we would solve your problem.

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).

Extending a Perl non-Moose respecting encapsulation

I have a legacy project and I would like to extend a couple of classes in it with a few attributes and methods. I have access to the source code and know that the class uses a blessed hashref. I can of course go ahead and extend that hashref adding the keys that I want and re-bless into my class. But obviously this breaks encapsulation and I would like to avoid it as much as I can.
Is there a way to extend a (non-Moose) Perl class with attributes, not just methods, in a way that does not break encapsulation of the orginal class? The option to use Moose to do this is not available. Thank you.
First, one best practice for writing objects based on hashrefs is to prefix all fields with the package name, e.g.
package Parent;
sub new {
my ($class, $x, $y) = #_;
bless { "Parent::x" => $x, "Parent::y" => $y } => $class;
}
sub x { shift()->{"Parent::x"} }
sub y { shift()->{"Parent::y"} }
In that case, the issue doesn't arise, as every class has its own attribute namespace. But who writes his classes that way?
There are two ways I can think of to circumvent any problems: Proxying the original object via Autoload, or using inside-out object patterns. The third solution is to use prefixed attributes in your class, and hope that the parent never ever uses these names.
Inside-Out Objects
An inside-out object uses the blessed reference as an ID, and stores the attributes in lexical variables inside your class:
package Child;
use Scalar::Util qw/refaddr/;
use parent 'Parent';
my %foo;
sub new {
my ($class, $foo, #args) = #_;
my $self = $class->SUPER::new(#args);
$foo{refaddr $self} = $foo;
return $self;
}
sub foo {
my ($self) = #_;
$foo{refaddr $self};
}
sub set_foo {
my ($self, $val) = #_;
$foo{refaddr $self} = $val;
}
sub DESTROY {
my ($self) = #_;
# remove entries for this object
delete $foo{refaddr $self};
$self->SUPER::DESTROY if $self->SUPER::can('DESTROY');
}
This is a slightly dated pattern, but it works extremely well for your use case.
Proxy objects
We can contain a parent instance in a field of our class (i.e. both has-a and is-a relationship). Whenever we encounter unknown methods, we delegate to that object:
package Child;
use Parent ();
our $SUPER = 'Parent';
use Carp;
sub new {
my ($class, $foo, #args) = #_;
bless {
parent => $SUPER->new(#args),
foo => $foo,
} => $class;
}
sub foo {
my ($self) = #_;
$self->{foo};
}
sub set_foo {
my ($self, $val) = #_;
$self->{foo} = $val;
}
# manually establish pseudo-inheritance
# return true if our class inherits a given package
sub isa {
my ($self, $class) = #_;
return !!1 if $class eq __PACKAGE__;
return +(ref $self ? $self->{parent} : $SUPER)->isa($class);
}
# return a coderef to that method, or false
sub can {
my ($self, $meth) = #_;
my %methods = (new => \&new, foo => \&foo, set_foo => \&set_foo, DESTROY => \&DESTROY);
if (my $code = $methods{$meth}) {
return $code;
}
# check parent
my $code = ( ref $self ? $self->{parent} : $SUPER)->can($meth);
return undef unless $code;
return sub {
my $self = shift;
unshift #_, ref $self ? $self->{parent} : $self;
goto &$code;
};
}
# write explicit destroy to satisfy autoload
sub DESTROY {
my ($self) = #_;
$self->{parent}->DESTROY if ref $self and $SUPER->can('DESTROY');
}
sub AUTOLOAD {
# fetch appropriate method coderef
my $meth = our $AUTOLOAD;
$meth =~ s/.*:://; # clean package name from name
my $code = $_[0]->can($meth);
$code or croak qq(Can't locate object method "$meth" via package "#{[__PACKAGE__]}");
goto &$code;
}
The ugly part is to fake methods defined in superclasses in the can code: We have to wrap the actual method inside a anonymous sub that unpacks our object to call the method on the proxied object. The gotos make our extra levels invisible to the called code, which is neccessary when somebody uses caller.
Most of this boilerplate proxying code can be abstracted into another module (and probably is, somewhere on CPAN).

How to check some value on every call to object's methods?

I'd like to check on every call to my object's methods some value (in this case: token's age). Is it possible to set it to all methods at once? Like in constructor? I have such simple constructor:
sub new {
my $class = shift;
my %args = #_;
my $self = {};
$self->{key} = $args{key};
bless($self, $class);
($self->{token}, $self->{token_start}) = $self->_get_authorized_token();
return $self;
}
And bunch of methods, which depends of tokens age, like this:
sub add_item {
my $self = shift;
my %args = #_;
...
}
I'd like to avoid including age-checking in every method, so i look for more general way to implement it. Has there some?
All I can think of is to hide all your 'real' methods - either in the classical way with a preceding underscore, or in a hash of subroutines - and use AUTOLOAD to direct the call properly.
The example below shos the idea
module MyClass.pm
package MyClass;
use strict;
use warnings;
sub new {
bless {}, __PACKAGE__;
}
sub _method1 {
print "In method1\n";
}
sub _method2 {
print "In method2\n";
}
sub AUTOLOAD {
our $AUTOLOAD;
my ($class, $method) = $AUTOLOAD =~ /(.+)::(.+)/;
return if $method eq 'DESTROY';
my $newmethod = "${class}::_$method";
unless (exists &$newmethod) {
die qq(Can't locate object method "$method" via package "$class");
}
print "Preprocessing...\n";
goto &$newmethod
}
1;
program
use strict;
use warnings;
use MyClass;
my $thing = MyClass->new;
$thing->method1;
$thing->method2;
$thing->method3;
output
Preprocessing...
In method1
Preprocessing...
In method2
Can't locate object method "method3" via package "MyClass" at MyClass.pm line 23.
See Class::Method::Modifiers or Class::Method::Modifiers::Fast module.
I honestly think that if you're doing OO in Perl and you want to deal with things like attributes, method modifiers and deferred resource loading without the boilerplate, it's worth investing in learning Moose. To illustrate, this is one way to write what you want using Moose:
use Moose;
has key => (isa => 'Str', is => 'ro');
has token => (isa => 'HashRef', is => 'ro', lazy_build => 1);
before [qw(add_item method2 method3)] => sub {
my $self = shift;
if (do something with $self->token) {
# return, die, etc.
}
};
sub _build_token {
my $self = shift;
my $key = $self->key;
return { token => 'foo', token_start => time };
}
These might be helpful:
Moose::Manual::MethodModifiers
Moose::Cookbook::Basics::BinaryTree_BuilderAndLazyBuild

Deferring code on scope change in Perl

I often find it useful to be able to schedule code to be executed upon leaving the current scope. In my previous life in TCL, a friend created a function we called defer.
It enabled code like:
set fp [open "x"]
defer("close $fp");
which was invoked when the current scope exited. The main benefit is that it's always invoked no matter how/where I leave scope.
So I implemented something similar in Perl but it seems there'd be an easier way. Comments critiques welcome.
The way I did it in Perl:
create a global, tied variable which holds an array of subs to be executed.
whenever I want to schedule a fn to be invoked on exit, I use local to change the array.
when I leave the current scope, Perl changes the global to the previous value
because the global is tied, I know when this value change happens and can invoke the subs in the list.
The actual code is below.
Is there a better way to do this? Seems this would be a commonly needed capability.
use strict;
package tiescalar;
sub TIESCALAR {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub FETCH {
my $self = shift;
return $self->{VAL};
}
sub STORE {
my $self = shift;
my $value = shift;
if (defined($self->{VAL}) && defined($value)) {
foreach my $s (#{$self->{VAL}}) { &$s; }
}
$self->{VAL} = $value;
}
1;
package main;
our $h;
tie($h, 'tiescalar');
$h = [];
printf "1\n";
printf "2\n";
sub main {
printf "3\n";
local $h = [sub{printf "9\n"}];
push(#$h, sub {printf "10\n";});
printf "4\n";
{
local $h = [sub {printf "8\n"; }];
mysub();
printf "7\n";
return;
}
}
sub mysub {
local $h = [sub {printf "6\n"; }];
print "5\n";
}
main();
printf "11\n";
Well, your specific case is already handled if you use lexical filehandles (as opposed to the old style bareword filehandles). For other cases, you could always use the DESTROY method of an object guaranteed to go to zero references when it goes out of scope:
#!/usr/bin/perl
use strict;
use warnings;
for my $i (1 .. 5) {
my $defer = Defer::Sub->new(sub { print "end\n" });
print "start\n$i\n";
}
package Defer::Sub;
use Carp;
sub new {
my $class = shift;
croak "$class requires a function to call\n" unless #_;
my $self = {
func => shift,
};
return bless $self, $class;
}
sub DESTROY {
my $self = shift;
$self->{func}();
}
ETA: I like brian's name better, Scope::OnExit is a much more descriptive name.
Instead of using tie for this, I think I'd just create an object. You can also avoid the local that way too.
{
my $defer = Scope::OnExit->new( #subs );
$defer->push( $other_sub ); # and pop, shift, etc
...
}
When the variable goes out of scope, you have a chance to do things in the DESTROY method.
Also, in the example you posted, you need to check that the values you store are code references, and it's probably a good idea to check that the VAL value is an array reference:
sub TIESCALAR { bless { VAL => [] }, $_[0] }
sub STORE {
my( $self, $value ) = #_;
carp "Can only store array references!" unless ref $value eq ref [];
foreach { #$value } {
carp "There should only be code refs in the array"
unless ref $_ eq ref sub {}
}
foreach ( #{ $self->{VAL}} ) { $_->() }
$self->{VAL} = $value;
}
You may want to try out B::Hooks::EndOfScope
I Believe this works:
use B::Hooks::EndOfScope;
sub foo {
on_scope_end {
$codehere;
};
$morecode
return 1; # scope end code executes.
}
foo();
I think you want something like Scope::Guard, but it can't be pushed. Hmmm.
Thanks.
Trivially,
sub OnLeavingScope::DESTROY { ${$_[0]}->() }
used like:
{
...
my $onleavingscope = bless \sub { ... }, 'OnLeavingScope';
my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope';
...
}
(The extra level of having a reference to a reference to a sub is necessary only to work around an optimization (that's arguably a bug) when using a non-closure anonymous sub.)