Moops lexical_has and default values - perl

I am trying to understand how lexical_has attributes work in Moops. This feature comes from Lexical::Accessor and, as I understand it, the lexical_has function is able to generate a CODE reference to any attribute a class might "lexically have" by using a scalar reference (which is kept in accessor =>). The CODE reference can then be used to access the class attribute in a way that "enforces" scope (because they are "inside out"??). But this is just my surmise and wild guesses so I would appreciate a better explanation. I also want to know why this approach doesn't seem to work in the following example:
Working from the example that is part of the Moops introduction I'm creating a class Car:
use Moops;
class Car {
lexical_has max_speed => (
is => 'rw',
isa => Int,
default => 90,
accessor => \(my $max_speed),
lazy => 1,
);
has fuel => (
is => 'rw',
isa => Int,
);
has speed => (
is => 'rw',
isa => Int,
trigger => method ($new, $old?) {
confess "Cannot travel at a speed of $new; too fast"
if $new > $self->$max_speed;
},
);
method get_top_speed() {
return $self->$max_speed;
}
}
Then I instantiate the object and try to use its methods to access its attributes:
my $solarcharged = Car->new ;
# This correctly won't compile due to $max_speed scoping:
# say $solarcharged->$max_speed;
# This shows expected error "too fast"
$solarcharged->speed(140);
# This prints nothing - wrong behavior?
say $solarcharged->get_top_speed();
The last line which uses the custom accessor baffles me: nothing happens. Am I missing an attribute or setting for the class (marking it eager or lazy => 0 doesn't work)? Do I need a BUILD function? Is there an initialization step I'm missing?
N.B. If I add a setter method to the class that looks like this:
method set_top_speed (Int $num) {
$self->$max_speed($num);
}
and then call it in my final series of statements:
# shows expected error "too fast"
$solarcharged->speed(140);
$solarcharged->set_top_speed(100);
# prints 100
say $solarcharged->get_top_speed();
the get_top_speed() method starts to return properly. Is this expected? If so, how does the default from the class settings work?
I've reported this as a bug here: https://rt.cpan.org/Public/Bug/Display.html?id=101024.
Since One can easily work around this by using "perl convention" (i.e. not using lexical_has and prefixing private attributes with "_") and this question arose from a bug, I don't expect a fix or a patch as an answer. For the bounty - I would appreciate an explanation of how Lexical::Accessor is supposed to work; how it "enforces" private internal scope on accessors; and maybe some CS theory on why that is a good thing.

According to the ticket filed by the OP, this bug was fixed in Lexical-Accessor 0.009.

Related

Moose creating accessors

given a list of accessors is the following possible? If it is possible how would i create builder method for each, i assumed lazy_build attribute would be doing that? please help
my #accessors= qw/type duration process/; # used 3 as example but the list is about 50
foreach my $accessors (#accessors) {
has $accessors => (
is => 'rw',
isa => 'Str',
lazy_build => 1,
);
}
Yes, it's possible. It is both documented to work and trivial to test.
As documented, lazy_build does not create builders; it specifies that an attribute should be lazily initialized and that it should call a builder named _build_${attr_name}. You have to supply your own builder methods called _build_type etc.
If your attributes all take the same builder (unlikely, but maybe they do), don't say lazy_build. Instead, say lazy => 1, builder => '_build_stuff' and implement _build_stuff to work for each case. But like I said, that's unlikely; the fact that you can easily use it in a loop is in fact one of lazy_build's advantages.

Perl: What type should be used for class object in "validate" function

I want to pass the reference of class object called "A" in constructor. And use "validate" function to check it.
like that:
test1.pm
my $object = Object1->new;
my $newObject = Object2->new({
param1 => $object,
});
test2.pm
sub new {
my $class = shift;
my (%options) = validate (#_, {
param1 => { type => SCALARREF, default => undef},
});
...
}
The problem that I'm not sure about the type of the parameter param1. I tried "OBJECT" and "SCALARREF" but there were errors like "SCALARREF not allowed while strict sub".
What type should I use?
It looks you're trying to do a quasi-Moose thing here. But in Moose, you don't create new subs, because Moose does that for you. If you need anything--you create a BUILD sub.
The Perl (5) base object system doesn't work like Moose, so 'SCALARREF' or whatever is what you make it in base Perl.
Do you realize that you are passing a hashref to new?
Do you realize that vaildate is getting two hashrefs?
validate( {}, {} )
And if SCALARREF has not been defined, it will always be a bareword.
Read up on Moose. You can start with the Moose Types Manual, to see how ScalarRef is used, but since you don't even show "use Moose", you should start at page 1 of the Manual.

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.

