1package Bread::Board::Container::Parameterized; 2our $AUTHORITY = 'cpan:STEVAN'; 3$Bread::Board::Container::Parameterized::VERSION = '0.37'; 4use Moose; 5use Moose::Util 'find_meta'; 6use Bread::Board::Container::FromParameterized; 7# ABSTRACT: A parameterized container 8 9use Bread::Board::Container; 10 11with 'Bread::Board::Traversable'; 12 13has 'name' => ( 14 is => 'rw', 15 isa => 'Str', 16 required => 1 17); 18 19has 'allowed_parameter_names' => ( 20 is => 'ro', 21 isa => 'ArrayRef', 22 required => 1, 23); 24 25has 'container' => ( 26 is => 'ro', 27 isa => 'Bread::Board::Container', 28 lazy => 1, 29 builder => '_build_container', 30 handles => [qw[ 31 add_service 32 get_service 33 has_service 34 get_service_list 35 has_services 36 services 37 38 add_sub_container 39 get_sub_container 40 has_sub_container 41 get_sub_container_list 42 has_sub_containers 43 sub_containers 44 ]] 45); 46 47sub _build_container { 48 my $self = shift; 49 Bread::Board::Container->new( name => $self->name ) 50} 51 52sub fetch { die "Cannot fetch from a parameterized container"; } 53sub resolve { die "Cannot resolve from a parameterized container"; } 54 55sub create { 56 my ($self, %params) = @_; 57 58 my @allowed_names = sort @{ $self->allowed_parameter_names }; 59 my @given_names = sort keys %params; 60 61 (scalar @allowed_names == scalar @given_names) 62 || confess "You did not pass the correct number of parameters"; 63 64 ((join "" => @allowed_names) eq (join "" => @given_names)) 65 || confess "Incorrect parameter list, got: (" 66 . (join "" => @given_names) 67 . ") expected: (" 68 . (join "" => @allowed_names) 69 . ")"; 70 71 72 my $clone = $self->container->clone( 73 name => ($self->container->name eq $self->name 74 ? join "|" => $self->name, @given_names 75 : $self->container->name) 76 ); 77 78 my $from_parameterized_meta = find_meta('Bread::Board::Container::FromParameterized'); 79 $clone = $from_parameterized_meta->rebless_instance($clone); 80 81 if ($self->has_parent) { 82 my $cloned_parent = $self->parent->clone; 83 84 $cloned_parent->sub_containers({ 85 %{ $cloned_parent->sub_containers }, 86 $self->name => $clone, 87 }); 88 89 $clone->parent($cloned_parent); 90 } 91 92 foreach my $key ( @given_names ) { 93 $clone->add_sub_container( 94 $params{ $key }->clone( name => $key ) 95 ); 96 } 97 98 $clone; 99} 100 101__PACKAGE__->meta->make_immutable; 102 103no Moose; no Moose::Util; 1; 104 105__END__ 106 107=pod 108 109=encoding UTF-8 110 111=head1 NAME 112 113Bread::Board::Container::Parameterized - A parameterized container 114 115=head1 VERSION 116 117version 0.37 118 119=head1 DESCRIPTION 120 121This class implements a sort of container factory for L<Bread::Board>: 122a parameterized container is a, in practice, a function from a set of 123parameters (which must be containers) to an actual container. See 124L<Bread::Board::Manual::Example::FormSensible> for an example. 125 126=head1 ATTRIBUTES 127 128=head2 C<name> 129 130Read/write string, required. Every container needs a name, by which it 131can be referenced when L<fetching it|Bread::Board::Traversable/fetch>. 132 133=head2 C<allowed_parameter_names> 134 135Read-only arrayref of strings, required. These are the names of the 136containers that must be passed to L<< C<create>|create ( %params ) >> 137to get an actual container out of this parameterized object. 138 139=head2 C<container> 140 141This attribute holds the "prototype" container. Services inside it can 142depend on service paths that include the container names given in 143L</allowed_parameter_names>. 144 145=head1 METHODS 146 147=head2 C<add_service> 148 149=head2 C<get_service> 150 151=head2 C<has_service> 152 153=head2 C<get_service_list> 154 155=head2 C<has_services> 156 157=head2 C<add_sub_container> 158 159=head2 C<get_sub_container> 160 161=head2 C<has_sub_container> 162 163=head2 C<get_sub_container_list> 164 165=head2 C<has_sub_containers> 166 167All these methods are delegated to the "prototype" L</container>, so 168that this object can be defined as if it were a normal container. 169 170=head2 C<create> 171 172 my $container = $parameterized_container->create(%params); 173 174After checking that the keys of C<%params> are exactly the same 175strings that are present in L</allowed_parameter_names>, this method 176clones the prototype L</container>, adds the C<%params> to the clone 177as sub-containers, and returns the clone. 178 179If this was not a top-level container, the parent is also cloned, and 180the container clone is added to the parent clone. 181 182Please note that the container returned by this method does I<not> 183have the same name as the parameterized container, and that calling 184this method with different parameter values will return different 185containers, but all with the same name. It's probably a bad idea to 186instantiate a non-top-level parameterized container more than once. 187 188=head2 C<fetch> 189 190=head2 C<resolve> 191 192These two methods die, since services in a parameterized container 193won't usually resolve, and attempting to do so is almost always a 194mistake. 195 196=head1 AUTHOR 197 198Stevan Little <stevan@iinteractive.com> 199 200=head1 BUGS 201 202Please report any bugs or feature requests on the bugtracker website 203https://github.com/stevan/BreadBoard/issues 204 205When submitting a bug or request, please include a test-file or a 206patch to an existing test-file that illustrates the bug or desired 207feature. 208 209=head1 COPYRIGHT AND LICENSE 210 211This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. 212 213This is free software; you can redistribute it and/or modify it under 214the same terms as the Perl 5 programming language system itself. 215 216=cut 217