API-Docker

 view release on metacpan or  search on metacpan

lib/API/Docker/Role/Type.pm  view on Meta::CPAN

package API::Docker::Role::Type;
# ABSTRACT: Instance behaviour of a generated API::Docker::Type class
our $VERSION = '0.004';
use Moo::Role;
use Carp qw( croak );
use JSON::MaybeXS ();
use namespace::clean;


# The merged views are rebuilt whenever API::Docker::Type registers a new
# attribute (it calls _invalidate_docker_cache), so they can be cached. Keyed
# by class name, one entry each.
my %ATTR_CACHE;    # class -> { perl_name => info }
my %ORDER_CACHE;   # class -> [ perl_name, ... ]
my %WIRE_CACHE;    # class -> { wire_name => perl_name }
my %ENTITY_CACHE;  # class -> { perl_name => 1 }  (not daemon fields)


has unknown_fields => (
  is      => 'ro',
  default => sub { {} },
);


has rejected_fields => (
  is      => 'ro',
  default => sub { {} },
);

# True while `from_data` is inflating a decoded engine response, so that the
# hashref coercion in API::Docker::Type reads a nested literal the same way
# the outermost one was read. Dynamically scoped: `local`ised around the
# whole construction, which is what a nested from_data sees.
our $RESPONSE = 0;


# Constructor-side name resolution for `new`, and one of the two places
# unknown fields are collected. A key is taken as a Perl attribute name
# first and as a wire name second, because `new` assembles a REQUEST out of
# what a caller wrote and both spellings are the caller's to choose. The
# response path does not come through here -- see L</from_data>.
#
# Two keys resolving to one attribute is refused rather than decided by hash
# order, and refused whether or not the two values agree: values that happen
# to match are not the same thing as an unambiguous call, and the caller
# should see the mistake instead of the luck (karr k85).
#
# Idempotent on purpose: where one generated class extends another (the
# `allOf` shape, see API::Docker::Type) the role is composed into both, so
# this modifier runs twice on the same arguments. The second pass sees every
# key already resolved and merges an empty set into unknown_fields.
around BUILDARGS => sub {
  my ($orig, $class, @args) = @_;
  my $args = $class->$orig(@args);
  my $known = $class->_docker_attr_registry;
  my $wire  = $class->_docker_wire_index;
  my $mine  = $class->_entity_attribute_index;
  my %unknown  = %{ delete($args->{unknown_fields})  || {} };
  my %rejected = %{ delete($args->{rejected_fields}) || {} };
  my (%out, %from);
  for my $key (keys %$args) {
    my $attr = $known->{$key}           ? $key
             : defined $wire->{$key}    ? $wire->{$key}
             : $mine->{$key}            ? $key
             :                            undef;
    unless (defined $attr) { $unknown{$key} = $args->{$key}; next }
    croak __PACKAGE__ . ": $class got '"
      . join("' and '", sort($from{$attr}, $key))
      . "' for the same field '$attr'; pass one spelling, not both"
      if exists $out{$attr};
    $out{$attr}  = $args->{$key};
    $from{$attr} = $key;
  }
  $out{unknown_fields}  = \%unknown;
  $out{rejected_fields} = \%rejected;
  return \%out;
};


sub from_data {
  my ($class, $data, %extra) = @_;
  $class = ref($class) if ref($class);
  croak __PACKAGE__ . '->from_data needs a HashRef'
    unless ref $data eq 'HASH';
  my $reg  = $class->_docker_attr_registry;
  my $wire = $class->_docker_wire_index;
  # Lifted out of the loop rather than handled in it, so that what the loop



( run in 1.569 second using v1.01-cache-2.11-cpan-6736b670a1e )