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