Moose class attribute that acts like Class::Data::Inheritable - perl

I've got a class attribute in a Moose class, but I'd like it to work like Class::Data::Inheritable in terms of a subclass overriding the parent's value. That is, the subclass inherits the parent's value until the setter is called on the subclass, at which point the values become distinct. e.g.
#!/usr/bin/perl
use warnings;
use strict;
{
package Foo;
use Moose;
use MooseX::ClassAttribute;
class_has Item => ( is => 'rw' );
}
{
package Bar;
use Moose;
extends 'Foo';
}
Foo->Item(4);
# This prints "4, 4" as expected
#
print join( ", ", Foo->Item(), Bar->Item() ) . "\n";
Bar->Item(5);
# Would like this to print "4, 5", but it prints "5, 5"
#
print join( ", ", Foo->Item(), Bar->Item() ) . "\n";
What's the best way to get this effect, with MooseX::ClassAttribute or otherwise? Seems like desirable behavior for any class w/class-data that expects to be inherited from.

I know you asked for inheritance but may be roles would help you to get your problem solved in a different way.
Try it with a simple example:
#!/usr/local/bin/perl
use strict;
use feature 'say';
{
package Bomb;
use Moose::Role;
sub fuse { say "Bomb explode" }
sub explode { say "Bomb fuse"}
}
{
package Spouse;
use Moose::Role;
sub fuse { say "Spouse explode"}
sub explode { say "Spouse fuse"}
}
{
package PracticalJoke;
use Moose;
with 'Bomb' => { excludes => 'explode' },
'Spouse' => { excludes => 'fuse' };
}
my $joke = PracticalJoke->new();
$joke->fuse();
$joke->explode();
And with the 'excludes' you can exactly control what should happen.
Have a look at why roles are awsome
and the slides from Ovid about inheritance versus roles.

I had the same problem and found this page when looking for a solution. It's almost a decade after the original post but, as I have now worked out a solution, it might help the next person.
The solution was to add MooseX::ClassAttribute to Bar and a cloned version of class_has Item ... (note the leading + on Item).
{
package Bar;
use Moose;
use MooseX::ClassAttribute;
extends 'Foo';
class_has '+Item' => ();
}
I can now change the subclass Item without it affecting the superclass Item.

Related

Accessing class variables in inherited function?

