File Coverage

blib/lib/Test/Inline.pm
Criterion Covered Total %
statement 189 231 81.8
branch 63 120 52.5
condition 6 13 46.1
subroutine 39 43 90.7
pod 18 18 100.0
total 315 425 74.1


line stmt bran cond sub pod time code
1             package Test::Inline; # git description: v2.213-6-g42eabb2
2             # ABSTRACT: Embed your tests in your code, next to what is being tested
3              
4             #pod =pod
5             #pod
6             #pod =head1 DESCRIPTION
7             #pod
8             #pod Embedding tests allows tests to be placed near the code being tested.
9             #pod
10             #pod This is a nice supplement to the traditional .t files.
11             #pod
12             #pod =head2 How does it work?
13             #pod
14             #pod C lets you write small fragments of general or
15             #pod function-specific testing code, and insert it anywhere you want in your
16             #pod modules, inside a specific tagged L segment, like the
17             #pod following.
18             #pod
19             #pod =begin testing
20             #pod
21             #pod # This code assumes we have a cpuinfo file
22             #pod ok( -f /proc/cpuinfo, 'Host has a standard /proc/cpuinfo file' );
23             #pod
24             #pod =end testing
25             #pod
26             #pod =begin testing label
27             #pod
28             #pod # Test generation of the
29             #pod is( My::HTML->label('foo'), '', '->label(simple) works' );
30             #pod is( My::HTML->label('bar', 'foo'), '', '->label(for) works' );
31             #pod
32             #pod =end testing
33             #pod
34             #pod You can add as many, or as few, of these chunks of tests as you wish.
35             #pod The key condition when writing them is that they should be logically
36             #pod independant of each other. Each chunk of testing code should not die
37             #pod or crash if it is run before or after another chunk.
38             #pod
39             #pod Using L or another test compiler, you can then transform
40             #pod these chunks in a test script, or an entire tree of modules into a
41             #pod complete set of standard L-based test scripts.
42             #pod
43             #pod These test scripts can then be executed as normal.
44             #pod
45             #pod =head2 What is Test::Inline good for?
46             #pod
47             #pod C is incredibly useful for doing ad-hoc unit testing.
48             #pod
49             #pod In any large groups of modules, you can add testing code here, there and
50             #pod everywhere, anywhere you want. The next time the test compiler is run, a
51             #pod new test script will just appear.
52             #pod
53             #pod This also makes it great for testing assumptions you normally wouldn't
54             #pod bother to write run-time code to test. It ensures that your assumptions
55             #pod about the way Perl does some operation, or about the state of the host,
56             #pod are confirmed at install-time.
57             #pod
58             #pod If your assumption is ever wrong, it gets picked up at install-time and
59             #pod based on the test failures, you can correct your assumption.
60             #pod
61             #pod It's also extremely useful for systematically testing self-contained code.
62             #pod
63             #pod That is, any code which can be independantly tested without the need for
64             #pod external systems such as databases, and that has no side-effects on external
65             #pod systems.
66             #pod
67             #pod All of this code, written by multiple people, can then have one single set
68             #pod of test files generated. You can check all the bits and pieces of a large
69             #pod API, or anything you like, in fine detail.
70             #pod
71             #pod Test::Inline also introduces the concept of unit-tested documentation.
72             #pod
73             #pod Not only can your code be tested, but if you have a FAQ or some other
74             #pod pure documentation module, you can validate that the documentation is
75             #pod correct for the version of the module installed.
76             #pod
77             #pod If the module ever changes to break the documentation, you can catch it
78             #pod and correct the documentation.
79             #pod
80             #pod =head2 What is Test::Inline bad for?
81             #pod
82             #pod C is B a complete testing solution, and there are several
83             #pod types of testing you probably DON'T want to use it for.
84             #pod
85             #pod =over
86             #pod
87             #pod =item *
88             #pod
89             #pod Static testing across the entire codebase
90             #pod
91             #pod =item *
92             #pod
93             #pod Functional testing
94             #pod
95             #pod =item *
96             #pod
97             #pod Tests with side-effects such as those that might change a testing database
98             #pod
99             #pod =back
100             #pod
101             #pod =head2 Getting Started
102             #pod
103             #pod Because Test::Inline creates test scripts with file names that B
104             #pod start with a number (for ordering purposes), the first step is to create
105             #pod your normal test scripts using file names in the CPAN style of
106             #pod F<01_compile.t>, F<02_main.t>, F<03_foobar.t>, and so on.
107             #pod
108             #pod You can then add your testing fragments wherever you like throughout
109             #pod your code, and use the F script to generate the test scripts
110             #pod for the inline tests. By default the test scripts will be named after
111             #pod the packages/classes that the test fragments are found in.
112             #pod
113             #pod Tests for Class::Name will end up in the file C.
114             #pod
115             #pod These test files sit quite happily alongside your number test scripts.
116             #pod
117             #pod When you run the test suite as you normally would, the inline scripts
118             #pod will be run after the numbered tests.
119             #pod
120             #pod =head1 METHODS
121             #pod
122             #pod =cut
123              
124 12     12   1099050 use 5.006;
  12         147  
