I'm Trying to extend a non-moose class, and when I call an accessor defined by moose for my extended class I'm getting the following error:
Not a HASH reference at accessor MyGraph::weight (defined at MyGraph.pm line 8) line 8
This is the simplified code:
package MyGraph;
use Moose;
use MooseX::NonMoose;
extends 'Graph';
has 'weight' => (
is => 'ro',
isa => 'Num',
);
no Moose;
__PACKAGE__->meta->make_immutable;
package main;
my $g = MyGraph->new;
$g->weight();
MooseX::NonMoose doesn't, out of the box, enable you to subclass a non-hashref class, and Graph uses an arrayref for its instances. The docs mention this, and suggest using MooseX::InsideOut to enable compatibility with non-moose classes that have other instance types.
The reference that the non-Moose class uses as its instance type must match the instance type that Moose is using. Moose's default instance type is a hashref.
Graph uses ARRAYREF as its instance type. MooseX::InsideOut is the solution.
package MyGraph;
use Moose;
use MooseX::InsideOut;
use MooseX::NonMoose;
extends 'Graph';
I've never done this but this looks like it might be what you want. http://metacpan.org/pod/MooseX::NonMoose
Related
I'm tinkering with Moose as introduced in Intermediate Perl. I have an abstract class Animal with a property sound. The default behaviour should be to complain that sound has to be defined in subclasses:
package Animal;
use namespace::autoclean;
use Moose;
has 'sound' => (
is => 'ro',
default => sub {
confess shift, " needs to define sound!"
}
);
1;
A subclass has to do nothing else than define sound:
package Horse;
use namespace::autoclean;
use Moose;
extends 'Animal';
sub sound { 'neigh' }
1;
But testing this with
use strict;
use warnings;
use 5.010;
use Horse;
my $talking = Horse->new;
say "The horse says ", $talking->sound, '.';
results in
Horse=HASH(0x3029d30) needs to define sound!
If I replace the anonymous function in Animal with something simpler as in
has 'sound' => (
is => 'ro',
default => 'something generic',
);
things work fine. Why is that? Why is the default function executed even though I override it in the subclass?
There's two things in play here: How attributes are initialized and how accessors work.
Non-lazy ('eager') attributes are initialized when the class is instantiated. That's why you can actually leave off the
say "The horse says ", $talking->sound, '.';
and get the same error. If you make the attribute lazy, on the other hand, the error goes away. And that leads us to the real reason: the difference between attributes, accessors, and builders.
Animal has an attribute, sound, which is just a place that stores some data related to instances of the class. Because sound was declared ro, Animal also has a method that acts as an accessor, confusingly also called sound. If you call this accessor, it looks at the value of the attribute and gives it to you.
But this value exists independent of the accessor. The accessor provides a way to get at the value, but the actual existence of the value is dependent on the attribute's builder. In this case, the builder for the attribute is the anonymous method sub { confess shift, " needs to define sound!" }, and it will get run as soon as the attribute needs to have a value.
In fact, if you leave out the is => 'ro', you will stop Moose from creating an accessor at all, and the error will still pop at construction time. Because that's when your class builds the sound attribute.
When the attribute needs its value depends on whether you've declared it as lazy or not. Eager attributes are given their values on object construction. It doesn't matter if there's an accessor, the builder gets called when the object is created. And in this case, the builder dies.
Lazy attributes are given their values the first time they are needed. The default accessor tries to get the value of the attribute, which causes the builder to fire, which causes the script to die. When you override sound, you replace the default accessor with one that doesn't call the builder and therefore doesn't die anymore.
Does that mean you should make the sound attribute lazy? No, I don't think so. There's better mechanisms available, depending on what exactly you are trying to assert. If what you are trying to assert is that Animal->sound must be defined, you can use BUILD like so:
package Animal;
use namespace::autoclean;
use Moose;
has 'sound' => (is => 'ro');
sub BUILD {
my ($self) = #_;
confess "$self needs to define sound!"
unless defined $self->sound;
}
1;
During object construction, each of the parent classes' BUILD methods gets called, which lets them make assertions about object state.
If, on the other hand, what you wanted to assert is that a subclass has to have overridden sound, it's better not to make sound an attribute at all. Instead,
package Animal;
use namespace::autoclean;
use Moose;
sub sound {
confess "Abstract method `sound` called!";
}
1;
I am fairly new to Moose, and have been trying to follow the best practices. I understand that we should try to make classes immutable when we can. My question is, after object construction, can we change the value of a public attribute using a setter and still have the class be immutable?
Yes. If a class is immutable, this only means we cannot add new attributes or new methods to the class. This allows the Moose system do do some neat optimizations.
Any instances of the class can still be mutated. In order for the instances to be immutable as well, all attributes must be readonly (is => 'ro').
Example:
package MyClass;
use Moose;
has attr => (is => 'rw'); # this attribute is read-write
__PACKAGE__->meta->make_immutable;
# after this, no new attributes can be added
Then:
my $instance = MyClass->new(attr => "foo");
say $instance->attr; # foo
$instance->attr("bar"); # here, we change the instance, but not the class
say $instance->attr; # bar
I'm an absolute newbie to Moose and so far I have read Moose and most of the Cookbook.
There a few things I don't get. I created the following package:
package MyRange;
use Moose;
use namespace::autoclean;
has [ 'start', 'end' ] => (
is => 'ro',
isa => 'Int',
required => 1,
);
__PACKAGE__->meta->make_immutable;
1;
Then:
use MyRange;
my $br = MyRange->new(
start => 100,
end => 180
);
Now I can access my fields using e.g. $br->{start}, but I can also modify them (although they are "read only") using e.g. $br->{start}=5000. I can also add new keys like $br->{xxx}=111.
Am I missing anything? Isn't the object protected in some way? What's the meaning of ro?
When you said is => 'ro' you told Moose to create read-only accessors for you, that is, a reader method. You call that as
$br->start;
or
$br->end;
Setting the attributes using those methods will result in an exception:
$br->start(42);
If you had used is => 'rw', then the above would work and update the attribute's value.
What you're doing is direct hash access on the object, which violates encapsulation and shouldn't ever be necessary when using Moose.
The Moose manual, i.e. all the documents under the Moose::Manual namespace explain that in detail. A good starting point for questions like this is probably Moose::Manual::Attributes.
When you access to the attribute with $br->{start}, you are bypassing the accessor and you are adressing directly the underlying Moose implementation. You can do it, but you are not supposed to. Also, if Moose changes the implementation, your code will break.
You should instead access the attribute using the accessor method:
my $start = $br->start;
When you say that the attribute is 'RO', it means you are not allowed to change the attribute value using the accessor:
$br->start(32);
Consider the following:
package MyApp::CGI;
use Moose;
use MooseX::NonMoose;
use Data::Dumper;
extends 'CGI::Application';
BEGIN {
print "begin isa = " . Dumper \#MyApp::CGI::ISA;
};
print "runtime isa = " . Dumper \#MyApp::CGI::ISA;
...
The output when this compiles is:
begin isa = $VAR1 = [
'Moose::Object'
];
runtime isa = $VAR1 = [
'CGI::Application',
'Moose::Object'
];
Why do I care? Because when I try to use a CGI::Application::Plugin::* class, it expects me to be inheriting from CGI::Application at compile-time already. The plugin class tries to call add_callback as a class method on my class, but can't, because my #ISA isn't set up yet.
What's the best way to solve this? Would tweaking #ISA manually in a BEGIN block interfere with MooseX::NonMoose?
Edit
The following appears to work, but I find it offensive:
package MyApp::CGI;
use Moose;
use MooseX::NonMoose;
use base 'CGI::Application';
extends 'CGI::Application';
I don't know enough (or anything, really) about Moose internals to know if this is a good idea.
I don't find use base 'CGI::Application'; extends 'CGI::Application'; to be terribly ghastly because it does precisely what you need:
At compile-time, #ISA contains 'CGI::Application', which exactly satisfies the usage requirements of CGI::Application::Plugin::*
At runtime, your class is a Moose descendant of CGI::Application, with all the ensuing benefits (being able to design the composition of your class with Moosey meta goodness). It's only after the extends 'CGI::Application' line is encountered that any work is done (i.e methods are called on your class) that rely on the work done by the extends statement: that your class descends from Moose::Object and you have a meta-class installed.
That said, jrockway's solution should also work:
BEGIN { extends 'CGI::Application' }
...where you get all the Moosey meta goodness just a little ahead of schedule from when you need it, and it shouldn't be too ahead of schedule, provided you already called use Moose and use MooseX::NonMoose in order to define extends.
(Addendum: Now I'm pondering the complilational complexities of creating the ability to force the parsing of a keyword at compile-time that are parsed immediately such as if they were wrapped in a BEGIN block. e.g. something like if Moose.pm declared use compiletime qw(extends). It would be a nice piece of syntactic sugar for sure.)
I have written a set of classes and interfaces that are implemented in Moose also using roles. What I am having trouble understanding is the exact differences in both usage and implementation of Moose traits vs. roles.
The Moose documentation states:
It is important to understand that roles and traits are the same thing. A role can be used as a trait, and a trait is a role. The only thing that distinguishes the two is that a trait is packaged in a way that lets Moose resolve a short name to a class name. In other words, with a trait, the caller can refer to it by a short name like "Big", and Moose will resolve it to a class like MooseX::Embiggen::Meta::Attribute::Role::Big.
It is my understanding that traits and roles are "the same". However, when implementing a basic test of the idea using the use Moose -traits 'Foo' syntax does not seem to do what I would expect. Surely I must be missing something here.
This first example fails with "Can't locate object method 'foo'"
package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
sub foo { warn 'foo' }
package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }
package MyApp::User;
use Moose -traits => 'HasTable';
__PACKAGE__->foo(); #Can't locate object method 'foo'
Compared to this one (which does work):
package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
sub foo { warn 'foo' }
package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }
package MyApp::User;
use Moose;
with 'MyApp::Meta::Class::Trait::HasTable';
__PACKAGE__->foo(); #foo
This is the only difference in how Moose uses the terms "Trait" and "Role".
Moose's documentation and APIs often use the term "traits" as "Roles applied
to Metaclasses". In your revised answer your first example applies the Role to
MyApp::User's metaclass via -traits, the second example applies it to the
class.
If you change your first example to:
package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
sub foo { warn 'foo' }
package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }
package MyApp::User;
use Moose -traits => 'HasTable';
__PACKAGE__->meta->foo();
You'll see "foo at [script]. line 3." Which is exactly what it supposed to
be doing.
UPDATE: Apparently I'm not exactly correct here. Traits are roles applied to instances. The -traits hook applies HasTable to the metaclass instance for MyApp::User. I have updated the relevant Moose docs.
You don't define a package 'x::Foo' with any role. Ripped straight from the documentation, we see that register_implementation returns the name of an actually defined package:
package MyApp::Meta::Class::Trait::HasTable;
use Moose::Role;
has table => (
is => 'rw',
isa => 'Str',
);
package Moose::Meta::Class::Custom::Trait::HasTable;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }
package MyApp::User;
use Moose -traits => 'HasTable';
__PACKAGE__->meta->table('User');
The "shortcut" is achieved by Moose looking for "Moose::Meta::Class::Trait::$trait_name" (when called in a "class context"), not just passing back a shorter name.