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