V
V
Vyacheslav Golovanov2012-04-25 01:12:39
Perl
Vyacheslav Golovanov, 2012-04-25 01:12:39

Perl, Moose, attribute handling

I got lost in the rich forest of Moose possibilities.

How do I process an attribute after it has been set via attribute accessor or constructor?

Let's say there is:

package MyMod;
use Moose;

has 'page' => (
  is => 'rw',
  required => 1,
);


Needed after this attribute is set via

$mm = MyMod->new( page => 'abcd/efgh');


or

$mm->page('fghj/asdf');


somehow change it every time. Remove "/" characters from it, for example.

Answer the question

In order to leave comments, you need to log in

5 answer(s)
Z
zdm001, 2012-04-25
@zdm001

has 'page'  => (is => 'rw', isa => 'Str');
has '_page' => (is => 'rw', isa => 'Str', lazy_build => 1);

sub _build__page {
    my $self = shift;
    return $self->{page} =~ s/^.+\/(.+)$/$1/r;
}

around 'page' => sub {
    my $orig = shift;
    my $self = shift;

    if (@_) {
        $self->$orig(@_);
        $self->_clear_page;
    }
    return $self->_page;
};

Like you, it's fine too.

Z
zdm001, 2012-04-25
@zdm001

1. immediately process the attribute value set through the (new) constructor only in the BUILDARGS method (we get access to the attribute hash before creating the object) or BUILD (we process the attributes immediately after the object is created), method modifiers (before, around, trigger) do not are called when the attribute is set in the constructor;
2. you can use trigger (called when the value is set);
3. you can use around, check and change the value with each call, if necessary;

Z
zdm001, 2012-04-25
@zdm001

You cannot change the value through a trigger, it is called after the value is set. You can only understand if the value has been changed or not.
If you refer to $self->page('xxx') in the trigger, there will be an endless recursion.
You need this in the trigger: $self->{page} = 'xxx' is a direct access to the class attribute in perl, bypassing Moose method modifiers. In this case, there will be no recursion, because the trigger will not be called a second time from itself.
You can only do it through around, it will work in any case when you get the value of the attribute. Then in around you will need to do a substitution every time you get a value or create a separate meta-attribute in which to store the flag - whether the value is processed or not ...
There are no other ways, modifiers are not called from new, that's the whole point.
You can still like this :)))
Get extra. the _page attribute and store the changed value in it. Make it lazy_build => 1.
Create a _page builder:

sub _build__page {
    my $self = shift;
    return $self->{page} =~ s/\///g; #именно так, чтобы не возникало рекурсии при вызове из around ниже
}

In around to page:
if(@_){ #если устанавливаются параметры - очищаем _page, чтобы при следующем обращении вызвался его билдер
    $self->_clear_page;
}
$self->$orig(@_); #проксируем на оригинальный метод
return $self->_page;#если мы меняем значение - будет вызван билдер для _page, его результат сохранится в _page и вернется, если не меняем вернется значение _page, которое там было раньше, второй раз код подмены для одного и того-же значения выполнятся не будет

This is the most elegant option.

Z
zdm001, 2012-04-25
@zdm001

optimized around:
if(@_){
$self->_clear_page;
$self->$orig(@_); #proxy to original method only if value is set
}
return $self->_page;

Z
zdm001, 2012-04-25
@zdm001

There is an error in the builder, you need to:
return $self->{page} =~ s/\///gr;
the r modifier is required for the regexp so that the result of the replacement is returned, not the number of replacements made

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question