Как объявить 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});
}
Есть ли подводные камни, которые я заметил?