1

Initially topic was started here, but I need a working code example how to properly delegate attributes with Moo or Moose.

Based on documentation I wrote this code to check:

package Cat;
use Moo;

has 'token' => ( is => 'rw', default => '12345' );
has 'tiger' => ( is => 'rw', default => sub { my $self = shift; Cat::Tiger->new(token => $self->token) }, handles => [ qw(token) ] );

package Cat::Tiger;
use Moo;
extends 'Cat';
# + some additional methods


package main;
use Data::Dumper;

my $cat = Cat->new(token=>'54321');
warn $cat->token;
warn $cat->tiger->token;

But this code produce an error:

You cannot overwrite a locally defined method (token) with a delegation at 3.pl line 5

If I remove handles => [ qw(token) ] at line 5 code will return another error:

Deep recursion on subroutine "Tiger::new" at 3.pl line 5.

So how to do?

I need to set token of Cat::Tiger object ($cat->tiger->token) same as in Cat object ($cat-token) and synс them everytime when token of Cat object changed.

Paul Serikov
  • 2,550
  • 2
  • 21
  • 36
  • 1
    I think since `Cat` has a `token` attribute, another attribute (here: `tiger`) cannot delegate an attribute that is already defined in `Cat` (here: `token`). What are you trying to achieve? ( You get "deep recursion" error because `Cat::Tiger` extends `Cat` and you construct a new `Cat::Tiger` object when constructing a `Cat`, which will create a new `Cat::Tiger`, which will create a new `Cat`, and so on – Håkon Hægland Apr 10 '17 at 17:38
  • In other words, I'd like to implement a partial `extend` : `package Cat::Tiger` must extend only `token` attribute and value of `package Cat`. It is possible to implement somehow with Moo or Moose ? – Paul Serikov Apr 10 '17 at 18:42
  • 2
    I am not sure I completely understand. So `Cat::Tiger` should not have access to `tiger` attribute of `Cat` even if it extends `Cat`? Why would you do that? It might help if you could give an example of this use case – Håkon Hægland Apr 10 '17 at 18:49
  • 1
    It should. For example, right now I'm working on improving my [API::Google](https://metacpan.org/pod/API::Google) module. `Cat` is like`API::Google` class, `Cat::Tiger` is `API::Google::Gmail` or `API::Google::Calendar`. I'd like to simplify API call to `$gapi->Calendar->CalendarList->list`, for such method chaining I need that token attribute of `API::Google::Calendar::CalendarList` instance will same as `API::Google` – Paul Serikov Apr 10 '17 at 20:35

1 Answers1

2

Well, problem solved with moving token to separate class and using MooX::Singleton for this class:

package Credentials;
use Moo;
with 'MooX::Singleton';
has 'token' => ( is => 'rw', default => '12345' );

package Cat;
use Moo;
has 'credentials' => ( is => 'rw', default => sub { Credentials->instance }, handles => [qw(token)] );
has 'tiger' => ( is => 'rw', default => sub { Cat::Tiger->new(token => shift->token) } );

package Cat::Tiger;
use Moo;
has 'credentials' => ( is => 'rw', default => sub { Credentials->instance }, handles => [qw(token)] );


package main;
use Data::Dumper;
my $cat = Cat->new;
warn $cat->token;
warn $cat->tiger->token;
$cat->token('54321');
warn $cat->token;
warn $cat->tiger->token; # will be also 54321

If someone knows better solution you are welcome to suggest it :)

Paul Serikov
  • 2,550
  • 2
  • 21
  • 36