File Coverage

blib/lib/Brick/Bucket.pm
Criterion Covered Total %
statement 91 127 71.6
branch 15 36 41.6
condition 11 17 64.7
subroutine 29 39 74.3
pod 12 12 100.0
total 158 231 68.4


line stmt bran cond sub pod time code
1             package Brick::Bucket;
2 5     5   1020 use strict;
  5         8  
  5         258  
3              
4 5     5   36 use base qw(Exporter);
  5         15  
  5         676  
5 5     5   38 use subs qw();
  5         7  
  5         97  
6 5     5   17 use vars qw($VERSION);
  5         18  
  5         239  
7              
8 5     5   21 use Carp;
  5         7  
  5         367  
9              
10 5     5   2108 use Brick::Constraints;
  5         13  
  5         2873  
11              
12             foreach my $package ( qw(Numbers Regexes Strings Dates General
13             Composers Filters Selectors Files) ) {
14             # print STDERR "Requiring $package\n";
15             eval "require Brick::$package";
16             print STDERR $@ if $@;
17             }
18              
19             $VERSION = '0.905';
20              
21             =encoding utf8
22              
23             =head1 NAME
24              
25             Brick::Bucket - The thing that keeps everything straight
26              
27             =head1 SYNOPSIS
28              
29             use Brick::Bucket;
30              
31             my $bucket = Brick::Bucket->new();
32              
33             =head1 DESCRIPTION
34              
35             =head2 Class methods
36              
37             =over 4
38              
39             =item new()
40              
41             Creates a new bucket to store Brick constraints
42              
43             =cut
44              
45             sub new {
46 4     4 1 9 my( $class ) = @_;
47              
48 4         10 my $self = bless {}, $class;
49              
50 4         15 $self->_init;
51              
52 4         10 $self;
53             }
54              
55             sub _init {
56 4     4   6 my $self = shift;
57              
58 4         143 $self->{_names} = {};
59 4         76 $self->{_field_labels} = {};
60             }
61              
62             =item entry_class
63              
64              
65             Although this is really a class method, it's also an object method because
66             Perl doesn't know the difference. The return value, however, isn't designed
67             to be mutable. You may want to change it in a subclass, but the entire system
68             still needs to agree on what it is. Since I don't need to change it (although
69             I don't want to hard code it either), I have a method for it. If you need
70             something else, figure out the consequences and see if this could work another
71             way.
72              
73             =cut
74              
75 14     14 1 35 sub entry_class { __PACKAGE__ . "::Entry"; }
76              
77             =back
78              
79             =head2 Object methods
80              
81             =over 4
82              
83             =item add_to_bucket( HASHREF )
84              
85             =item add_to_pool # DEPRECATED
86              
87             You can pass these entries in the HASHREF:
88              
89             code - the coderef to add to the bucket
90             name - a name for the entry, which does not have to be unique
91             description - explain what this coderef does
92             args - a reference to the arguments that the coderef closes over
93             fields - the input field names the coderef references
94             unique - this name has to be unique
95              
96             If you pass a true value for the C value, then there can't be
97             any other brick with that name already, or a later brick which tries to
98             use the same name will fail.
99              
100             The method adds these fields to the entry:
101              
102             gv - a GV reference from B::svref_2object($sub), useful for
103             finding where an anonymous coderef came from
104              
105             created_by - the name of the routine that added the entry to the bucket
106              
107             It returns the subroutine reference.
108              
109             =cut
110              
111 0     0 1 0 sub add_to_pool { croak "add_to_pool is now add_to_bucket" }
112              
113             sub add_to_bucket {
114 20     20 1 61 require B;
115 20         28 my @caller = __caller_chain_as_list();
116             # print STDERR Data::Dumper->Dump( [\@caller],[qw(caller)] );
117 20         28 my( $bucket, $setup ) = @_;
118              
119             my( $sub, $name, $description, $args, $fields, $unique )
120 20         40 = @$setup{ qw(code name description args fields unique) };
121              
122 20   50     71 $unique ||= 0;
123              
124 20 100       26 unless( defined $name ) {
125 2         3 my $default = '(anonymous)';
126             #carp "Setup does not specify a 'name' key! Using $default";
127 2   33     6 $name ||= $default;
128             }
129              
130             # ensure we have a sub first
131 20 50 66 0   124 unless( ref $sub eq ref sub {} ) {
    50          
    50          
132             #print STDERR Data::Dumper->Dump( [$setup],[qw(setup)] );
133 0         0 croak "Code ref [$sub] is not a reference! $caller[1]{sub}";
134             }
135             # and that the name doesn't exist already if it's to be unique
136 0 50       0 elsif( $unique and exists $bucket->{ _names }{ $name } ) {
137 0         0 croak "A brick named [$name] already exists";
138             }
139             # or the name isn't unique already
140 0 100       0 elsif( exists $bucket->{ _names }{ $name } and $bucket->{ _names }{ $name } ) {
141 0         0 croak "A brick named [$name] already exists";
142             }
143             # and that the code ref isn't already in there
144             elsif( exists $bucket->{ $sub } ) {
145 5     5   35 no warnings;
  5         6  
  5         3558  
146             my $old_name = $bucket->{ $sub }{name};
147             }
148              
149 20   66     55 my $entry = $bucket->{ $sub } || $bucket->entry_class->new( $setup );
150              
151 20         21 $entry->{code} = $sub;
152 20         22 $entry->{unique} = $unique;
153              
154 20         24 $entry->set_name( do {
155 20 50       26 if( defined $name ) { $name }
  20 0       33  
    0          
156 0         0 elsif( defined $entry->get_name ) { $entry->get_name }
157 0 0       0 elsif( ($name) = map { $_->{'sub'} =~ /^__|add_to_bucket/ ? () : $_->{'sub'} } @caller )
158             {
159 0         0 $name;
160             }
161             else
162             {
163 0         0 "Unknown";
164             }
165             } );
166              
167 20   100     29 $entry->set_description(
168             $entry->get_description
169             ||
170             $description
171             ||
172             "This spot left intentionally blank by a naughty programmer"
173             );
174              
175 20 100 100     47 $entry->{created_by} ||= [ map { $_->{'sub'} =~ /add_to_bucket/ ? () : $_->{'sub'} } @caller ];
  106         189  
176              
177 20         70 $entry->set_gv( B::svref_2object($sub)->GV );
178              
179 20         70 $bucket->{ $sub } = $entry;
180 20         40 $bucket->{ _names }{ $name } = $unique;
181 20         92 $sub;
182             }
183              
184             =item get_from_bucket( CODEREF )
185              
186             Gets the entry for the specified CODEREF. If the CODEREF is not in the bucket,
187             it returns false.
188              
189             The return value is an entry instance.
190              
191             =cut
192              
193             sub get_from_bucket {
194 24     24 1 26 my( $bucket, $sub ) = @_;
195              
196 24 50       53 return exists $bucket->{$sub} ? $bucket->{$sub} : ();
197             }
198              
199             =item get_brick_by_name( NAME )
200              
201             Gets the code references for the bricks with the name NAME. Since
202             bricks don't have to have a unique name, it might return more than
203             one.
204              
205             In list context return the bricks with NAMe, In scalar context
206             returns the number of bricks it found.
207              
208             =cut
209              
210             sub get_brick_by_name {
211 0     0 1 0 my( $bucket, $name ) = @_;
212              
213 0         0 my @found;
214              
215 0         0 foreach my $key ( $bucket->get_all_keys ) {
216             #print STDERR "Got key $key\n";
217 0         0 my $brick = $bucket->get_from_bucket( $key );
218             #print STDERR Data::Dumper->Dump( [$brick], [qw(brick)] );
219              
220 0 0       0 next unless $brick->get_name eq $name;
221              
222 0         0 push @found, $brick->get_coderef;
223             }
224              
225 0 0       0 wantarray ? @found : scalar @found;
226             }
227              
228             =item get_all_keys
229              
230             Returns an unordered list of the keys (entry IDs) in the bucket.
231             Although you probably know that the bucket is a hash, use this just in
232             case the data structure changes.
233              
234             =cut
235              
236 0     0 1 0 sub get_all_keys { grep { ! /^_/ } keys %{ $_[0] } }
  0         0  
  0         0  
