File Coverage

blib/lib/Archive/Builder.pm
Criterion Covered Total %
statement 153 163 93.8
branch 52 64 81.2
condition 1 2 50.0
subroutine 37 39 94.8
pod 14 16 87.5
total 257 284 90.4


line stmt bran cond sub pod time code
1             package Archive::Builder;
2              
3             # This packages provides a simplified object for a collection of generated
4             # files, and ways to then distribute the files.
5              
6 7     7   502222 use 5.005;
  7         30  
  7         308  
7 7     7   43 use strict;
  7         14  
  7         253  
8 7     7   50 use Scalar::Util ();
  7         15  
  7         135  
9 7     7   125 use List::Util 1.15 ();
  7         164  
  7         165  
10 7     7   37 use File::Spec 0.80 ();
  7         153  
  7         153  
11 7     7   38 use File::Spec::Unix ();
  7         26  
  7         175  
12 7     7   12211 use Params::Util 0.22 ('_INSTANCE', '_STRING');
  7         73194  
  7         1078  
13 7     7   9499 use Class::Inspector 1.12 ();
  7         48251  
  7         246  
14 7     7   8680 use IO::String 1.08 ();
  7         39195  
  7         255  
15 7     7   9743 use Class::Autouse 1.27 ('File::Flat');
  7         56844  
  7         53  
16              
17             # Load the rest of the classes;
18 7     7   40453 use Archive::Builder::Section ();
  7         25  
  7         247  
19 7     7   8568 use Archive::Builder::File ();
  7         21  
  7         163  
20 7     7   4394 use Archive::Builder::Archive ();
  7         26  
  7         152  
21 7     7   17306 use Archive::Builder::Generators ();
  7         30  
  7         167  
22              
23             # Version
24 7     7   46 use vars qw{$VERSION $errstr};
  7         16  
  7         417  
25             BEGIN {
26 7     7   18 $VERSION = '1.16';
27 7         13842 $errstr = '';
28             }
29              
30              
31              
32              
33              
34             #####################################################################
35             # Main Interface Methods
36              
37             # Constructor
38 8     8 1 669 sub new { bless { sections => {} }, shift }
39              
40             # Test generate and cache all files.
41 0 0   0 0 0 sub test { foreach ( $_[0]->section_list ) { $_->test or return undef } 1 }
  0         0  
  0         0  
42              
43             # Save all files to disk
44             sub save {
45 2     2 1 2035 my $self = shift;
46 2   50     10 my $base = shift || '.';
47              
48             # Check we can write to the location
49 2 50       15 unless ( File::Flat->canWrite( $base ) ) {
50 0         0 return $self->_error( "Insufficient permissions to write to '$base'" );
51             }
52              
53             # Process each of the sections
54 2         674 foreach my $Section ( $self->section_list ) {
55 3         19 my $subdir = File::Spec->catdir( $base, $Section->path );
56 3 50       15 unless ( $Section->save( $subdir ) ) {
57 0         0 return $self->_error( "Failed to save Archive::Builder to '$base'" );
58             }
59             }
60              
61 2         20 1;
62             }
63              
64             # Explicitly delete Archive.
65             # Just pass the call down to the sections.
66 0     0 1 0 sub delete { foreach ( $_[0]->section_list ) { $_->delete } 1 }
  0         0  
  0         0  
67              
68             # If any files have been generated, flush the content cache
69             # so they will be generated again.
70             # Just pass the call down to the sections.
71 1     1 1 7 sub reset { foreach ( $_[0]->section_list ) { $_->reset } 1 }
  2         11  
  1         5  
72              
73             # Create a new archive for the Builder
74 9     9 1 25167 sub archive { Archive::Builder::Archive->new( $_[1], $_[0] ) }
75              
76             # Create a more shorthand set of data, keying path against content ref
77             sub _archive_content {
78 9     9   17 my $self = shift;
79              
80             # Get and merge the _archive_content()s for each section
81 9         20 my %tree = ();
82 9         23 foreach my $Section ( $self->section_list ) {
83 9 50       38 my $subtree = $Section->_archive_content or return undef;
84 9         42 my $path = $Section->path;
85 9         45 foreach ( keys %$subtree ) {
86 35         214 my $full = File::Spec::Unix->catfile( $path, $_ );
87 35         123 $tree{$full} = $subtree->{$_};
88             }
89             }
90              
91 9         37 \%tree;
92             }
93              
94             sub _archive_mode {
95 9     9   14 my $self = shift;
96              
97             # Collect a list of permission modes to apply
98 9         20 my %tree = ();
99 9         20 foreach my $Section ( $self->section_list ) {
100 9 50       32 my $subtree = $Section->_archive_mode or return undef;
101 9         26 my $path = $Section->path;
102 9         34 foreach ( keys %$subtree ) {
103 35         130 my $full = File::Spec::Unix->catfile( $path, $_ );
104 35         221 $tree{$full} = $subtree->{$_};
105             }
106             }
107              
108 9         63 \%tree;
109             }
110              
111              
112              
113              
114              
115             #########################################################################
116             # Working with sections
117              
118             # Add an existing section
119             sub add_section {
120 20     20 1 6082 my $self = shift;
121 20 100       207 my $Section = _INSTANCE(shift, 'Archive::Builder::Section') or return undef;
122              
123             # Does a section with the name already exists?
124 15         72 my $name = $Section->name;
125 15 100       84 if ( exists $self->{sections}->{$name} ) {
126 2         9 return $self->_error( 'A section with that name already exists' );
127             }
128              
129             # Add the section
130 13         73 $Archive::Builder::Section::_PARENT{Scalar::Util::refaddr($Section)} = $self;
131 13         69 $self->{sections}->{$name} = $Section;
132             }
133              
134             # Add a new section and return it
135             sub new_section {
136 17     17 1 8062 my $self = shift;
137              
138             # Create the section with the arguments
139 17 100       105 my $Section = Archive::Builder::Section->new( @_ ) or return undef;
140 11         49 $self->add_section($Section);
141             }
142              
143             # Add a number of new sections
144             sub new_sections {
145 3     3 1 1021 my $self = shift;
146 1         6 my %sections = (ref $_[0] eq 'HASH') ? %{$_[0]}
  2         7  
147 3 100       13 : map { $_ => $_ } @_;
148              
149             # Add each of the sections
150 3         18 foreach my $name ( sort keys %sections ) {
151 4 50       13 my $Section = $self->new_section($name) or return undef;
152 4 100       14 if ( $sections{$name} ne $name ) {
153 2 50       8 $Section->path($sections{$name}) or return undef;
154             }
155             }
156              
157 3         15 1;
158             }
159              
160             # Get the hash of sections
161 3 100   3 1 7 sub sections { %{$_[0]->{sections}} ? { %{$_[0]->{sections}} } : 0 }
  3         20  
  1         8  
