Упаковка веб-сервисов с помощью Moo
Недавно я взял на себя обслуживание клиента Perl, который упаковывает веб-службу доставки. В исходном состоянии проекта используются объекты Moo, которые отображаются непосредственно на объект веб-службы, например, есть классы Parcel, Address и Label.
В v2 API вы должны отправить все данные, которые представляют один из этих объектов, в веб-службу, после чего вы получите уникальный идентификатор для этого объекта, который должен использоваться для всех последующих транзакций.
Например, если я написал:
{ name => 'Hunter',
street => '121 Baker St',
city => 'New York',
state => 'NY',
}
Я бы вернул те же данные, но с включенным идентификатором:
{ id => 'adr_xq1411',
name => 'Hunter',
street => '121 Baker St',
city => 'New York',
state => 'NY',
}
У меня проблемы с решением, как создать эти объекты. В настоящее время у меня есть этот конструктор, который выполняет фактическую POSTing для получения идентификатора, а затем изменяет текущий объект:
sub BUILD {
my $self = shift;
my $requestor = Net::Easypost::Request->new;
my $resp = $requestor->post(
'/addresses',
$self->serialize( [qw(street1 street2 city state zip)] )
);
# save the id for this Address from Easypost
$self->id( $resp->{id} );
return $self;
}
Это распространенный подход при переносе веб-сервисов в Perl? Кажется, что идеальным методом было бы отправить POST в веб-сервис и создать все атрибуты объекта Address одновременно, но в Moo(se), когда вы находитесь в методе BUILD, объект уже создан.
Я не настолько знаком с идиомами для упаковки веб-сервисов, есть ли более простой способ, чем этот?
Любые советы / комментарии / предложения будут оценены.
1 ответ
Это, безусловно, один из способов сделать это. Это может быть хорошей идеей, чтобы выделить BUILD
метод в роль. Что-то вроде:
package MyApp::PostOnBuild;
use Moo::Role;
has id => (is => 'rwp');
has endpoint => (is => 'ro', default => sub { '/addresses' });
has requestor => (is => 'ro', default => sub { Net::Easypost::Request->new });
has field_names => (is => 'ro', builder => 1);
requires '_build_field_names';
requires 'serialize'; # or maybe just implement serialize within this role!
sub BUILD { }
after BUILD => sub {
my $self = shift;
my $resp = $self->requestor->post($self->endpoint, $self->serialize($self->field_names));
$self->_set_id( $resp->{id} );
};
Теперь вам не нужно определять свои классы BUILD
метод. Все, что им нужно сделать, это:
package MyApp::Address;
use Moo;
with 'MyApp::PostOnBuild';
my @fields = qw/ street1 street2 city state zip /;
has $_ => (is => 'ro') for @fields;
sub _build_field_names { \@fields }
sub serialize { ... } # would this method be better defined in MyApp::PostOnBuild??
Обратите внимание, что requestor
теперь является атрибутом, поэтому, когда вы тестируете класс, вы можете сделать что-то вроде:
my $adr = MyApp::Address->new(
street1 => '123 Example Lane',
city => 'Sydney',
state => 'NSW',
zip => '2035',
requestor => Test::Requestor->new,
);