237              
238             =item comprise( COMPOSED_CODEREF, THE_OTHER_CODEREFS )
239              
240             Tell the bucket that the COMPOSED_CODEREF is made up of THE_OTHER_CODEREFS.
241              
242             $bucket->comprise( $sub, @component_subs );
243              
244             =cut
245              
246             sub comprise {
247 8     8 1 14 my( $bucket, $compriser, @used ) = @_;
248              
249 8         12 $bucket->get_from_bucket( $compriser )->add_bit( @used );
250             }
251              
252              
253             =item dump_bucket
254              
255             Show the names and descriptions of the entries in the bucket. This is
256             mostly a debugging tool.
257              
258             =cut
259              
260             sub dump_bucket {
261 0     0 1 0 my $bucket = shift;
262              
263 0         0 foreach my $key ( $bucket->get_all_keys ) {
264 0         0 my $brick = $bucket->get_from_bucket( $key );
265              
266 0         0 print $brick->get_name, " --> $key\n";
267 0         0 print $brick->get_description, "\n";
268             }
269              
270 0         0 1;
271             }
272              
273             =back
274              
275             =head2 Field labels
276              
277             The bucket can store a dictionary that maps field names to arbitrary
278             strings. This way, a brick can translate and input parameter name
279             (e.g. a CGI input field name) into a more pleasing string for humans
280             for its error messages. By providing methods in the bucket class,
281             every brick has a chance to call them.
282              
283             =over 4
284              
285             =item use_field_labels( HASHREF )
286              
287             Set the hash that C uses to map field names to
288             field labels.
289              
290             This method croaks if its argument isn't a hash reference.
291              
292             =cut
293              
294             sub use_field_labels {
295 1 50   1 1 6 croak "Not a hash reference!" unless UNIVERSAL::isa( $_[1], ref {} );
296 1         2 $_[0]->{_field_labels} = { %{$_[1]} };
  1         7  
297             }
298              
299             =item get_field_label( FIELD )
300              
301             Retrieve the label for FIELD.
302              
303             =cut
304              
305             sub get_field_label {
306 5     5   37 no warnings 'uninitialized';
  5         22  
  5         1163  
307 12     12 1 2694 $_[0]->{_field_labels}{ $_[1] };
308             }
309              
310             =item set_field_label( FIELD, VALUE )
311              
312             Set the label for FIELD to VALUE. It returns VALUE.
313              
314             =cut
315              
316             sub set_field_label {
317 4     4 1 1628 $_[0]->{_field_labels}{ $_[1] } = $_[2];
318             }
319              
320             sub __caller_chain_as_list {
321 38     38   33 my $level = 0;
322 38         37 my @Callers = ();
323              
324 38         29 while( 1 ) {
325 286         647 my @caller = caller( ++$level );
326 286 100       373 last unless @caller;
327              
328 248         876 push @Callers, {
329             level => $level,
330             package => $caller[0],
331             'sub' => $caller[3] =~ m/(?:.*::)?(.*)/,
332             };
333             }
334              
335             #print STDERR Data::Dumper->Dump( [\@Callers], [qw(callers)] ), "-" x 73, "\n";
336 38         68 @Callers;
337             }
338              
339             =back
340              
341             =head1 Brick::Bucket::Entry
342              
343             =cut
344              
345             package Brick::Bucket::Entry;
346              
347 5     5   56 use Carp qw(carp);
  5         17  
  5         1822  
