How to declare 2 dependent attributes in Mooseish?

In my object constructor, I had an instruction to initialize two attributes at the same time:

($self->{token}, $self->{token_start}) = $self->_get_authorized_token(); 

So, I received the token and its start time together in one expression.

Now I'm trying to port my module to use Moo (se), and here I do not know how I should set these two related attributes at the same time. Some pseudo code would be like this:

 has qw/token token_start/ => ( is => 'rw', default => shift->_get_authorized_token(); ); 

But how to declare 2 related attributes in Moo (se) ish way?


EDIT. I am showing the code of the _get_authorized_token method, maybe it will bring 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); } 
+4
source share
4 answers

As soon as you finish with two attributes, which are mainly related to the point where you always set them at the same time ... The answer is usually to create a value object with two attributes for this purpose, and then delegate the appropriate 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 

therefore, the presence of a value object is not required to be recognized externally, but inside you still got two attributes linked together so that your implementation would treat them as one “thing”.

- mst

+4
source

I don’t know a single way to link two attributes together, as you have with direct hash assignments.

I probably would have two lazy collectors:

 sub _build_token { my $self = shift; my ($t, $ts) = $self->_get_authorized_token(); $self->token_start($ts); return $t } 

And then the reverse is to create token_start.

What you really want, I suspect, is to make token / token_start part of your own object. In this way, you can ensure that both are installed together.


I will still have two dependent attributes, and I cannot separate them. Or where is the point?

I'm not sure the question is clear from the question. It seems that these two values ​​belong to each other, or at least token_start depends on the token. I would prefer to use $self->auth->token so that the connection is clear.

If you want to skip this reference to the "auth" object, just use handle

+1
source

I also understood something. Maybe, instead of using a method with return values, I should use the setter method, which returns one (if any) value, but sets 2? Like this:

 sub _set_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 => '' ); $self->{token_start} = $ref->{Notification_Datetime}; $self->{token} = $ref->{Notification_Data}{body}{token}; return ($self->{token}); } 

Are there any pitfalls that I noticed?

0
source

When you come across something like this - two or more attributes whose values ​​are generated immediately - and there is no good reason to create a small class to handle this issue, I usually create one attribute and then delegated access to access the Results. eg:.

 has _token_info => ( traits => ['Hash'], is => 'ro', isa => 'HashRef', builder => '_build__token_info', handles => { token => [ get => 'token' ], token_start => [ get => 'token_start' ] }, ); sub _build__token_info { # ... whatever needs to be done to get $token{,_start} return { token => $token, token_start => $token_start }; } 

Thus, for the first time when someone accesses tokens () or token_start (), tokens and the initial value are generated and transmitted together.

Note that this approach is generally best suited for values ​​that are always either created or set privately inside the class, and not when you expect to build a token to pass the class and token_start to new ().

see also http://whitepointstarllc.com/2012/05/simulating-multiple-lazy-attributes/

0
source

All Articles