125 12     12   65 use strict;
  12         21  
  12         251  
126 12     12   6613 use IO::Handle ();
  12         70876  
  12         331  
127 12     12   77 use List::Util 1.19 ();
  12         256  
  12         273  
128 12     12   70 use File::Spec 0.80 ();
  12         246  
  12         277  
129 12     12   5952 use Params::Util 0.21 ();
  12         70964  
  12         400  
130 12     12   7122 use Algorithm::Dependency 1.02 ();
  12         33620  
  12         284  
131 12     12   85 use Algorithm::Dependency::Source ();
  12         26  
  12         168  
132 12     12   5320 use Test::Inline::Util ();
  12         30  
  12         253  
133 12     12   6242 use Test::Inline::Section ();
  12         34  
  12         289  
134 12     12   5672 use Test::Inline::Script ();
  12         32  
  12         293  
135 12     12   4777 use Test::Inline::Content ();
  12         32  
  12         243  
136 12     12   5134 use Test::Inline::Content::Legacy ();
  12         31  
  12         257  
137 12     12   4801 use Test::Inline::Content::Default ();
  12         27  
  12         306  
138 12     12   5108 use Test::Inline::Content::Simple ();
  12         34  
  12         296  
139 12     12   5572 use Test::Inline::Extract ();
  12         35  
  12         263  
140 12     12   5210 use Test::Inline::IO::File ();
  12         35  
  12         29964  
