API-Docker
view release on metacpan or search on metacpan
lib/API/Docker/Role/Using.pm view on Meta::CPAN
. '; where the pairs are computed, test for them at the call'
unless @args;
my %opts = @args;
for my $name (sort keys %opts) {
croak __PACKAGE__ . '->using does not carry \'' . $name . '\'. It carries '
. join(' and ', sort keys %CARRIED) . '; everything else a request '
. 'needs is an argument of the method that builds it'
unless $CARRIED{$name};
}
# Built from the client and the options rather than copied wholesale: the
# resource classes hold exactly one piece of state, the client, and passing
# it through the constructor is what re-weakens the reference in the clone.
return ref($self)->new(
client => $self->client,
_request_options => { %{ $self->_request_options }, %opts },
);
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
API::Docker::Role::Using - A resource class clone that bounds a run of calls
=head1 VERSION
version 0.004
=head1 SYNOPSIS
my $docker = API::Docker->new(read_timeout => 30);
# the rule, for this client
$docker->containers->list;
# the exception, for these calls
my $quick = $docker->containers->using(read_timeout => 5);
$quick->list;
$quick->inspect($id);
# or in passing
$docker->images->using(connect_timeout => 2, read_timeout => 60)
->pull(fromImage => 'alpine');
=head1 DESCRIPTION
L<API::Docker::Role::HTTP/read_timeout> and
L<API::Docker::Role::HTTP/connect_timeout> are attributes of the client, so
they are set once and hold for every request it makes. That is the right
level for a rule and the wrong one for an exception: a client bounded at 30
seconds is no help to the one call that must give up after 2, and a client
bounded at 2 cannot pull an image.
C<using> is the exception. It returns a B<clone of the resource class>
carrying the options, and every request made through that clone is given
them:
$docker->containers->using(read_timeout => 5)->list;
Only the two transport bounds may be carried. Everything else a request needs
-- query parameters, the body, a streaming callback -- is an argument of the
method that builds it, and a value carried past that method could only either
overwrite what it built or be overwritten by it.
=head2 The clone and the client
The clone holds the B<same client>, on the same terms: the resource classes
hold it as a C<weak_ref>, and the clone does too. So a clone never keeps a
client alive that the caller has let go, and never becomes the reason a client
outlives its scope.
The other side of that is the footgun this distribution already has:
my $quick = API::Docker->new->containers->using(read_timeout => 5);
$quick->list; # dies: the client was gone at the end of the first line
Keep the client in a variable. That is not new here: the entity classes hold
it weakly for the same reason, so C<< API::Docker->new->images->list >> has
always handed back images whose C<client> was already gone.
Nothing else is shared: the options live on the clone, the original resource
class is not touched, and two clones of one resource class know nothing of
each other.
my $r = $docker->containers;
my $a = $r->using(read_timeout => 5);
my $b = $r->using(read_timeout => 60);
# $r is still unbounded, $a is 5, $b is 60
=head2 Chaining merges, key by key
$docker->images->using(connect_timeout => 2)->using(read_timeout => 60)
carries both, and a repeated key takes the later value:
->using(read_timeout => 60)->using(read_timeout => 0) # 0 wins
Merging rather than replacing, because the two bounds are independent: a
helper that hands out a resource class with a connect bound already on it, and
a caller that then tightens the read bound, are both saying what they mean --
and a C<using> that dropped the other half would do it silently, which is the
one outcome neither of them could have wanted.
=head2 What it refuses
Both are croaks rather than a shrug, because the failure they replace is
invisible: an option this role kept and no request read would leave the caller
believing a bound is in force that is not.
=over
=item * B<An unknown option.> C<< ->using(read_timout => 5) >> is a typo, and
carrying it would bound nothing while looking exactly like a call that does.
=item * B<No options at all.> C<< ->using() >> asks for a clone that differs
from the original in nothing. Where the pairs are computed rather than
written, decide it at the call:
my $r = %bounds ? $docker->containers->using(%bounds) : $docker->containers;
=back
An odd number of arguments croaks too, before the pairs are read.
=head2 What has no clone of its own
B<The entity classes.> C<< $container->logs >> and its neighbours are
one-line delegations to the resource class, and they hold the container's own
daemon fields -- every one of them, verbatim -- rather than a call surface, so
a clone would have to copy a record whose shape is the daemon's. The bound
belongs where the request is built:
$docker->containers->using(read_timeout => 5)->logs($container->id);
B<The client.> C<< $docker->using(...) >> would be a second client sharing
one connection state and one negotiated API version with the first. The
client already takes both bounds as constructor arguments, which is the level
it works at.
=head2 using
my $bounded = $docker->containers->using(read_timeout => 5);
Returns a clone of the resource class that hands C<read_timeout> and
C<connect_timeout> to every request made through it. Takes those two options
and no others; an unknown one, an odd number of arguments, and an empty call
all croak.
What it carries is held in C<_request_options>, which is private and composed
into the resource classes alongside it: C<{}> on a resource class nobody
called C<using> on, and spliced by each resource method into the option list
it hands the transport.
=head1 SEE ALSO
=over
=item * L<API::Docker/TIMEOUTS> - what the two bounds cover, and what they do
not
=item * L<API::Docker::Role::HTTP/"Bounding a request that never ends"> -
C<read_timeout>, per transport
=item * L<API::Docker::Role::HTTP/"Bounding the connection itself"> -
C<connect_timeout>, per transport
=back
=head1 SUPPORT
=head2 Issues
Please report bugs and feature requests on GitHub at
L<https://github.com/Getty/p5-api-docker/issues>.
=head1 CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
=head1 AUTHOR
Torsten Raudssus <getty@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> L<https://raudssus.de/>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
( run in 2.226 seconds using v1.01-cache-2.11-cpan-d01c6094234 )