I recently learned about Moose. When I create a new attribute in a subclass, it seems to somehow override other functions that should be working...
use strict; use warnings;
################################### VEHICLE ####################################
package Vehicle;
sub new{
my $classname = shift;
bless { wheels=>'unknown', color=>'unknown', #_ } => $classname
}
sub wheels{
my $vehicle = shift;
return $$vehicle{wheels}
}
sub setWheels{
my $vehicle = shift;
$$vehicle{wheels} = $_[0];
}
##################################### CAR ######################################
package Car;
use Moo; extends 'Vehicle';
sub new{
my $classname = shift;
my $vehicle = vehicle->new( #_ );
$vehicle->setWheels(4);
bless $vehicle => $classname
}
has 'spoiler' => ( is=>'rw', reader=>'rspoil', writer=>'setSpoiler' );
1
The issue is that when I create a Car object, it does not have 4 wheels. It has 'unknown' wheels. If I comment out the "has 'spoiler' => ..." statement at the bottom, it works just fine.
What is causing the issue?
What is the recommended way to do what I am doing?
Firstly, if you're writing a class using Moose, you should never define your own method called new. See Moose best practices.
Secondly, if you're using Moose to extend a non-Moose class, you probably want to use MooseX::NonMoose which is able to make that all work pretty smoothly.
Moo bakes in the extending non-Moo classes. Assuming that for your example you're working with a Vehicle class that isn't yours, but trying to write the child class in Moo, here's how to do it.
In Moo*, you don't declare a new. It handles that for you. You can mutate state by declaring a BUILD subroutine - this will get run after instantiation on the instantiated object from parent to child. Thus:
use strict; use warnings;
################################### VEHICLE ####################################
package Vehicle;
sub new{
my $classname = shift;
bless { wheels=>'unknown', color=>'unknown', #_ } => $classname
}
sub wheels{
my $vehicle = shift;
return $$vehicle{wheels}
}
sub setWheels{
my $vehicle = shift;
$$vehicle{wheels} = $_[0];
}
##################################### CAR ######################################
package Car;
use Moo; extends 'Vehicle';
sub BUILD {
my $self = shift;
if ($self->wheels eq 'unknown') {
$self->setWheels(4);
}
}
has 'spoiler' => ( is=>'rw', reader=>'rspoil', writer=>'setSpoiler' );
package Main;
use strict;
use warnings;
use Data::Printer;
p(Car->new(spoiler => 'big', color => 'bright red'));
my $strangecar = Car->new(spoiler => 'piddly', color => 'yellow', wheels => 3);
p($strangecar);
$strangecar->setWheels(6);
$strangecar->setSpoiler('not so piddly');
p($strangecar);
Output
Car {
Parents Vehicle
public methods (4) : BUILD, new, rspoil, setSpoiler
private methods (0)
internals: {
color "bright red",
spoiler "big",
wheels 4
}
}
Car {
Parents Vehicle
public methods (4) : BUILD, new, rspoil, setSpoiler
private methods (0)
internals: {
color "yellow",
spoiler "piddly",
wheels 3
}
}
Car {
Parents Vehicle
public methods (4) : BUILD, new, rspoil, setSpoiler
private methods (0)
internals: {
color "yellow",
spoiler "not so piddly",
wheels 6
}
}
To use Moo for both parent and child, you would do:
use strict; use warnings;
################################### VEHICLE ####################################
package Vehicle;
use Moo;
has 'wheels' => ( is=>'rw', writer=>'setWheels', default => sub { 'unknown' });
has 'color' => (is => 'rw', default => sub { 'unknown' });
##################################### CAR ######################################
package Car;
use Moo; extends 'Vehicle';
has 'spoiler' => ( is=>'rw', reader=>'rspoil', writer=>'setSpoiler' );
has '+wheels' => ( default => sub {4} );
package Main;
use strict;
use warnings;
use Data::Printer;
p(Car->new(spoiler => 'big', color => 'bright red'));
my $strangecar = Car->new(spoiler => 'piddly', color => 'yellow', wheels => 3);
p($strangecar);
$strangecar->setWheels(6);
$strangecar->setSpoiler('not so piddly');
p($strangecar);
Which yields similar output to the above code.
Related
I have a Class defined with Class::Accessor like that:
package Worker;
use Class::Accessor 'antlers';
# PROPS
has first_name => ( is => 'rw' );
has position => ( is => 'rw' );
# METHODS
sub print {
my $self = shift;
print "------------\n";
print "Ref: ", ref $self, "\n";
print "First Name: ", $self->first_name, "\n";
if ($self->position) {
print "Position: ", $self->position, "\n";
}
}
1;
Now I want to create the Engineer class that extends Worker in such a way that property always set's to: position => 'Engineer'
For example:
package Engineer;
use Class::Accessor 'antlers';
use Worker;
extends(qw/Worker/);
# METHOS
sub new {
return bless(__PACKAGE__->SUPER::new({position => 'Engineer'}));
}
1;
But this doesn't work, because when I instantiate Engineer class like that:
use Data::Dumper;
use strict;
# Relative Path to Class libraries
use FindBin;
use lib "$FindBin::Bin/.";
use Worker;
use Engineer;
my $wor = Worker->new({first_name => 'Matt'});
my $eng = Engineer->new({first_name => 'Ray'});
$wor->print;
$eng->print;
I loose the first_name in the extended class Engineer:
------------
Ref: Worker
First Name: Matt
------------
Ref: Engineer
First Name:
Position: Engineer
On the other hand, I'm not sure if overriding the Engineer->new() method returning a bless is a good idea...
So, How should I extend the Worker class to get an Engineer class using Class::Accessor?
This new override seems to work well:
package Engineer;
use Class::Accessor 'antlers';
use Worker;
use Data::Dumper;
extends(qw/Worker/);
# METHODS
sub new {
my($class) = shift;
my $obj = __PACKAGE__->SUPER::new(#_);
$obj->position('engineer');
return bless $obj, $class;
}
Setters and getters still working for all fields and 'position' field is initialized when object is instantiated.
I want to be able to have a Moo* class with these characteristics:
an object's attribute can store a reference to the object itself
that attribute will be type-constrained using a Type::Tiny type so the reference must be of the right type
the class must function when it is immutable, and the attribute is "required", i.e. an undefined value is unacceptable and it cannot be updated later
E.g.
package GraphQLType;
use Moo;
use Types::Standard -all;
has [qw(children)] => (
is => 'rwp',
isa => ArrayRef[InstanceOf['GraphQLType']],
required => 1,
);
package main;
my $type;
$type = GraphQLType->new(children => [$type]);
The above presents a chicken-and-egg problem: $type will be undefined and therefore fail the type constraint.
A pattern used in graphql-js is "thunking". In Perl terms:
package GraphQLType;
use Moo;
use Types::Standard -all;
has [qw(children)] => (
is => 'rwp',
isa => CodeRef | ArrayRef[InstanceOf['GraphQLType']],
required => 1,
);
package main;
my $type;
$type = GraphQLType->new(children => sub { [$type] });
While that works for the specific type there, how can I have a parameterised type that implements something like this? Also, it will help even more if this can hook into the "lazy" functionality to minimise the code involved in storing the computed value.
package Thunking;
use Moo;
use Types::Thunking -all;
use Types::Standard -all;
has [qw(children)] => (
is => 'lazy',
isa => Thunk[ArrayRef[InstanceOf['GraphQLType']]],
required => 1,
);
Two issues need to be dealt with here: a parameterised Type::Tiny type constraint for a delayed-computation immutable attribute (DCIA), and an actually-functioning DCIA.
Parameterised type
Since this is Perl, there is more than one way to do this. The heart of making a parameterised type in Type::Tiny is to provide a constraint_generator parameter. The most idiomatic way to do this, using only Type::Tiny components, is:
package Types::Thunking;
use Types::TypeTiny -all;
use Type::Library -base;
use Type::Utils -all;
declare "Thunk", constraint_generator => sub { union [ CodeLike, #_ ] };
That's it! If no parameters are given, it works just like a CodeLike. The libraries can take care of any "inline" code generating.
The reason it can be so short is that the constraint_generator must return either a code-ref, which would probably be a closure that captures the parameters passed to it (see below), or simply a Type::Tiny - in which case the other parameterisability parameters are not needed. Since union (which looks like it's normally intended for producing arguments to a declare) returns a suitably-constructed Type::Tiny::Union, it just drops in perfectly.
A more spelled-out version, not using a union type (and for brevity, using CodeRef not CodeLike:
package Types::Thunking;
use Types::Standard -all;
use Type::Library -base;
use Type::Utils -all;
declare "Thunk",
constraint_generator => sub {
my ($param) = #_;
die "parameter must be a type" if grep !UNIVERSAL::isa($_, 'Type::Tiny'), #_;
return sub { is_CodeRef($_) or $param->check($_) };
},
inline_generator => sub {
my ($param) = #_;
die "parameter must be a type" if grep !UNIVERSAL::isa($_, 'Type::Tiny'), #_;
return sub {
my ($constraint, $varname) = #_;
return sprintf(
'Types::Standard::is_CodeRef(%s) or %s',
$varname,
$param->inline_check($varname),
);
};
};
This is the "harness" I used for testing these:
#!/usr/bin/perl
use Thunking;
sub do_test {
use Data::Dumper; local $Data::Dumper::Terse = 1; local $Data::Dumper::Indent = 0;
my ($args, $should_work) = #_;
my $l = eval { Thunking->new(#$args) };
if (!$l) {
say "correctly did not work" and return if !$should_work;
say "INcorrectly did not work" and return if $should_work;
}
my $val = eval { $l->attr };
if (!$val) {
say "correctly did not work" and return if !$should_work;
say "INcorrectly did not work" and return if $should_work;
}
say(($should_work ? "" : "INcorrectly worked: "), Dumper $val);
}
do_test [attr => { k => "wrong type" }], 0;
do_test [attr => ["real value at init"]], 1;
do_test [attr => sub { [ "delayed" ] }], 1;
do_test [attr => sub { { k => "delayed wrong type" } }], 0;
Delayed-computation immutable attribute
In order to make this immutable, we want setting the attribute to fail unless it's us doing it. When reading the attribute, we want to see whether there is computation to be done; if yes, do it; then return the value.
Naive approach
package Thunking;
use Moo;
use Types::Standard -all;
use Types::Thunking -all;
has attr => (
is => 'rwp',
isa => Thunk[ArrayRef],
required => 1,
);
before 'attr' => sub {
my $self = shift;
return if #_; # attempt at setting, hand to auto
my $value = $self->{attr};
return if ref($value) ne 'CODE'; # attempt at reading and already resolved
$self->_set_attr($value->());
}
The before should be fairly self-explanatory but you will see it manually looks in the object's hash-ref, which is usually a clue that your programming is not finished yet. Also, it's rwp and requires the before in the class, which is far from pretty.
Using MooX modules
An approach that tries to generalise this with a separate module, MooX::Thunking. First, another module to encapsulate overriding of Moo functions:
package MooX::Utils;
use strict;
use warnings;
use Moo ();
use Moo::Role ();
use Carp qw(croak);
use base qw(Exporter);
our #EXPORT = qw(override_function);
sub override_function {
my ($target, $name, $func) = #_;
my $orig = $target->can($name) or croak "Override '$target\::$name': not found";
my $install_tracked = Moo::Role->is_role($target) ? \&Moo::Role::_install_tracked : \&Moo::_install_tracked;
$install_tracked->($target, $name, sub { $func->($orig, #_) });
}
Now the thunking MooX module itself, which uses the above to override has:
package MooX::Thunking;
use MooX::Utils;
use Types::TypeTiny -all;
use Class::Method::Modifiers qw(install_modifier);
sub import {
my $target = scalar caller;
override_function($target, 'has', sub {
my ($orig, $name, %opts) = #_;
$orig->($name, %opts), return if $opts{is} ne 'thunked';
$opts{is} = 'ro';
$orig->($name, %opts); # so we have method to modify
install_modifier $target, 'before', $name => sub {
my $self = shift;
return if #_; # attempt at setting, hand to auto
my $value = $self->{$name};
return if !eval { CodeLike->($value); 1 }; # attempt at reading and already resolved
$self->{$name} = $value->();
$opts{isa}->($self->{$name}) if $opts{isa}; # validate
}
});
}
This applies "thunking" to an attribute. It will only function if the attribute is ro, and will quietly resolve any CodeLike values on reading. It can be used like this:
package Thunking;
use Moo;
use MooX::Thunking;
use Types::Standard -all;
use Types::Thunking -all;
has attr => (
is => 'thunked',
isa => Thunk[ArrayRef],
);
Using BUILDARGS and lazy
An alternative approach, suggested by the mighty #haarg:
package MooX::Thunking;
use MooX::Utils;
use Types::TypeTiny -all;
use Class::Method::Modifiers qw(install_modifier);
sub import {
my $target = scalar caller;
override_function($target, 'has', sub {
my ($orig, $name, %opts) = #_;
$orig->($name, %opts), return if $opts{is} ne 'thunked';
$opts{is} = 'lazy';
my $gen_attr = "_gen_$name";
$orig->($gen_attr => (is => 'ro'));
$opts{builder} = sub { $_[0]->$gen_attr->(); };
install_modifier $target, 'around', 'BUILDARGS' => sub {
my ($orig, $self) = (shift, shift);
my $args = $self->$orig(#_);
$args->{$gen_attr} = delete $args->{$name} if eval { CodeLike->($args->{$name}); 1 };
return $args;
};
$orig->($name, %opts);
});
}
It uses the built-in lazy mechanism, creating a builder that will call the supplied CodeLike if that is what is given. One important downside is that this technique does not work for Moo::Roles.
I have the following packages and files:
Child.pm
package Child;
use Father; # this should automatically extends Father also
has 'name' => (is => 'rw', default => "Harry");
1;
Father.pm
package Father;
use Moose;
sub import {
my ($class, #args) = #_;
my ($caller, $script) = caller;
my $package = __PACKAGE__;
{
no strict 'refs';
#{"${caller}::ISA"} = ($package, #{"${caller}::ISA"});
# tried this also
#eval {"package $caller; use Moose; extends qw($package);1;"}
}
}
1;
test.cgi
#!/usr/bin/perl
use Child;
my $child = Child->new;
print "child name: " . $child->name;
I want the package Child extends package Father automatically.
I put a code in the import function of Father to push to Child module ISA but did not work.
How to make this work, let Father module extends Child module during the import process.
Use the Moose keyword extends rather than use:
package Child;
use Moose;
extends 'Father';
You're only importing the package with use, not inheriting from it. What you are trying to do here is a hack, and while you may be able to get it to work, you're making it harder to understand. Particularly for other people who may have to deal with the code as well.
Looking at some exporting modules, I found Import::Into, it is very useful and solved the problem.
Here is how I solved the problem:
Child.pm
package Child;
use Father; # automatically extends Father also
has 'name' => (is => 'rw', lazy=>1, default => "Harry");
1;
Father.pm
package Father;
use Moose;
use utf8;
use Import::Into;
use Module::Runtime qw(use_module);
our #EXPORT_MODULES = (
Moose => [],
);
sub import {
my ($class, #args) = #_;
my ($caller, $script) = caller;
my $package = __PACKAGE__;
# ignore calling from child import
return if ($class ne $package);
my #modules = #EXPORT_MODULES;
while (#modules) {
my $module = shift #modules;
my $imports = ref($modules[0]) eq 'ARRAY' ? shift #modules : [];
use_module($module)->import::into($caller, #{$imports});
}
{
no strict 'refs';
#{"${caller}::ISA"} = ($package, #{"${caller}::ISA"});
}
}
sub father {
my $self = shift;
return "Potter";
}
1;
test.cgi
#!/usr/bin/perl
use Child;
my $child = Child->new;
print "child name: " . $child->name, "\n";
print "father name: " . $child->father, "\n";
output of test.cgi:
child name: Harry
father name: Potter
Try something like this:
#Father.pm
use Moose;
extends 'Father';
package Father;
...;
1;
__END__
to get rid of all the "extra code".
I want to define a "registry" hash in the base class that all subclasses can read and write to, how do I accomplish this with Moose/Perl?
Here is an implementation with plain Perl OO-style.
You have two classes, BaseClass with global variable $REGISTRY, and DerivedClass which inherits from BaseClass.
$REGISTRY is readable and writable from any class instance via registry() method.
#!/usr/bin/env perl
use 5.012;
use strict;
package BaseClass;
our $REGISTRY = {};
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
sub registry {
my $self = shift;
return $REGISTRY;
}
package DerivedClass;
push our #ISA, 'BaseClass';
package main;
my $base = BaseClass->new;
$base->registry->{ alpha } = 1;
my $derived = DerivedClass->new;
$derived->registry->{ beta } = 2;
say $_, ' -> ', $base->registry->{ $_ } foreach keys %{ $base->registry };
If you run this program you get:
alpha -> 1
beta -> 2
If you prefer an all-Moose solution you should try this one:
#!/usr/bin/env perl
use 5.012;
use strict;
package BaseClass;
use Moose;
our $_REGISTRY = {};
has '_REGISTRY' => (
is => 'rw',
isa => 'HashRef',
default => sub { return $_REGISTRY }
);
sub registry {
my $self = shift;
return $self->_REGISTRY;
}
__PACKAGE__->meta->make_immutable;
no Moose;
package DerivedClass;
use Moose;
use base 'BaseClass';
__PACKAGE__->meta->make_immutable;
no Moose;
package main;
my $base = BaseClass->new;
$base->registry->{ alpha } = 1;
my $derived = DerivedClass->new;
$derived->registry->{ beta } = 2;
say $_, ' -> ', $base->registry->{ $_ } foreach keys %{ $base->registry };
It yields the same result of the OO Perl program.
Note how the _REGISTRY attribute is defined. Moose doesn't like refs as default values: default => {} is forbidden, you have to wrap any reference as a return value in an anonymous subroutine.
How about just implement it as a method:
package BaseClass;
my $hash = {};
sub registry { $hash };
Sub-classes just use $self->registry->{$key} to access values and $self->registry->{$key} = $value to set them.
MooseX::ClassAttribute
OK. I have a problem trying to inherit constants set in a parent class for any of the child classes.
#!/usr/bin/perl
use strict;
use warnings;
package Car;
use Exporter qw( import );
our #EXPORT_OK = ( 'WHEELS', 'WINGS' );
use constant WHEELS => 4;
use constant WINGS => 0;
sub new {
my ( $class, %args ) = #_;
my $self = {
doors => $args{doors},
colour => $args{colour},
wheels => WHEELS,
wings => WINGS,
};
bless $self, $class;
return $self;
}
package Car::Sports;
use base qw( Car );
sub new {
my ( $class, %args ) = #_;
my $self = {
doors => $args{doors},
engine => $args{engine},
wheels => WHEELS,
wings => WINGS,
};
bless $self, $class;
return $self;
}
package main;
my $obj = Car->new( doors => 4, colour => "red" );
print Dumper $obj;
my $obj2 = Car::Sports->new( doors => 5, engine => "V8" );
print Dumper $obj2;
__END__
The error is:
Bareword "WHEELS" not allowed while "strict subs" in use at ./t.pl line 30.
Bareword "WINGS" not allowed while "strict subs" in use at ./t.pl line 30.
Execution of ./t.pl aborted due to compilation errors.
Now, I haven't come here to post without doing some research. I understand that one option would be to use Car qw( WHEELS WINGS) in Car::Sports. However, if I do that I get the following error, because the classes are all inline in the same file:
Can't locate Car.pm in #INC
For a variety of reasons, I need to keep my packages in one file. Is there a way around this? As constants are basically just subs, why do I have to import them when the same would not be true for a normal method?
Finally, I also know I can do this:
package Car::Sports;
use base qw( Car );
sub new {
my ( $class, %args ) = #_;
my $self = {
doors => $args{doors},
engine => $args{engine},
wheels => Car::WHEELS,
wings => Car::WINGS,
};
bless $self, $class;
return $self;
}
And it's fine... But I have a number of classes and want to make the inheritance of constants more generic that having to name the parent class explicitly (and sometimes it's not just the parent class, but the grandparent).
Many thanks in advance for any pointers!
Cheers
One workaround is to include the line
package Car::Sports;
use base qw( Car );
Car->import(qw(WHEELS WINGS));
AND use the sigils in the Car::Sports constructor:
...
wheels => &WHEELS,
wings => &WINGS,
...
Your Car class isn't defining its #EXPORTS_OK list until run-time. The sigils are required because the Car::Sports constructor is parsed at compile-time, and the compiler doesn't know there should be WHEELS and WINGS symbols in the Car::Sports namespace.
The only way to avoid the sigils is to define Car's exports at compile-time:
package Car;
our #EXPORT_OK;
BEGIN {#EXPORT_OK = qw(WHEELS WINGS)} # set at compile not run time
...
package Car::Sports;
use base qw(Car);
BEGIN {Car->import('WHEELS','WINGS')} # import before c'tor is parsed
You could also avoid these machinations by defining the Car class in its own Car.pm file. Then you would just say
use Car qw(WHEELS WINGS);
and everything in the Car.pm file would be parsed at compile time, AND the Exporter::import method (triggered by a call to Car::import) would automatically get run and import the desired symbols to your current namespace.
May this change suit your needs?
[...]
wheels => $class->SUPER::WHEELS,
wings => $class->SUPER::WINGS,
[...]
Using Data::Dumper you get:
$VAR1 = bless( {
'wings' => 0,
'colour' => 'red',
'doors' => 4,
'wheels' => 4
}, 'Car' );
$VAR1 = bless( {
'wings' => 0,
'engine' => 'V8',
'doors' => 5,
'wheels' => 4
}, 'Car::Sports' );
Alternative, you could do exactly what use does:
BEGIN {
package Car;
use Exporter qw( import );
#EXPORT_OK = qw( WHEELS );
...
$INC{'Car.pm'} = 1;
}
BEGIN {
package Car::Sports;
use Car qw( WHEELS );
#ISA = 'Car';
...
$INC{'Car/Sports.pm'} = 1;
}
Generally, exposing that something is a constant to any package other than the one defining it is actually a bad idea. This argues, among other things, against using unusual forms when referring to values that happen to be constant in other areas of your code.
The constant module actually supports an invocation form that hides the fact that we're talking about constants, inasmuch as calling constants as class methods works just fine:
package Car;
use constant default_wheel_count => 4;
package Car::Sports;
sub new {
my ($class) = #_;
return bless {
wheels => $class->default_wheel_count,
} => $class;
}
That's how one actually inherits constants, but it's still probably the wrong approach. Eliminating the copypasta by only using the constants from the classes that implement construction of those attributes is the actual right thing to do.