Is there a simple way to test if a Moose attribute is read-only?

I currently use a block eval to test that I've set an attribute as read-only. Is there a simpler way to do this?
Example from working code:
#Test that sample_for is ready only
eval { $snp_obj->sample_for('t/sample_manifest2.txt');};
like($#, qr/read-only/xms, "'sample_for' is read-only");
UPDATE
Thanks to friedo, Ether, and Robert P for their answers and to Ether, Robert P, and jrockway for their comments.
I like how Ether's answer ensures that $is_read_only is only a true or false value (i.e. but never a coderef) by negating it with a !. Double negation also provides that. Thus, you can use $is_read_only in an is() function, without it printing out the coderef.
See Robert P's answer below for the most complete answer. Everyone has been very helpful and built on each other's answers and comments. Overall, I think he's helped me the most, hence his is now marked the accepted answer. Again, thanks to Ether, Robert P, friedo, and jrockway.
In case you might be wondering, as I did at first, here is documentation about the difference between get_attribute and find_attribute_by_name (from Class::MOP::Class):
$metaclass->get_attribute($attribute_name)
This will return a Class::MOP::Attribute for the specified $attribute_name. If the
class does not have the specified attribute, it returns undef.
NOTE that get_attribute does not search superclasses, for that you need to use
find_attribute_by_name.
Technically, an attribute does not need to have a read or a write method. Most of the time it will, but not always. An example (graciously stolen from jrockway's comment) is below:
has foo => (
isa => 'ArrayRef',
traits => ['Array'],
handles => { add_foo => 'push', get_foo => 'pop' }
)
This attribute will exist, but not have standard readers and writers.
So to test in every situation that an attribute has been defined as is => 'RO', you need to check both the write and the read method. You could do it with this subroutine:
# returns the read method if it exists, or undef otherwise.
sub attribute_is_read_only {
my ($obj, $attribute_name) = #_;
my $attribute = $obj->meta->get_attribute($attribute_name);
return unless defined $attribute;
return (! $attribute->get_write_method() && $attribute->get_read_method());
}
Alternatively, you could add a double negation before the last return to boolify the return value:
return !! (! $attribute->get_write_method() && $attribute->get_read_method());
As documented in Class::MOP::Attribute:
my $attr = $this->meta->find_attribute_by_name($attr_name);
my $is_read_only = ! $attr->get_write_method();
$attr->get_write_method() will get the writer method (either one you created or one that was generated), or undef if there isn't one.
You should be able to get this from the object's metaclass:
unless ( $snp_obj->meta->get_attribute( 'sample_for' )->get_write_method ) {
# no write method, so it's read-only
}
See Class::MOP::Attribute for more.

Can I define functions outside of a class using MooseX::Declare?

I recently started using the module MooseX::Declare. I love it for its syntax. It's elegant and neat. Has anyone come across cases where you would want to write many functions (some of them big) inside a class and the class definition running into pages? Is there any workaround to make the class definition to just have the functions declared and the real function definition outside the class?
What I am look for is something like this -
class BankAccount {
has 'balance' => ( isa => 'Num', is => 'rw', default => 0 );
# Functions Declaration.
method deposit(Num $amount);
method withdraw(Num $amount);
}
# Function Definition.
method BankAccount::deposit (Num $amount) {
$self->balance( $self->balance + $amount );
}
method BankAccount::withdraw (Num $amount) {
my $current_balance = $self->balance();
( $current_balance >= $amount )
|| confess "Account overdrawn";
$self->balance( $current_balance - $amount );
}
I can see that there is a way to make the class mutable. Does anyone know how to do it?
Easy (but needs adding to the doc).
class BankAccount is mutable {
}
As an aside, why are you defining your methods outside the class?
You can just go
class BankAccount is mutable {
method foo (Int $bar) {
# do stuff
}
}
I want my class definition to be short, and to give an abstract idea of what the class is for. I like the way its been done in C++ where you have an option to define functions inline or outside the class using the scope resolution operator. This makes the class definition short and neat. This is what I am looking for.
Thanks for your time.