How to implement a class constant that is different for each subclass? - moose

In my class hierarchy, I need a common attribute where each subclass needs to provide a different value that is constant for all objects of that class. (This attribute serves as a key to an existing hierarchy that I'm mirroring -- not the best OO design, but I need to preserve this link.)
One way to implement this is with attributes, like this:
package TypeBase;
use Moose::Role;
has type => (
is => 'ro',
isa => enum([qw(A B)]),
builder => '_type',
init_arg => nil,
required => 1,
);
1;
#####
package TypeA;
use Moose;
with 'TypeBase';
sub _type { 'A' };
1;
#####
package TypeB;
use Moose;
with 'TypeBase';
sub _type { 'B' };
1;
Is there a better way to do this? I could just have requires 'type' in the base class, which each concrete class would have to provide, except that this loses me the type constraint that I had with the attribute route.

My solution is equivalent to yours in terms of the external interface. The main difference is the use of constant to better reflect what you are doing. (Note that TYPE can still be called like a method.) Because if is using requires it should also give you a compile-time rather than runtime error if you haven't implemented TYPE in one of your classes.
package TypeBase;
use Moose::Role;
requires 'TYPE';
package TypeA;
use Moose;
with 'TypeBase';
use constant TYPE => 'A';
package TypeB;
use Moose;
with 'TypeBase';
use constant TYPE => 'B';

Related

How can I set a required attribute of my base class using Class::Std?

I am working with multiple different versions of Perl (all 5.8+) across different operating systems. I am unable to manage modules on various machines which has led me to use a combination of Class::Std and the base pragma to facilitate OO Perl and inheritance.
I want to set a required attribute of my base class when constructing the subclass object. Additionally, I want to do so without explicitly specifying the value within the arguments sent to my subclass since it will be the same for all objects of the given subclass.
The following code (followed by a question) illustrates this scenario:
My base class:
package Person;
use Class::Std;
# class data
my %person_title :ATTR( name => "title" );
my %person_name :ATTR( name => "name" );
1;
My subclass:
package Doctor;
use Class::Std;
# set inheritance
use base 'Person';
# class data
my %doctor_specialty :ATTR( name => "specialty" );
1;
My script (this currently fails with "Missing initializer label for Person: 'title'"):
use strict;
use warnings;
use Doctor;
my %args = (
"name" => "John Smith",
"specialty" => "Dentist",
);
Doctor->new(\%args);
When I create a Doctor object, I always want the 'title' to be 'Dr.'. How can this be accomplished?

Is there a way to override the constructor in Moo?

I am working on a MooX module that needs to add a wrapper around the constructor.
I've tried method modifies or having the import method directly change *{"${target}::new"} with no effect.
So how can I do this?
Apparently, around does work:
package MyRole;
use Moo::Role
around new => sub { ... };
but the role that has the around needs to be consumed after attributes are added, e.g.
package MyClass;
use Moo;
has attr1 => (... );
with 'MyRole';

Perl Moose Hash traits

I have a parameter object in Moose which has attributes of file wildcards to glob
So I had a method to do this
sub getInputFileParams{
my ($self) = #_;
#the only parameters passed in are in fact the input files
return keys(%{$self->{extraParams}});
}
but then I though why not iterate the attributes as a hash?
has 'extraParams' => (
is => 'ro',
isa => 'JobParameters::Base',
default => sub { {} },
traits => ['Hash'],
handles => {
keys_extraParams => 'keys',
},
);
However that chokes as its not a hash reference. have I missed something or is using the object as a hash bad
Yes, using objects as plain hashes is bad.
You're accessing their internal state directly, which bypasses any interface that they may present and makes your class closely coupled to the internal representation of the JobParameters::Base class.
If you need to be able to get the contents of a JobParameters::Base object as a hash, then add a to_hash method to JobParameters::Base, and delegate to that method in your attribute...
This means that if later you add caching (for example!) to JobParameters::Base, and use a __cache key to store internal data, you can safely make this change by also changing the to_hash method to remove the internal data from the hash it returns.
It is fine to store an attribute as just a hash, but if you're storing a blessed hash, then don't reach into it's guts..
You've got all tools in place in your Moose class definition, you just aren't using them - try this:
return $self->keys_extraParams

Modifing inherited accessors and retaining around modifiers

I'm trying to inherit and extend a base class with a more specific child class that removes the required attribute from an accessor and specifies a lazily built default. However, when doing so, the derived class no longer wraps the around subroutine around calls to the accessor.
What am I doing wrong in my definition?
Edit: I should state that I can simply inherit the accessor without modifying it and the around modifier still works, and I'm aware I can do something like set the accessor to have a getter, then define a getter method with the name of the accessor (i.e. sub attr { my $self = shift; my $value = $self->_get_attr; return "The value of attr is '$value'"; }). I'm simply surprised the around modifier gets dumped so easily.
use strict;
use warnings;
use 5.010;
package My::Base;
use Moose;
has 'attr' => (is => 'ro', isa => 'Str', required => 1);
around 'attr' => sub {
my $orig = shift;
my $self = shift;
my $response = $self->$orig(#_);
return "The value of attr is '$response'"
};
package My::Derived;
use Moose;
extends 'My::Base';
has '+attr' => (required => 0, lazy_build => 1);
sub _build_attr {
return "default value";
}
package main;
my $base = My::Base->new(attr => 'constructor value');
say $base->attr; # "The value of attr is 'constructor value'"
my $derived = My::Derived->new();
say $derived->attr; # "default value"
Per a response from stvn for the same question on perlmonks, the issue is:
Actually, it is not removing the 'around' modifier, you are simply
creating a new accessor in your derived class, which itself is not
around-ed. Allow me to explain ...
When you create an attribute, Moose compiles the accessor methods for
you and installs them in the package in which they are defined. These
accessor methods are nothing magical (in fact, nothing in Moose is
very magical, complex yes, but magical no), and so they are inherited
by subclasses just as any other method would be.
When you "around" a method (as you are doing here) Moose will extract
the sub from the package, wrap it and replace the original with the
wrapped version. This all happens in the local package only, the
method modifiers do not know (or care) anything about inheritance.
When you change an attributes definition using the +attr form, Moose
looks up the attribute meta-object in the superclass list and then
clones that attribute meta-object, applying the changes you requested
and then installs that attributes into the local class. The result is
that all accessor methods are re-compiled into the local class,
therefore overriding the ones defined in the superclass.
It doesn't go the other way around, where the accessor is built from the bottommost class in the ISA, then the around modifiers up the ISA stack are applied in turn.

What is the most efficient way to override an attribute in lots of my Moose based sub classes?

I am using HTML::FormHandler. To use it one is supposed to subclass from it and then you can override some attributes such as field_name_space or attribute_name_space.
However, I now have lots of forms all extending HTML::FormHandler or its DBIC based variant HTML::FormHandler::Model::DBIC and therefore have these overidden attributes repeated many times.
I tried to put them in a role but get an error that +attr notation is not supported in Roles. Fair enough.
What is the best way of eliminating this repetition? I thought perhaps subclassing but then I would have to do it twice for HTML::FormHandler and HTML::FormHandler::Model::DBIC, plus I believe general thought was that subclassing is generally better achieved with Roles instead.
Update: I thought it would be a good idea to give an example. This is what I am currently doing - and it involves code repetition. As you can see one form uses a different parent class so I cannot create one parent class to put the attribute overrides in. I would have to create two - and that also adds redundancy.
package MyApp::Form::Foo;
# this form does not interface with DBIC
extends 'HTML::Formhandler';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
...
package MyApp::Form::Bar;
# this form uses a DBIC object
extends 'HTML::Formhandler::Model::DBIC';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
...
package MyApp::Form::Baz;
# this form also uses a DBIC object
extends 'HTML::Formhandler::Model::DBIC';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
...
First of all, roles are composed into a class, they have nothing to do with subclassing. A subclass is a full class that extends a parent (or more than one, but in my experience multiple inheritance should be avoided if it can be). A role is a piece of behaviour, or a parial interface that can be applied to a class. The role then directly modifies the class. There's no new class created in general.
So inheritance and role composition are really two different things and two different kinds of design. Thus you can't simply exchange one for the other. Both have different design-implications.
My strategy with HTML::FormHandler has been to make a real subclass for each form that I require, and put the different behaviours of the form that I wanted to re-use into roles.
I'd think this question (how to implement the extensions you need in a clean and sane way) can't really be answered without knowing the actual design you're aiming for.
Update: I see what you mean and that's a tricky case. HTML::FormHandler is primarily targetted at extension by inheritance. So I think the best strategy would indeed be to have two subclasses, one for HTML::FormHandler and one for HTML::FormHandler::Model::DBIC. It seems teadious at first, but you might want to have different settings for them in the long run anyway. To avoid repeating the actual configuration (the default values) I'd try the following (this example is plain HFH, without DBIC):
package MyApp::Form;
use Moose;
use namespace::autoclean;
extends 'HTML::FormHandler';
with 'MyApp::Form::DefaultSettings';
# only using two fields as example
for my $field (qw( html_prefix field_traits )) {
has "+$field", default => sub {
my $self = shift;
my $init = "_get_default_$field";
my $method = $self->can($init)
or die sprintf q{Class %s does not implement %s method}, ref($self), $init;
return $self->$method;
};
}
1;
Note that you'd need to make an attribute lazy if it requires the values of another attribute for its computation. The above base class would look for hooks to find the initialized values. You'd have to do this in both classes, but you could put the default subroutine generation into a function you import from a library. Since the above doesn't require direct manipulation of the attribute anymore to change the default values, you can put that stuff in a role I called MyApp::Form::DefaultSettings above:
package MyApp::Form::DefaultSettings;
use Moose::Role;
use namespace::autoclean;
sub _build_html_prefix { 1 }
sub _build_field_traits { ['MyApp::Form::Trait::Field'] }
1;
This method will allow your roles to influence the default value construction. For example, you could have a role based on the one above that modifies the value with around.
There is also a very simple, but in my opinion kind-of ugly way: You could have a role provide a BUILD method that changes the values. This seems pretty straight-forward and easy at first, but it's trading extendability/flexibility with simplicity. It works simple, but also only works for very simple cases. Since the amount of forms in web applications is usually rather high, and the needs can be quite diverse, I'd recommend going with the more flexible solution.
The code for HTML::FormHandler::Model::DBIC is actually in a Moose trait in order to help with this situation. You can inherit from your base class, and in your forms that use the DBIC model, you can do
with 'HTML::FormHandler::TraitFor::Model::DBIC';
Would this method, using multiple inheritance (I know I know, ugh), where you put your common default overrides in one class, and then your customized code in others?
package MyApp::Form;
use Moose;
extends 'HTML::Formhandler';
has '+html_prefix' => (default => 1);
has '+field_traits' => (default => sub { ['MyApp::Form::Trait::Field'] });
has '+field_name_space' => (default => 'MyApp::Form::Field');
has '+widget_name_space' => (default => sub { ['MyApp::Form::Widget'] });
has '+widget_wrapper' => (default => 'None');
package MyApp::Form::Model::DBIC;
use Moose;
extends 'MyApp::Form', 'HTML::Formhandler::Model::DBIC';
# ... your DBIC-specific code
Now you can descend from MyApp::Form or MyApp::Form::Model::DBIC as needed.