141              
142             our $VERSION = '2.214';
143             our @ISA = 'Algorithm::Dependency::Source';
144              
145              
146              
147              
148              
149             #####################################################################
150             # Constructor and Accessors
151              
152             #pod =pod
153             #pod
154             #pod =head2 new
155             #pod
156             #pod my $Tests = Test::Inline->new(
157             #pod verbose => 1,
158             #pod readonly => 1,
159             #pod output => 'auto',
160             #pod manifest => 'auto/manifest',
161             #pod );
162             #pod
163             #pod The C constructor creates a new test generation framework. Once the
164             #pod constructor has been used to create the generator, the C method
165             #pod can be used to specify classes, or class heirachies, to generate tests for.
166             #pod
167             #pod B - The C option causes the generator to write state and
168             #pod debugging information to STDOUT as it runs.
169             #pod
170             #pod B - The C option, if provided, will cause a manifest
171             #pod file to be created and written to disk. The manifest file contains a list
172             #pod of all the test files generated, but listed in the prefered order they
173             #pod should be processed to best satisfy the class-level dependency of the
174             #pod tests.
175             #pod
176             #pod B - The C value controls how strictly the
177             #pod test script will watch the number of tests that have been executed.
178             #pod
179             #pod When set to false, the script does no count checking other than the
180             #pod standard total count for scripts (where all section counts are known)
181             #pod
182             #pod When set to C<1> (the default), C does smart count checking,
183             #pod doing section-by-section checking for known-count sections B when
184             #pod the total for the entire script is not known.
185             #pod
186             #pod When set to C<2> or higher, C does full count checking,
187             #pod doing section-by-section checking for every section with a known number
188             #pod of tests.
189             #pod
190             #pod B - The C option should be provided as a CODE
191             #pod reference, which will be passed as arguments the C object,
192             #pod and a single L object, and should return a string
193             #pod containing the contents of the resulting test file. This will be written
194             #pod to the C.
195             #pod
196             #pod B - The C option provides the location of the directory
197             #pod where the tests will be written to. It should both already exist, and be
198             #pod writable. If using a custom C, the value of C should
199             #pod refer to the location B that the files will be
200             #pod written to.
201             #pod
202             #pod B - The C option, if provided, indicates that any
203             #pod generated test files should be created (or set when updated) with
204             #pod read-only permissions, to prevent accidentally adding to or editing the
205             #pod test scripts directly (instead of via the classes).
206             #pod
207             #pod This option is currently disabled by default, by may be enabled by default
208             #pod in a future release, so if you do NOT want your tests being created as
209             #pod read-only, you should explicitly set this option to false.
210             #pod
211             #pod B - The C option, if provided, supplies an
212             #pod alternative C from which source modules are retrieved.
213             #pod
214             #pod B - The C option, if provided, supplies an
215             #pod alternative C to which the resulting test scripts are written.
216             #pod
217             #pod Returns a new C object on success.
218             #pod
219             #pod Returns C if there is a problem with one of the options.
220             #pod
221             #pod =cut
222              
223             # For now, the various Handlers are hard-coded
224             sub new {
225 17     17 1 14369 my $class = Params::Util::_CLASS(shift);
226 17         251 my %params = @_;
227 17 50       92 unless ( $class ) {
228 0         0 die '->new is a static method';
229             }
230              
231             # Create the object
232             my $self = bless {
233             # Return errors via exceptions?
234             exception => !! $params{exception},
235              
236             # Extensibility provided through the use of Handler classes
237             InputHandler => $params{InputHandler},
238             ExtractHandler => $params{ExtractHandler},
239             ContentHandler => $params{ContentHandler},
240             OutputHandler => $params{OutputHandler},
241              
242             # Store the ::TestFile objects
243 17         138 Classes => {},
244             }, $class;
245              
246             # Run in verbose mode?
247 17         112 $self->{verbose} = !! $params{verbose};
248              
249             # Generate tests with read-only permissions?
250 17         61 $self->{readonly} = !! $params{readonly};
251              
252             # Generate a manifest file?
253 17 100       55 $self->{manifest} = $params{manifest} if $params{manifest};
254              
255             # Do count checking?
256             $self->{check_count} = exists $params{check_count}
257             ? $params{check_count}
258 17 100       81 ? $params{check_count} >= 2
    100          
    100          
259             ? 2 # Paranoid count checking
260             : 1 # Smart count checking
261             : 0 # No count checking
262             : 1; # Smart count checking (default)
263              
264             # Support the legacy file_content param
265 17 50       52 if ( $params{file_content} ) {
266 0 0       0 Params::Util::_CODE($params{file_content}) or return undef;
267             $self->{ContentHandler} = Test::Inline::Content::Legacy->new(
268             $params{file_content}
269 0 0       0 ) or return undef;
270             }
271              
272             # Set the default Handlers
273 17   50     97 $self->{ExtractHandler} ||= 'Test::Inline::Extract';
274 17   33     161 $self->{ContentHandler} ||= Test::Inline::Content::Default->new;
275 17   33     244 $self->{InputHandler} ||= Test::Inline::IO::File->new( File::Spec->curdir );
276             $self->{OutputHandler} ||= Test::Inline::IO::File->new(
277             path => File::Spec->curdir,
278             readonly => $self->{readonly},
279 17   33     127 );
280              
281             # Where to write test file to, within the context of the OutputHandler
282 17 100       61 $self->{output} = defined $params{output} ? $params{output} : '';
283              
284 17         67 $self;
285             }
286              
287             #pod =pod
288             #pod
289             #pod =head2 exception
290             #pod
291             #pod The C method returns a flag which indicates whether error will
292             #pod be returned via exceptions.
293             #pod
294             #pod =cut
295              
296             sub exception {
297 0     0 1 0 $_[0]->{exception};
298             }
299              
300             #pod =pod
301             #pod
302             #pod =head2 InputHandler
303             #pod
304             #pod The C method returns the file handler object that will be
305             #pod used to find and load the source code.
306             #pod
307             #pod =cut
308              
309             sub InputHandler {
310 35     35 1 154 $_[0]->{InputHandler};
311             }
312              
313             #pod =pod
314             #pod
315             #pod =head2 ExtractHandler
316             #pod
317             #pod The C accessor returns the object that will be used
318             #pod to extract the test sections from the source code.
319             #pod
320             #pod =cut
321              
322             sub ExtractHandler {
323 20     20 1 165 $_[0]->{ExtractHandler};
324             }
325              
326             #pod =pod
327             #pod
328             #pod =head2 ContentHandler
329             #pod
330             #pod The C accessor return the script content generation handler.
331             #pod
332             #pod =cut
333              
334             sub ContentHandler {
335 15     15 1 58 $_[0]->{ContentHandler};
336             }
337              
338             #pod =pod
339             #pod
340             #pod =head2 OutputHandler
341             #pod
342             #pod The C accessor returns the file handler object that the
343             #pod generated test scripts will be written to.
344             #pod
345             #pod =cut
346              
347             sub OutputHandler {
348 18     18 1 65 $_[0]->{OutputHandler};
349             }
350              
351              
352              
353              
354              
355             #####################################################################
356             # Test::Inline Methods
357              
358             #pod =pod
359             #pod
360             #pod =head2 add $file, $directory, \$source, $Handle
361             #pod
362             #pod The C method is a parameter-sensitive method for adding something
363             #pod to the build schedule.
364             #pod
365             #pod It takes as argument a file path, a directory path, a reference to a SCALAR
366             #pod containing perl code, or an L (or subclass) object. It will
367             #pod retrieve code from the parameter as appropriate, parse it, and create zero
368             #pod or more L objects representing the test scripts that
369             #pod will be generated for that source code.
370             #pod
371             #pod Returns the number of test scripts added, which could be zero, or C
372             #pod on error.
373             #pod
374             #pod =cut
375              
376             sub add {
377 13     13 1 6645 my $self = shift;
378 13 50       51 my $source = $self->_source(shift) or return undef;
379 13 100       57 if ( ref $source ) {
380             # Add a chunk of source code
381 12         61 return $self->_add_source($source);
382             } else {
383             # Add a whole directory
384 1         9 return $self->_add_directory($source);
385             }
386             }
387              
388             #pod =pod
389             #pod
390             #pod =head2 add_class
391             #pod
392             #pod $Tests->add_class( 'Foo::Bar' );
393             #pod $Tests->add_class( 'Foo::Bar', recursive => 1 );
394             #pod
395             #pod The C method adds a class to the list of those to have their tests
396             #pod generated. Optionally, the C option can be provided to add not just
397             #pod the class you provide, but all classes below it as well.
398             #pod
399             #pod Returns the number of classes found with inline tests, and added, including
400             #pod C<0> if no classes with tests are found. Returns C if an error occurs
401             #pod while adding the class or it's children.
402             #pod
403             #pod =cut
404              
405             sub add_class {
406 0     0 1 0 my $self = shift;
407 0 0       0 my $name = shift or return undef;
408 0         0 my %options = @_;
409              
410             # Determine the files to add
411 0         0 $self->_verbose("Checking $name\n");
412             my $files = $options{recursive}
413 0 0       0 ? $self->InputHandler->find( $name )
414             : $self->InputHandler->file( $name );
415 0 0       0 return $files unless $files; # 0 or undef
416              
417             # Add the files
418 0         0 my $added = 0;
419 0         0 foreach my $file ( @$files ) {
420 0         0 my $rv = $self->add( $file );
421 0 0       0 return undef unless defined $rv;
422 0         0 $added += $rv;
423             }
424              
425             # Clear the caches
426 0         0 delete $self->{schedule};
427 0         0 delete $self->{filenames};
428              
429 0         0 $added;
430             }
431              
432             #pod =pod
433             #pod
434             #pod =head2 add_all
435             #pod
436             #pod The C method will search the C for all *.pm files,
437             #pod and add them to the generation set.
438             #pod
439             #pod Returns the total number of test scripts added, which may be zero, or
440             #pod C on error.
441             #pod
442             #pod =cut
443              
444             sub add_all {
445 1     1 1 612 my $self = shift;
446 1         3 my $rv = eval {
447 1         4 $self->_add_directory('.');
448             };
449 1 50       4 return $self->_error($@) if $@;
450 1         3 return $rv;
451             }
452              
453             # Recursively add an entire directory of files
454             sub _add_directory {
455 2     2   5 my $self = shift;
456              
457             # Find all module files in the directory
458 2 50       7 my $files = $self->InputHandler->find(shift) or return undef;
459              
460             # Add each file
461 2         6 my $added = 0;
462 2         8 foreach my $file ( @$files ) {
463 7 50       20 my $source = $self->InputHandler->read($file) or return undef;
464 7         23 my $rv = $self->_add_source($source);
465 7 50       17 return undef unless defined $rv;
466 7         16 $added += $rv;
467             }
468              
469 2         21 $added;
470             }
471              
472             # Actually add the source code
473             sub _add_source {
474 20     20   699 my $self = shift;
475 20 50       105 my $source = Params::Util::_SCALAR(shift) or return undef;
476              
477             # Extract the elements from the source code
478 20 50       74 my $Extract = $self->ExtractHandler->new( $source )
479             or return $self->_error("Failed to create ExtractHandler");
480 20 100       85 my $elements = $Extract->elements or return 0;
481              
482             # Parse the elements into sections
483 17 100       134 my $Sections = Test::Inline::Section->parse( $elements )
484             or return $self->_error("Failed to parse sections: $Test::Inline::Section::errstr");
485              
486             # Split up the Sections by class
487 16         44 my %classes = ();
488 16         40 foreach my $Section ( @$Sections ) {
489             # All sections MUST have a package
490 49 50       123 my $context = $Section->context
491             or return $self->_error("Section does not have a package context");
492 49   100     209 $classes{$context} ||= [];
493 49         70 push @{$classes{$context}}, $Section;
  49         121  
494             }
495              
496             # Convert the collection of Sections into class-specific test file objects
497 16         32 my $added = 0;
498 16         46 my $Classes = $self->{Classes};
499 16         54 foreach my $_class ( keys %classes ) {
500             # We can't safely spread tests for the same class across
501             # different files. Error if we spot a duplicate.
502 20 50       54 if ( $Classes->{$_class} ) {
503 0         0 return $self->_error("Caught duplicate test class");
504             }
505              
506             # Create a new ::TestFile object for the collection of Sections
507             my $File = Test::Inline::Script->new(
508             $_class,
509             $classes{$_class},
510             $self->{check_count}
511 20 50       143 ) or return $self->_error("Failed to create a new TestFile for '$_class'");
512 20         126 $self->_verbose("Adding $File to schedule\n");
513 20         45 $Classes->{$_class} = $File;
514 20         44 $added++;
515             }
516              
517 16         168 $added++;
518             }
519              
520             #pod =pod
521             #pod
522             #pod =head2 classes
523             #pod
524             #pod The C method returns a list of the names of all the classes that
525             #pod have been added to the C object, or the null list C<()> if
526             #pod nothing has been added.
527             #pod
528             #pod =cut
529              
530             sub classes {
531 5     5 1 12 my $self = shift;
532 5         10 sort keys %{$self->{Classes}};
  5         47  
533             }
534              
535             #pod =pod
536             #pod
537             #pod =head2 class
538             #pod
539             #pod For a given class name, fetches the L object for that
540             #pod class, if it has been added to the C object. Returns C
541             #pod if the class has not been added to the C object.
542             #pod
543             #pod =cut
544              
545 23     23 1 486 sub class { $_[0]->{Classes}->{$_[1]} }
546              
547             #pod =pod
548             #pod
549             #pod =head2 filenames
550             #pod
551             #pod For all of the classes added, the C method generates a map of the
552             #pod filenames that the test files for the various classes should be written to.
553             #pod
554             #pod Returns a reference to a hash with the classes as keys, and filenames as
555             #pod values.
556             #pod
557             #pod Returns C<0> if there are no files to write.
558             #pod
559             #pod Returns C on error.
560             #pod
561             #pod =cut
562              
563             sub filenames {
564 35     35 1 54 my $self = shift;
565 35 100       113 return $self->{filenames} if $self->{filenames};
566              
567             # Create an Algorithm::Dependency for the classes
568 6 50       72 my $Algorithm = Algorithm::Dependency::Ordered->new(
569             source => $self,
570             ignore_orphans => 1,
571             ) or return undef;
572              
573             # Get the build schedule
574 6         209 $self->_verbose("Checking dependencies\n");
575 6 50       27 unless ( $Algorithm->source->items ) {
576 0         0 return 0;
577             }
578 6 50       36 my $schedule = $Algorithm->schedule_all or return undef;
579              
580             # Merge the test position counter with the class base names
581 6         107 my %filenames = ();
582 6         29 for ( my $i = 0; $i <= $#$schedule; $i++ ) {
583 14         25 my $class = $schedule->[$i];
584 14         40 $filenames{$class} = $self->{Classes}->{$class}->filename;
585             }
586              
587 6         25 $self->{schedule} = [ map { $filenames{$_} } @$schedule ];
  14         35  
588 6         47 $self->{filenames} = \%filenames;
589             }
590              
591             #pod =pod
592             #pod
593             #pod =head2 schedule
594             #pod
595             #pod While the C method generates a map of the files for the
596             #pod various classes, the C returns the list of file names in the
597             #pod order in which they should actually be executed.
598             #pod
599             #pod Returns a reference to an array containing the file names as strings.
600             #pod
601             #pod Returns C<0> if there are no files to write.
602             #pod
603             #pod Returns C on error.
604             #pod
605             #pod =cut
606              
607             sub schedule {
608 8     8 1 60 my $self = shift;
609 8 100       42 return $self->{schedule} if $self->{schedule};
610              
611             # Generate the file names and schedule
612 1 50       5 $self->filenames or return undef;
613              
614 1         5 $self->{schedule};
615             }
616              
617             #pod =pod
618             #pod
619             #pod =head2 manifest
620             #pod
621             #pod The C generates the contents of the manifest file, if it is both
622             #pod wanted and needed.
623             #pod
624             #pod Returns the contents of the manifest file as a normal string, false if it
625             #pod is either not wanted or needed, or C on error.
626             #pod
627             #pod =cut
628              
629             sub manifest {
630 8     8 1 21 my $self = shift;
631              
632             # Do we need to create a file?
633 8 50       71 my $schedule = $self->schedule or return undef;
634 8 50       43 return '' unless $self->{manifest};
635 8 50       25 return '' unless @$schedule;
636              
637             # Each manifest entry should be listed by it's path relative to
638             # the location of the manifest file.
639 8         172 my $manifest_dir = (File::Spec->splitpath($self->{manifest}))[1];
640             my $relative_path = Test::Inline::Util->relative(
641             $manifest_dir => $self->{output},
642 8         69 );
643 8 50       24 return undef unless defined $relative_path;
644              
645             # Generate and merge the manifest entries
646 8         23 my @manifest = @$schedule;
647 8 100       33 if ( length $relative_path ) {
648 7         14 @manifest = map { File::Spec->catfile( $relative_path, $_ ) } @manifest;
  25         162  
649             }
650 8         27 join '', map { "$_\n" } @manifest;
  26         100  
651             }
652              
653             #pod =pod
654             #pod
655             #pod =head2 save
656             #pod
657             #pod $Tests->save;
658             #pod
659             #pod The C method generates the test files for all classes, and saves them
660             #pod to the C directory.
661             #pod
662             #pod Returns the number of test files generated. Returns C on error.
663             #pod
664             #pod =cut
665              
666             sub save {
667 4     4 1 7887 my $self = shift;
668              
669             # Get the file names to save to
670 4         20 my $filenames = $self->filenames;
671 4 50       17 return $filenames unless $filenames; # undef or 0
672              
673             # Write the manifest if needed
674 4         12 my $manifest = $self->manifest;
675 4 50       12 return undef unless defined $manifest;
676 4 50       20 if ( $manifest ) {
677 4 50       25 if ( $self->OutputHandler->write( $self->{manifest}, $manifest ) ) {
678 4         30 $self->_verbose( "Wrote manifest file '$self->{manifest}'\n" );
679             } else {
680 0         0 $self->_verbose( "Failed to write manifest file '$self->{manifest}'\n" );
681 0         0 return undef;
682             }
683             }
684              
685             # Write the files
686 4         10 my $written = 0;
687 4         28 foreach my $class ( sort keys %$filenames ) {
688 14 50       32 $self->_save( $class ) or return undef;
689 14         29 $written++;
690             }
691              
692 4         23 $written;
693             }
694              
695             sub _file {
696 29     29   38 my $self = shift;
697 29 50       49 my $filenames = $self->filenames or return undef;
698 29         96 $filenames->{$_[0]};
699             }
700              
701             sub _save {
702 14     14   25 my $self = shift;
703 14 50       34 my $class = shift or return undef;
704 14 50       34 my $filename = $self->_file($class) or return undef;
705 14         63 local $| = 1;
706              
707             # Write the file
708 14 50       39 my $content = $self->_content($class) or return undef;
709 14         35 $self->_verbose("Saving...");
710 14 50       60 if ( $self->{output} ) {
711 14         145 $filename = File::Spec->catfile( $self->{output}, $filename );
712             }
713 14 50       45 unless ( $self->OutputHandler->write( $filename, $content ) ) {
714 0         0 $self->_verbose("Failed\n");
715 0         0 return undef;
716             }
717 14         54 $self->_verbose("Done\n");
718              
719 14         56 1;
720             }
721              
722             sub _content {
723 15     15   27 my $self = shift;
724 15 50       34 my $class = shift or return undef;
725 15 50       32 my $filename = $self->_file($class) or return undef;
726 15 50       41 my $Script = $self->class($class) or return undef;
727              
728             # Get the file content
729 15         63 $self->_verbose("Generating $filename for $class...");
730 15         40 my $content = $self->ContentHandler->process( $self, $Script );
731 15 50       47 $self->_verbose("Failed\n") unless defined $content;
732              
733 15         41 $content; # content or undef
734             }
735              
736              
737              
738              
739              
740             #####################################################################
741             # Implement the Algorithm::Dependency::Source Interface
742              
743 0     0 1 0 sub load { 1 }
744 42     42 1 770 sub item { $_[0]->{Classes}->{$_[1]} }
745             sub items {
746 12     12 1 74 my $classes = shift->{Classes};
747 12         48 map { $classes->{$_} } sort keys %$classes;
  28         70  
748             }
749              
750              
751              
752              
753              
754             #####################################################################
755             # Support Methods
756              
757             # Get the source code from a variety of places
758             sub _source {
759 13     13   25 my $self = shift;
760 13 50       49 return undef unless defined $_[0];
761 13 50       41 unless ( ref $_[0] ) {
762 13 100       41 if ( $self->InputHandler->exists_file($_[0]) ) {
    50          
763             # File path
764 12         54 return $self->InputHandler->read(shift);
765             } elsif ( $self->InputHandler->exists_dir($_[0]) ) {
766             # Directory path
767 1         7 return shift; # Handled seperately
768             }
769 0         0 return undef;
770             }
771 0 0       0 if ( Params::Util::_SCALAR($_[0]) ) {
772             # Reference to SCALAR containing code
773 0         0 return shift;
774             }
775 0 0       0 if ( Params::Util::_INSTANCE($_[0], 'IO::Handle') ) {
776 0         0 my $fh = shift;
777 0         0 my $old = $fh->input_record_separator(undef);
778 0         0 my $code = $fh->getline;
779 0         0 $fh->input_record_separator($old);
780 0         0 return \$code;
781             }
782              
783             # Unknown
784 0         0 undef;
785             }
786              
787             # Print a message if we are in verbose mode
788             sub _verbose {
789 73     73   116 my $self = shift;
790 73 50       210 return 1 unless $self->{verbose};
791 0           print @_;
792             }
793              
794             # Warn and return
795             sub _error {
796 0     0     my $self = shift;
797 0 0         if ( $self->exception ) {
798 0           Carp::croak("Error: $_[0]");
799             }
800 0           $self->_verbose(map { "Error: $_" } @_);
  0            
801 0           undef;
802             }
803              
804             1;
805              
806             __END__