348              
349             =over 4
350              
351             =item my $entry = Brick::Bucket::Entry->new( HASHREF )
352              
353             =cut
354              
355             sub new {
356 14     14   16 my $class = shift;
357              
358 14         18 my $self = bless {}, $class;
359              
360 14   50     39 $self->{comprises} ||= [];
361              
362 14         25 $self;
363             }
364              
365              
366             =item $entry->get_gv()
367              
368             Get the GV object associated with the entry. The GV object comes from
369             the svref_2object(SVREF) function in the C module. Use it to get
370             information about the coderef's creation.
371              
372             my $entry = $bucket->get_entry( $coderef );
373             my $gv = $entry->get_gv;
374              
375             printf "$coderef comes from %s line %s\n",
376             map { $gv->$_ } qw( FILE LINE );
377              
378             The C documentation explains what you can do with the GV object.
379              
380             =cut
381              
382 0 0   0   0 sub get_gv { $_[0]->{gv} || Object::Null->new }
383              
384             =item $entry->get_name()
385              
386             Get the name for the entry.
387              
388             =cut
389              
390 16     16   24 sub get_name { $_[0]->{name} }
391              
392             =item $entry->get_description()
393              
394             Get the description for the entry.
395              
396             =cut
397              
398 20     20   58 sub get_description { $_[0]->{description} }
399              
400             =item $entry->get_coderef()
401              
402             Get the coderef for the entry. This is the actual reference that you
403             can execute, not the string form used for the bucket key.
404              
405             =cut
406              
407 2     2   4 sub get_coderef { $_[0]->{code} }
408              
409             =item $entry->get_comprises()
410              
411             Get the subroutines that this entry composes. A coderef might simply
412             combine other code refs, and this part gives the map. Use it recursively
413             to get the tree of code refs that make up this entry.
414              
415             =cut
416              
417 14     14   20 sub get_comprises { $_[0]->{comprises} }
418              
419             =item $entry->get_created_by()
420              
421             Get the name of the routine that added the entry to the bucket. This
422             is handy for tracing the flow of code refs around the program. Different
423             routines my make coderefs with the same name, so you also want to know
424             who created it. You can use this with C to get file and line numbers
425             too.
426              
427             =cut
428              
429 0 0   0   0 sub get_created_by { ref $_[0]->{created_by} ? $_[0]->{created_by} : [] }
430              
431             =item $entry->get_fields()
432              
433             =cut
434              
435 0     0   0 sub get_fields { [ keys %{ $_[0]->entry( $_[1] )->{fields} } ] }
  0         0  
