Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Moose Trait to Add a Method to an Attribute

by choroba (Cardinal)
on Oct 07, 2016 at 20:36 UTC ( [id://1173508]=perlquestion: print w/replies, xml ) Need Help??

choroba has asked for the wisdom of the Perl Monks concerning the following question:

Dear brethren and sistren!

Imagine a Moose class with an array attribute.

package My; use Moose; has list => ( is => 'rw', isa => 'ArrayRef[Str]', );

If we need to change all the elements of the attribute to uppercase, the simplest solution would be to use map directly:

my $obj = 'My'->new(list => [qw[ a b c ]]); $obj->list([ map uc, @{ $obj->list } ]);

Now, suppose our attribute is more complex. We'll implement it with the Array trait and its map method:

package My; use Moose; has list => ( is => 'rw', isa => 'ArrayRef[Str]', traits => [qw[ Array ]], handles => { add_to_list => 'push', list_elements => 'elements', map_list => 'map' }, );

Which changes the code to

$obj->list([ $obj->map_list(sub { uc }) ]);

We can still imagine a simpler interface: the apply_to_list method that takes a code reference and applies it to each element of the list.

I was able to implement it as another trait:

#!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package Array::Apply; use Moose::Role; before install_accessors => sub { my $class = shift; my $name = $class->name; my $method = "apply_to_$name"; $class->associated_class->add_method( $method => sub { my ($self, $code) = @_; $_ = $code->($_) for @{ $self->$name }; } ); }; } { package My; use Moose; has list => ( is => 'rw', isa => 'ArrayRef[Str]', default => sub { [] }, traits => [qw[ Array Array::Apply ]], handles => { add_to_list => 'push', list_elements => 'elements', }, ); __PACKAGE__->meta->make_immutable; } my $obj = My->new; $obj->add_to_list(qw( a b c )); $obj->apply_to_list(sub { uc }); say for $obj->list_elements;

But that's not exactly what we imagined. Is it possible to create a trait (or modify the Array trait) in a way that it adds a new apply method to the attribute which we can use in delegation, i.e. something like

handles => { add_to_list => 'push', list_elements => 'elements', apply_to_list => 'apply', },

I've been trying for several hours, but I just don't know how to do it. Do you?

($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Replies are listed 'Best First'.
Re: Moose Trait to Add a Method to an Attribute ( Moose/Meta/Method/Accessor/Native/Array/apply.pm )
by beech (Parson) on Oct 07, 2016 at 23:25 UTC

    We can still imagine a simpler interface: the apply_to_list method that takes a code reference and applies it to each element of the list.

    But that's not exactly what we imagined. Is it possible to create a trait (or modify the Array trait) in a way that it adds a new apply method to the attribute which we can use in delegation, i.e. something like

    Maybe just copy what moose itself does?

    untested, with minimum modification of https://metacpan.org/source/ETHER/Moose-2.1806/lib/Moose/Meta/Method/Accessor/Native/Array/sort_in_place.pm

    update: tested, seems to work

    Looks like Moose makes copies of arrays with potential_value ... very confusing/disgusting

      Thanks a lot! It's definitely a step in the right direction. But it modifies the Array trait globally, instead of creating a new one. _potential_value is not only confusing/disgusting, but also private and undocumented :-(

      I inspected some other methods in the same directory (e.g. map), which uses _return_value instead, but sort_in_place is much better because it shows how to modify the elements of the list. Thank again for pointing me to it. However, it still doesn't really modify the values in place: it returns a reference to a new anonymous array containing the new values.

      Update: I had to quotequote InvalidArgumentToMethod in the code, otherwise I got

      > Could not create writer for list because Failed to compile source: Bareword "InvalidArgumentToMethod" not allowed while "strict subs" in use at native delegation method My::apply_to_list (apply) of attribute list

      $self->_inline_throw_exception( '"InvalidArgumentToMethod"' => 'argument => $_[0],'. 'method_name => "apply",'. 'type_of_argument => "code reference",' +. 'type => "CodeRef",',

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1173508]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (7)
As of 2024-04-16 07:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found