162              
163             # Get the sections as a list
164             sub section_list {
165 37     37 1 85 my $sections = $_[0]->{sections};
166 37         151 map { $sections->{$_} } sort keys %$sections;
  43         176  
167             }
168              
169             # Get a section by name
170 21 100   21 1 4549 sub section { defined $_[1] ? $_[0]->{sections}->{$_[1]} : undef }
171              
172             # Remove a section, by name
173             sub remove_section {
174 3     3 1 14 my $self = shift;
175 3 100       16 my $name = defined $_[0] ? shift : return undef;
176 2 100       13 my $Section = $self->{sections}->{$name} or return undef;
177              
178             # Delete from our sections
179 1         4 delete $self->{sections}->{$name};
180              
181             # Remove the parent link
182 1         6 delete $Archive::Builder::Section::_PARENT{Scalar::Util::refaddr($Section)};
183              
184 1         4 1;
185             }
186              
187             # Returns the number of files in the Builder, by totalling
188             # all it's sections
189             sub file_count {
190 13 100   13 1 9637 List::Util::sum map { $_->file_count } $_[0]->section_list or 0;
  14         76  
191             }
192              
193             # Get a hash of files
194             sub files {
195 1     1 1 7 my $self = shift;
196 1         3 my %files = ();
197 1         2 foreach my $Section ( values %{$self->{sections}} ) {
  1         5  
198 2         8 foreach my $File ( $Section->file_list ) {
199 6         28 my $path = File::Spec::Unix->catfile( $Section->path, $File->path );
200 6         25 $files{$path} = $File;
201             }
202             }
203              
204 1         4 \%files;
205             }
206              
207              
208              
209              
210              
211             #####################################################################
212             # Utility methods
213              
214             sub _check {
215 111     111   170 my $either = shift;
216 111         148 my $type = shift;
217 111         212 my $string = shift;
218              
219 111 100       338 if ( $type eq 'name' ) {
220 25 100       87 return '' unless defined $string;
221 21 100       185 return $string =~ /^\w{1,31}$/ ? 1 : '';
222             }
223              
224 86 100       355 if ( $type eq 'relative path' ) {
225             # This makes sure a directory isn't bad
226 49         316 return $either->_relative_path($string);
227             }
228              
229 37 50       113 if ( $type eq 'generator' ) {
230 37 100       89 return $either->_error( 'No generator defined' ) unless defined $string;
231              
232             # Look for illegal characters
233 33 100       235 unless ( $string =~ /^\w+(::\w+)*$/ ) {
234 2         24 return $either->_error( 'Invalid function name format' );
235             }
236              
237             # Is it a valid alias
238 31 100       119 $string = "Archive::Builder::Generators::$string" unless $string =~ /::/;
239              
240             # All is good if the function is already loaded
241 7     7   56 SCOPE: { no strict 'refs';
  7         15  
  7         10490  
  31         63  
242 31 100       44 return 1 if defined *{"$string"}{CODE};
  31         448  
243             }
244              
245             # Does the class exist?
246 4         23 my ($module) = $string =~ m/^(.*)::.*$/;
247 4 50       28 unless ( Class::Inspector->installed( $module ) ) {
248 4         1884 return $either->_error( "Package '$module' does not appear to be present" );
249             }
250              
251 0         0 return 1;
252             }
253              
254 0         0 undef;
255             }
256              
257             sub _relative_path {
258 57     57   102 my $either = shift;
259 57 100       268 my $string = _STRING(shift) or return '';
260              
261             # Get the canonical version of the path
262 49         279 my $canon = File::Spec::Unix->canonpath( $string );
263              
264             # Does the path contain escaping forward slashes
265 49 100       212 return '' if $string =~ /\\/;
266              
267             # We allow one specific exception to the upwards rules.
268             # That is in the case where we want to put the content from
269             # section into the root of the Builder tree.
270 46 50       3273 return $string if $string eq '.';
271              
272             # Does the path contain upwards stuff?
273 46 50       396 return '' unless File::Spec::Unix->no_upwards( $string );
274 46 100       170 return '' if $string =~ /\.\./;
275              
276             # Is the path absolute
277 43         913 ! File::Spec::Unix->file_name_is_absolute( $string );
278             }
279              
280             # Error handling
281 117     117 0 8948 sub errstr { $errstr }
282 68     68   1126 sub _error { $errstr = $_[1]; undef }
  68         438  
283 56     56   1822 sub _clear { $errstr = '' }
284              
285             1;
286              
287             __END__