Как объявить 2 зависимых атрибута в Mooseish?

В моем конструкторе объектов у меня было выражение для инициализации двух атрибутов одновременно:

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

Итак, я получил маркер, и сейчас время вместе в одном утверждении.

Теперь я пытаюсь портировать свой модуль для использования Moo(se), и здесь я не знаю, как мне установить эти два связанных атрибута одновременно?. Некоторый псевдокод будет выглядеть так:

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

Но как объявить 2 связанных атрибута в стиле Moo(se)?


РЕДАКТИРОВАТЬ. Я показываю код метода _get_authorized_tokenМожет быть, это принесет некоторые идеи:

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 ответа

Решение

Как только вы получите два атрибута, которые в основном связаны с точкой, в которой вы всегда устанавливаете их одновременно... ответ обычно заключается в создании объекта значения с двумя атрибутами для этой цели, а затем делегировании соответствующих методов ему. Итак, что-то вроде -

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

поэтому наличие объекта значения не требуется знать извне, но внутренне у вас все еще есть два атрибута, связанных между собой таким образом, что позволяет вашей реализации обрабатывать их как одну "вещь".

- мст

Я не знаю ни одного способа связать два атрибута вместе, как у вас, с прямыми хэш-присваиваниями.

Я бы, наверное, попросил двух ленивых строителей сделать что-то вроде:

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

А потом обратное построение token_start.

Я подозреваю, что вы действительно хотите сделать token / token_start частью своего собственного объекта. Таким образом, вы можете гарантировать, что оба установлены вместе соответствующим образом.


у меня все еще будет 2 зависимых атрибута вместе, я не могу их разделить там тоже. Или где смысл?

Я не уверен, что суть вопроса ясна. Мне кажется, что эти два значения принадлежат друг другу, или, по крайней мере, token_start зависит от токена. Я бы предпочел использовать $self->auth->token так что связь понятна.

Если вы хотите пропустить эту ссылку на объект "auth", просто используйте дескрипторы

Когда сталкиваюсь с чем-то такого рода - двумя или более атрибутами, значения которых генерируются одновременно - и нет веской причины создавать небольшой класс для обработки этого, я обычно создаю один атрибут, а затем делегирую средства доступа для доступа к результатам. например:

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 };
}

Таким образом, при первом обращении к token() или token_start() токен и начальное значение генерируются и передаются.

Обратите внимание, что этот подход, как правило, лучше всего работает со значениями, которые всегда либо создаются, либо устанавливаются конфиденциально внутри класса, а не тогда, когда предполагается построить класс, передающий token и token_start в new().

см. также http://whitepointstarllc.com/2012/05/simulating-multiple-lazy-attributes/

Я тоже кое-что понял. Может быть, вместо использования метода с возвращаемыми значениями, я должен использовать метод setter, который возвращает одно (если есть) значение, но устанавливает 2? Как это:

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});    
}

Есть ли подводные камни, которые я заметил?

Другие вопросы по тегам