436              
437             =item $entry->set_name( SCALAR )
438              
439             Set the entry's name. Usually this happens when you add the object
440             to the bucket, but you might want to update it to show more specific or higher
441             level information. For instance, if you added the code ref with a low
442             level routine that named the entry "check_number", a higher order routine
443             might want to reuse the same entry but pretend it created it by setting
444             the name to "check_integer", a more specific sort of check.
445              
446             =cut
447              
448 20     20   23 sub set_name { $_[0]->{name} = $_[1] }
449              
450             =item $entry->set_description( SCALAR )
451              
452             Set the entry's description. Usually this happens when you add the object
453             to the bucket, but you might want to update it to show more specific or higher
454             level information. See C.
455              
456             =cut
457              
458 20     20   21 sub set_description { $_[0]->{description} = $_[1] }
459              
460             =item $entry->set_gv( SCALAR )
461              
462             Set the GV object for the entry. You probably don't want to do this
463             yourself. The bucket does it for you when it adds the object.
464              
465             =cut
466              
467 20     20   47 sub set_gv { $_[0]->{gv} = $_[1] }
468              
469             =item $entry->add_bit( CODEREFS )
470              
471             I hate this name, but this is the part that adds the CODEREFS to the
472             entry that composes it.
473              
474             =cut
475              
476             sub add_bit {
477 8     8   7 my $entry = shift;
478 5     5   36 no warnings;
  5         7  
  5         1182  
479              
480             # can things get in here twice
481 8         8 push @{ $entry->{comprises} }, map { "$_" } @_;
  8         14  
  12         25  
482             }
483              
484             =item $entry->dump
485              
486             Print a text version of the entry.
487              
488             =cut
489              
490             sub dump {
491 0     0     require Data::Dumper;
492              
493 0           Data::Dumper->Dump( [ $_[0]->entry( $_[1] ) ], [ "$_[1]" ] )
494             }
495              
496             =item $entry->applies_to_fields
497              
498             Return a list of fields the brick applies to.
499              
500             I don't think I've really figured this out, but the composers should be
501             the ones to figure it out and add this stuff to the information that the
502             bucket tracks.
503              
504             =cut
505              
506             sub applies_to_fields {
507 0     0     my( $class, $sub, @fields ) = @_;
508              
509 0           foreach my $field ( @fields ) {
510 0           $class->registry->{$sub}{fields}{$field}++;
511 0           $class->registry->{_fields}{$field}{$sub}++;
512             }
513             }
514              
515              
516             =back
517              
518             =head1 TO DO
519              
520             TBA
521              
522             =head1 SEE ALSO
523              
524             TBA
525              
526             =head1 SOURCE AVAILABILITY
527              
528             This source is in Github:
529              
530             https://github.com/briandfoy/brick
531              
532             =head1 AUTHOR
533              
534             brian d foy, C<< >>
535              
536             =head1 COPYRIGHT
537              
538             Copyright © 2007-2026, brian d foy . All rights reserved.
539              
540             You may redistribute this under the terms of the Artistic License 2.0.
541              
542             =cut
543              
544             1;