How to declare 2 dependant attributes in Mooseish way?

moo, moose, perl

Solution

Once you end up with two attributes that are basically linked to the point where you always set them simultaneously ... the answer is usually to create a value object with two attributes for the purpose and then delegate the relevant methods to it. So, something like -

package MyApp::TokenInfo;

use Moo;

has token => (is => 'ro', required => 1);
has token_start => (is => 'ro', required => 1);

...

package MyApp::ThingWithAToken;

use Module::Runtime qw(use_module);
use Moo;

...

has token_info => (is => 'lazy', handles => [ qw(token token_start) ]);

sub _build_token_info {
  my ($self) = @_;
  my ($token, $token_start) = $self->_get_authorized_token;

  # this is equivalent to:
  #
  #   require MyApp::TokenInfo;
  #   return MyApp::TokenInfo->new(...);
  #
  # but more concise

  return use_module('MyApp::TokenInfo')->new(
    token => $token,
    token_start => $token_start
  );
}

...

my $thing = MyApp::ThingWithAToken->new(...);

$thing->token; # calls $thing->token_info->token;
$thing->token_start; # calls $thing->token_info->token_start

so the presence of the value object isn't required knowlede from externally but internally you've still got the two attributes tied together in a way that lets your implementation handle them as a single "thing".

-- mst

Problem

In my object constructor i had statement to initialize two attributes same time: ``` ($self->{token}, $self->{token_start}) = $self->_get_authorized_token(); ``` So i got token and it's starting time together in one statement. Now i try to port my module to use Moo(se) and here i don't know how should i set those two bound attributes at same time?. Some pseudo code would be like that: ``` has qw/token token_start/ => ( is => 'rw', default => shift->_get_authorized_token(); ); ``` But how to declare 2 bound attributes in Moo(se)ish way? EDIT. I show the code of the method `_get_authorized_token`, maybe it will brings some ideas: ``` sub _get_authorized_token { my $self = shift; my $postData = { 'apikey' => $self->{key} }; my $url = $self->{base_url} . '/seller'; my $xml = $self->_post(url => $url, postdata => $postData, ); my $ref = XMLin($xml, SuppressEmpty => '' ); my $time = $ref->{Notification_Datetime}; my $token = $ref->{Notification_Data}{body}{token}; return ($token, $time); } ```

Original source