I'm trying to create child classes in Perl that inherit class functions from a single parent. I got it to partially work, using the object method syntax Child->inheritedMethod() to call inherited functions outside the child, and my $class=shift; $class->inheritedMethod(); inside the child class, as described here.
However, for inherited methods, it seems control is passed to parent class, and the method is run in the parent scope with the parent variables. For example, this is in the Parent class:
our $VERSION = 0.11;
our $NICKNAME = "Parent Base";
sub version{ $VERSION }
sub whoami{ $NICKNAME }
sub whereami{
my $class = shift;
print "should be printing whereami right now...\n";
print "## In ",(caller(1))[3]," of ",$class->whoami," ",$class->version," in ",__PACKAGE__,"\n";
}
Each child class declares its own $VERSION and $NICKNAME, which I hoped would be accessed in place of the parent variables. But when I call whereami from the child, it gives
## Child::Method of Parent Base 0.11 in Parent.
Questions:
Is there a way around this? Some other module I should use like Moo(se)? Export all the methods instead of inheritance, which I hear shouldn't be done (polluting the namespace, not a problem here)?
Would this still be an issue using objects and object
attributes/variables? I'm trying to avoid it due to my team's
aversion to object-oriented.
Is this how inheritance usually works,
or just Perl? I thought the method would be called within the scope
of the child class, not passed to the parent.
The problem is that the method accesses the variable from the lexical scope where it was declared, i.e. the parent class. Class variables are therefore not the same thing as class attributes.
You can access the correct variable by fully qualifying its name (not possible under strict refs:
#!/usr/bin/perl
use warnings;
use strict;
{ package Parent;
our $package = 'Parent';
sub get_package {
my $class = shift;
{ no strict 'refs';
return (caller(0))[3], $class, ${"$class\::package"}
}
}
}
{ package Son;
use parent 'Parent';
our $package = 'Son';
}
print join ' ', 'Son'->get_package, "\n";
print join ' ', 'Parent'->get_package, "\n";
In Moo*, you can use Moo*X::ClassAttribute:
#!/usr/bin/perl
use warnings;
use strict;
{ package Parent;
use Moo;
use MooX::ClassAttribute;
class_has package => (is => 'ro',
default => 'Parent');
sub get_package {
my $class = shift;
return $class->package;
}
}
{ package Son;
use Moo;
use MooX::ClassAttribute;
extends 'Parent';
class_has package => (is => 'ro',
default => 'Son');
}
print 'Parent'->get_package, "\n";
print 'Son'->get_package, "\n";
Note that MooX::ClassAttribute says
Overriding class attributes and their accessors in subclasses is not yet supported.
Unlike in Moose, you can't use the class_has '+package' => (default => 'Son'); syntax for overriding.

How to override a sub in a Moose::Role?

I'm trying to implement a Moose::Role class that behaves like an abstract class would in Java. I'd like to implement some methods in the Role, but then have the ability to override those methods in concrete classes. If I try this using the same style that works when I extend classes I get the error Cannot add an override method if a local method is already present. Here's an example:
My abstract class:
package AbstractClass;
use Moose::Role;
sub my_ac_sub {
my $self = shift;
print "In AbstractClass!\n";
return;
}
1;
My concrete class:
package Class;
use Moose;
with 'AbstractClass';
override 'my_ac_sub' => sub {
my $self = shift;
super;
print "In Class!\n";
return;
};
__PACKAGE__->meta->make_immutable;
1;
And then:
use Class;
my $class = Class->new;
$class->my_ac_sub;
Am I doing something wrong? Is what I'm trying to accomplish supposed to be done a different way? Is what I'm trying to do not supposed to be done at all?
Turns out I was using it incorrectly. I opened a ticket and was shown the correct way of doing this:
package Class;
use Moose;
with 'AbstractClass';
around 'my_ac_sub' => sub {
my $next = shift;
my $self = shift;
$self->$next();
print "In Class!\n";
return;
};
__PACKAGE__->meta->make_immutable;
1;
Making this change has the desired effect.
Some time ago, I did this by having a role that consists solely of requires statements. That forms the abstract base class. Then, you can put your default implementations in another class and inherit from that:
#!/usr/bin/env perl
use 5.014;
package AbstractClass;
use Moose::Role;
requires 'my_virtual_method_this';
requires 'my_virtual_method_that';
package DefaultImpl;
use Moose;
with 'AbstractClass';
sub my_virtual_method_this {
say 'this';
}
sub my_virtual_method_that {
say 'that'
}
package MyImpl;
use Moose;
extends 'DefaultImpl';
with 'AbstractClass';
override my_virtual_method_that => sub {
super;
say '... and the other';
};
package main;
my $x = MyImpl->new;
$x->my_virtual_method_this;
$x->my_virtual_method_that;
If you want to provide default implementations for only a few methods define in the role, remove the requires from DefaultImpl.
Output:
$ ./zpx.pl
this
that
... and the other

How can I apply a Moose method modifier to a method based on a method attribute?

I want to apply a Moose 'before' method modifier to a number of methods in my class. I want to provide the modifier method in a role. I can do it a bit like this:
package MyApp::Role;
use Moose::Role
before [qw(foo bar)] => sub {
...
};
package MyApp;
use Moose;
with (MyApp::Role);
sub foo { ... }
sub bar { ... }
sub baz { ... } # this method is unaffected
However, having to maintain the list of relevant methods in the role ties it to the consuming class and that just seems wrong. I would like to do it a smarter way, like with method attributes:
package MyApp;
use Moose;
with (MyApp::Role);
sub foo :SomeFlag { ... }
sub bar :SomeFlag { ... }
sub baz { ... } # this method is unaffected
I'm not familiar with how to identify method attributes or how I would dynamically apply method modifiers to them.
Or, maybe there is a better way of doing this?
Let's use Attribute::Handlers for this – a fairly sane way to use attributes. We must define a function in a base class which itself has the attribute :ATTR(CODE). This takes a number of arguments:
The package where the sub (or other variable) comes from.
A globref, or the string ANON.
A reference to the value (here: coderef).
The name of the attribute.
Optional data for the attribute.
The (compilation) phase where the attribute was invoked.
The filename where the sub was declared.
The line number where the sub was declared.
So what we can do is to write a handler that applies a before:
use strict; use warnings; use feature 'say';
BEGIN {
package MyRole;
use Moose::Role;
use Attribute::Handlers;
sub SomeFlag :ATTR(CODE) {
my ($package, $globref, $code, $attr, $data, $phase, $filename, $line) = #_;
ref($globref) eq 'GLOB'
or die "Only global subroutines can be decorated with :SomeFlag"
. " at $filename line $line.\n";
# use the MOP to install the method modifier
$package->meta->add_before_method_modifier(
*$globref{NAME} => sub {
warn "Just about to call a flagged sub!";
},
);
}
}
BEGIN {
package MyApp;
use Moose;
# important: SomeFlag must be available before the attrs are handled (CHECK phase)
BEGIN { with 'MyRole' };
sub foo :SomeFlag { say "Hi from foo sub!" }
sub bar :SomeFlag { say "Hi from bar sub!" }
sub baz { say "Hi from baz sub!" }
}
package main;
my $o = MyApp->new;
$o->$_ for qw/foo bar baz/;
I stuffed all of this into a single file, but that obviously isn't neccessary (just add the required uses).
Output:
Just about to call a flagged sub! at so.pl line 16.
Hi from foo sub!
Just about to call a flagged sub! at so.pl line 16.
Hi from bar sub!
Hi from baz sub!

How to extend Class::Multimethods::Pure to recognise Moose Roles?

I need multemethod dispatch with Moose objects. I'm doing this with Class::Multimethods::Pure. I chose this instead of MooseX::MultiMethods because it depends on MooseX::Method::Signatures which can't install on my system because it fails its tests. I don't mind if you have an alternative approach to suggest.
The following works fine with types and subtypes:
package Foo::Type;
use Moose;
package Foo::SubType;
use Moose;
extends 'Foo::Type';
package main;
use Class::Multimethods::Pure;
multi hello => ('Foo::Type') => sub {
my ( $foo ) = #_;
print $foo;
};
hello( Foo::SubType->new );
But the scenario I now need to handle is where the declared type is actually a Moose Role:
package Foo::Role;
use Moose::Role;
package Foo::Type;
use Moose;
with 'Foo::Role';
package main;
use Class::Multimethods::Pure;
multi hello => ('Foo') => sub {
my ( $foo ) = #_;
print $foo;
};
hello( Foo::Type->new );
But this can't recognise the role:
No method found for args (Foo::Type=HASH(0x22ac854))
The documentation says it can be extended in various ways, including adding Perl 6-ish roles. But it's a little sketchy for me and I'm looking for a more detailed example. Has anyone tried this?
My solution was to convert the roles to abstract base classes using MooseX::ABC. In this way, they could be recognised as a class type.
On a side note, I managed to get MooseX::MultiMethods working on another system. It does work with roles, but it can't figure out which to use if we define a multimethod that takes the class and another multimethod that takes the role. Incidentally, MooseX::ABC resolved this issue also since it gave me a hierarchical structure which the roles did not really have.
package Foo::Role;
use Moose::Role;
package Foo::Type;
use Moose;
with 'Foo::Role';
package Merger;
use Moose;
use MooseX::MultiMethods;
multi method hello (Foo::Role $foo) {
print 'Foo::Role: '.$foo;
}
multi method hello (Foo::Type $foo) {
print 'Foo::Type: '.$foo;
}
package main;
my $merger = Merger->new;
my $f = Foo::Type->new;
$merger->hello( $f );
# Ambiguous match for multi method hello: (Foo::Role $foo), (Foo::Type $foo)
# with value [ Merger{ }, Foo::Type{ } ]

OO-Perl Aliasing Class Attributes

I have a module that I'm working on. I am setting up a few attributes like this:
$self->{FOO};
$self->{BAR};
$self->{FOOBAR};
And, I want to use AUTOLOAD to help create methods for accessing these attributes. For example, $foo->Bar() returns the value of $self->{BAR}. No problem. Everything is standard.
Now, I want to create alias Methods. For example, if someone says $obj->Fu();, I'll return $self->{FOO}. What I'd like to do is create a $self->{FU} that points to the same memory location as $self->{FOO}. That way, when I set the value of $self->{FOO}, $self-{FU} is also set. This way, I don't have to make all sorts of changes in the way AUTOLOAD works or remember to set $self->{FU} whenever I set $self->{FOO}.
Any easy way of doing this?
Yes, use Moose, rather than attempting to make explicit mapping between hash
keys. Writing your own accessors, or using AUTOLOAD, is not necessary and has
a much higher chance of error:
package MyClass;
use Moose;
use MooseX::Aliases;
has foo => (
is => 'rw', isa => 'Str',
alias => 'fu',
);
has bar => (
is => 'rw', isa => 'Str',
);
__PACKAGE__->meta->make_immutable;
no Moose;
1;
package main;
use strict;
use warnings;
use MyClass;
my $obj = MyClass->new;
$obj->foo("value");
$obj->fu("a new value");
# prints "foo has the value 'a new value'"
print "foo has the value '", $obj->foo, "'\n";
I would recommend Moose over what you're doing, but the easiest way to accomplish what you're asking is probably this:
sub Fu { shift->Foo(#_) }
This way, it doesn't matter if Foo is autoloaded or not.
The non-Moose solution is to just create an alias in the symbol table. It's not a common thing to do, and I suspect that whatever you are trying to do has a better way, Moose or otherwise. Don't use any of this if you can avoid it with a better design or interface, which are often the superior solutions to things like this.
In this AUTOLOAD routine, I look at a %Aliases hash to figure out other methods else I have to define. When I have aliases, I make proper aliases in the symbol table. It's a bit ugly, but it avoids adding another actual method in the call stack:
#!perl
use 5.010;
{
package SomeClass;
use Carp;
use vars qw($AUTOLOAD);
sub new {
return bless {
map { $_, undef } qw(FOO BAR FOOBAR)
}, $_[0];
};
my %Aliases = (
FOO => [ qw(fu) ],
);
sub AUTOLOAD {
our $method = $AUTOLOAD;
$method =~ s/.*:://;
carp "Autoloading $method";
{
no strict 'refs';
*{"$method"} = sub {
#_ > 1
?
$_[0]->{"\U$method"} = $_[1]
:
$_[0]->{"\U$method"}
};
foreach my $alias ( #{ $Aliases{"\U$method"} } ) {
*{"$alias"} = *{"$method"};
}
goto &{"$method"};
}
}
sub DESTROY { 1 }
}
my $object = SomeClass->new;
$object->foo(5);
say "Foo is now ", $object->foo;
say "Foo is now ", $object->foo(9);
say "Fu is now ", $object->fu;
say "Fu is set to ", $object->fu(17);
say "Foo is now ", $object->foo;
Now foo and fu access the same thing:
Foo is now 5
Foo is now 9
Fu is now 9
Fu is set to 17
Foo is now 17