File Coverage

blib/lib/App/SimpleScan.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package App::SimpleScan;
2              
3 48     48   492884 use warnings;
  48         93  
  48         1492  
4 48     48   260 use strict;
  48         115  
  48         1618  
5 48     48   45266 use English qw(-no_match_vars);
  48         263905  
  48         285  
6              
7             our $VERSION = '3.01';
8              
9 48     48   24291 use Carp;
  48         101  
  48         4626  
10 48     48   64843 use Getopt::Long;
  48         754558  
  48         320  
11 48     48   61350 use Regexp::Common;
  48         131151  
  48         681  
12 48     48   3480558 use Scalar::Util qw(blessed);
  48         116  
  48         5278  
13 48     48   3998539 use WWW::Mechanize;
  0            
  0            
14             use WWW::Mechanize::Pluggable;
15             use Test::WWW::Simple;
16             use App::SimpleScan::TestSpec;
17             use App::SimpleScan::Substitution;
18             use Graph;
19              
20             use Module::Pluggable search_path => [qw(App::SimpleScan::Plugin)];
21              
22             use base qw(Class::Accessor::Fast);
23             __PACKAGE__->mk_accessors(qw(sub_engine tests test_count
24             next_line_callbacks _deps));
25              
26             $|++; ##no critic
27              
28             use App::SimpleScan::TestSpec;
29              
30             my $reference_mech = new WWW::Mechanize::Pluggable;
31             my $sub_engine = new App::SimpleScan::Substitution;
32              
33             my @local_pragma_support =
34             (
35             ['agent' => \&_do_agent],
36             ['nocache' => \&_do_nocache],
37             ['cache' => \&_do_cache],
38             );
39              
40             # Variables and setup for basic command-line options.
41             my($generate, $run, $warn, $override, $defer, $debug);
42             my($cache_from_cmdline, $no_agent);
43             my($run_status);
44              
45             # Option-to-variable mappings for Getopt::Long
46             my %basic_options =
47             ('generate' => \$generate,
48             'run' => \$run,
49             'warn' => \$warn,
50             'override' => \$override,
51             'defer' => \$defer,
52             'debug' => \$debug,
53             'autocache' => \$cache_from_cmdline,
54             'no-agent' => \$no_agent,
55             'status' => \$run_status,
56             );
57              
58             use base qw(Class::Accessor::Fast);
59              
60             # Patterns to extract or >variables< from a string.
61             my $out_angled;
62             $out_angled = qr/ < ( [^<>] | (??{$out_angled}) )* > /x;
63             # open angle-bracket then ...
64             # non-angle chars ...
65             # or ...
66             # another angle-bracketed item ...
67             # if there are any ...
68             # and a close angle-bracket
69             my $in_angled;
70             $in_angled = qr/ > ( [^<>] | (??{$in_angled}) )* < /x;
71             # open angle-bracket then ...
72             # non-angle chars ...
73             # or ...
74             # another angle-bracketed item ...
75             # if there are any ...
76             # and a close angle-bracket
77             my $in_or_out_bracketed = qr/ ($out_angled) | ($in_angled) /x;
78              
79             ################################
80             # Basic class methods.
81              
82             # Create the object.
83             # - load and install plugins
84             # - make object available to test specs for callbacks
85             # - clear the tests and test count
86             # - process the command-line options
87             # - return the object
88             sub new {
89             my ($class) = @_;
90             my $self = {};
91             bless $self, $class;
92              
93             $self->_deps(Graph->new);
94             $self->sub_engine($sub_engine);
95              
96             # initialize fields first; plugins may expect good values.
97             $self->next_line_callbacks([]);
98             $self->tests([]);
99             $self->test_count(0);
100             $self->{InputQueue} = [];
101              
102             # Load and install the plugins.
103             $self->_load_plugins();
104             $self->install_pragma_plugins;
105              
106             # TestSpec needs to be able to find the App object.
107             App::SimpleScan::TestSpec->app($self);
108              
109             # Read the command line and process the options.
110             $self->handle_options;
111              
112             return $self;
113             }
114              
115             # Read the test specs and turn them into tests.
116             # Add any additional code from the plugins.
117             # Return the tests as a string.
118             sub create_tests {
119             my ($self) = @_;
120              
121             $self->transform_test_specs;
122             $self->finalize_tests;
123             return join q{}, @{$self->tests};
124             }
125              
126             # If the tests should be run, run them.
127             # Return any exceptions to the caller.
128             sub execute {
129             my ($self, $code) = @_;
130             eval $code if ${$self->run}; ##no critic
131             return $EVAL_ERROR;
132             }
133              
134             # Actually use the object.
135             # - create tests from input
136             # - run them if we should
137             # - print them if we should
138             sub go {
139             my($self) = @_;
140             my $exit_code = 0;
141              
142             my $code = $self->create_tests;
143             # Note the dereference of the scalars here.
144              
145             if ($self->test_count) {
146             if (my $result = $self->execute($code)) {
147             warn $result,"\n";
148             $exit_code = 1;
149             }
150             }
151             else {
152             if (${$self->warn}) {
153             $self->stack_test(qq(fail "No tests were found in your input file.\n"));
154             $exit_code = 1;
155             }
156             }
157              
158             if (${$self->generate}) {
159             print $code,"\n";
160             }
161              
162             return $exit_code;
163             }
164              
165             # Read files from command line or standard input.
166             # Turn these from test specs into test code.
167             sub transform_test_specs {
168             my ($self) = @_;
169             local $_; ##no critic
170             while(defined($_ = $self->next_line)) { ##no critic
171             chomp;
172             # Discard comments.
173             /^\#/mx and next;
174              
175             # Discard blank lines.
176             /^\s*$/mx and next;
177              
178             # Handle pragmas.
179             /^%% # pragma signifier
180             \s* # optional space
181             (.*?) # arbitrary identifier
182             ( # either ...
183             (
184             (:\s+|\s+) # an optional colon, and whitespace
185             (.*)$ # then a optional value
186             )| # ... or
187             $ # nothing at all
188             )/mx and do {
189             if (my $code = $self->pragma($1)) {
190             $code->($self,$5);
191             }
192             else {
193             # It's a substitution if it has no other meaning.
194             if (defined $5) {
195             my $var = $1;
196             # Check if the variable name needs to be substituted.
197             if ($var =~ /$in_or_out_bracketed/) {
198             next if $self->_queue_substituted_lines($_);
199             }
200             my @data = $self->expand_backticked($5);
201             my ($status, $message) = $self->_check_dependencies($var, @data);
202             if ($status) {
203             $self->_substitution_data($var, @data);
204             }
205             else {
206             my @items = split $message;
207             my $between = (@items < 3) ? 'between' : 'among';
208             $self->stack_test( qw(fail "Cannot add substitution for $var: dependency loop $between $message";\n));
209             }
210             }
211             }
212             next;
213             };
214              
215             # Commit any substitutions.
216             # We use 'next' because the substituted lines
217             # will have been queued on the input if there
218             # where any substitutions.
219             #
220             # We do this *after* pragma processing because
221             # if we have this:
222             # %%foo bar baz
223             # %%quux is the value
224             #
225             # and we expanded pragmas in place, we'd get
226             # %%foo bar baz
227             # %%quux bar
228             # %%quux baz
229             #
230             # which is probably *not* what is wanted.
231             # Putting this here makes sure that we only
232             # substitute into actual test specs.
233             next if $self->_queue_substituted_lines($_);
234              
235             # No substitutions in this line, so just process it.
236             my $item = App::SimpleScan::TestSpec->new($_);
237              
238             # Store it in case a plugin needs to look at the
239             # test spec in an overriding method.
240             $self->set_current_spec($item);
241              
242             if ($item->syntax_error) {
243             $self->stack_code(<<"END_MSG");
244             # @{[$item->raw]}
245             # Possible syntax error in this test spec
246             END_MSG
247             }
248             else {
249             $item->as_tests;
250             local $_ = $item ->raw; ##no critic
251             s/\n//mx;
252             }
253             # Drop the spec (there isn't one active now).
254             $self->set_current_spec();
255             }
256             return;
257             }
258              
259             # Calls each plugin's test_modules method
260             # to stack any other test modules needed to
261             # properly handle the test code. (Plugins may
262             # want to generate test code that needs
263             # something like Test::Differences, etc. -
264             # this lets them load that module so the
265             # tests actually work.)
266             #
267             # Also adds the test plan.
268             #
269             # Finally, initializes the user agent (unless
270             # we're specifically directed *not* to do so).
271             sub finalize_tests {
272             my ($self) = @_;
273             my @tests = @{$self->tests};
274             my @prepends;
275             foreach my $plugin (__PACKAGE__->plugins) {
276             if ($plugin->can('test_modules')) {
277             foreach my $module ($plugin->test_modules) {
278             push @prepends, "use $module;\n";
279             }
280             }
281             }
282             # Handle conditional user agent initialization.
283             # This was added because some servers (e.g., WAP
284             # servers) refuse connections from known user agents,
285             # but others (e.g., Yahoo!'s web servers) refuse
286             # login attempts from non-browser user agents.
287             #
288             # Set the user agent unless --no-agent was given.
289              
290             if (!$self->no_agent) {
291             push @prepends, qq(mech->agent_alias("Windows IE 6");\n);
292             }
293            
294             # Add the boilerplate testing stuff.
295             unshift @prepends,
296             (
297             "use Test::More tests=>@{[$self->test_count]};\n",
298             "use Test::WWW::Simple;\n",
299             "use strict;\n",
300             "\n",
301             );
302            
303            
304             $self->tests( [ @prepends, @tests ] );
305             return;
306             }
307              
308             #######################
309             # External utility methods.
310              
311             # Handle backticked values in substitutions.
312             sub expand_backticked {
313             use re 'eval';
314              
315             my ($self, $text) = @_;
316              
317             # The state machine was a really cool idea, except it didn't work. :-P
318             # A little reading in Mastering Regular Expressions gave me the patterns
319             # shown below for matching quoted strings.
320              
321             # For an explanation of why this works, see Friedl, p. 262 ff.
322             # It's called "unrolling" the regex there.
323              
324             # Pattern: quote (nonspecial)*(escape anything (nonspecial)*)* quote
325             my $qregex = qr/'[^'\\]*(?:\\.[^'\\]*)*'/;
326             my $qqregex = qr/"[^"\\]*(?:\\.[^"\\]*)*"/;
327             my $qxregex = qr/`[^`\\]*(?:\\.[^`\\]*)*`/;
328              
329             # Plus we need the cleanup tokenizer: match anything nonblank. This
330             # picks up tokens that are not properly-balanced quoted strings.
331             # We'll trap those later, or not. We'll see.
332             my $cleanup = qr/\S+/;
333              
334             # An item is a quoted string of any flavor, or the cleanup item.
335             # We turn on the capturing parens here because it we match an item,
336             # we want it.
337             my $item = qr/($qqregex|$qregex|$qxregex|$cleanup)/;
338              
339             # So now, to extract the items, we just match this with /g against the
340             # incoming text. We know already this is a single line, so we don't need
341             # any other switches. Boy, that's simpler.
342             my @data = ($text =~ /$item/g);
343              
344             # Evaluate the tokens.
345             my @result;
346             local $_;
347             for (@data) {
348             next unless defined;
349              
350             # Backticked: eval and process again.
351             if (/^\`(.*)`$/mx) {
352             push @result, $self->expand_backticked(eval $_); ##no critic
353             }
354             # Double-quoted: eval it.
355             elsif (/^"(.*)"$/mx) {
356             my $to_be_evaled = $1;
357             my @substituted = $self->sub_engine->expand($to_be_evaled);
358             push @result, map { eval $_ } @substituted; ##no critic
359             }
360             # Single-quoted: remove quotes.
361             elsif (/^'(.*)'$/mx) {
362             push @result, $1;
363             }
364             # Just an unquoted token. Save it.
365             else {
366             push @result, $_;
367             }
368             }
369             return @result;
370             }
371              
372             sub set_current_spec {
373             my ($self, $testspec) = @_;
374             $self->{CurrentTestSpec} = $testspec;
375             return $testspec;
376             }
377              
378             sub get_current_spec {
379             my ($self) = @_;
380             return $self->{CurrentTestSpec};
381             }
382              
383             # If there are any substitutions, build them, stack them on the input,
384             # and return true. Otherwise, just return false so the line will be passed on.
385             sub _queue_substituted_lines {
386             my ($self, $line) = @_;
387             my @results = $self->sub_engine->expand($line);
388              
389             if (@results != 1) {
390             # substitutions definitely happened
391             $self->queue_lines(@results);
392             return 1;
393             }
394             elsif ($results[0] ne $line) {
395             # single line is different, so substitution(s) happened
396             $self->queue_lines(@results);
397             return 1;
398             }
399             else {
400             # nothing happened, just process it as is
401             return 0;
402             }
403             }
404              
405             # Wrapper function for setter/getter that implements the
406             # override function (command-line substitutions override
407             # substitutions in the input).
408             sub _substitution_data {
409             my ($self, $pragma_name, @pragma_values) = @_;
410             if (! defined $pragma_name) {
411             croak 'No pragma specified';
412             }
413              
414             if (@pragma_values) {
415             if (${$self->override} and
416             $self->{Predefs}->{$pragma_name}) {
417             if (${$self->debug}) {
418             $self->stack_code(qq(diag "Substitution $pragma_name not altered to '@pragma_values'";\n));
419             }
420             }
421             else {
422             $self->sub_engine->substitution_value($pragma_name, @pragma_values);
423             }
424             }
425             else {
426             $self->sub_engine->substitution_value($pragma_name);
427             }
428             return
429             wantarray ? @{$self->sub_engine->dictionary->{$pragma_name}}
430             : $self->sub_engine->dictionary->{$pragma_name};
431             }
432              
433             sub _delete_substitution {
434             my ($self, $substitution) = @_;
435             return $self->sub_engine->delete_substitution($substitution);
436             }
437              
438             ########################
439             # Options methods
440              
441             sub handle_options {
442             my ($self) = @_;
443              
444             # Handle options, including ones from the plugins.
445             $self->install_options(%basic_options);
446              
447             # The --define option has to be handled slightly differently.
448             # We set things up so that we have a hash of predefined variables
449             # in the object; that way, we can set them up appropriately, and
450             # know whether or not they should be checked for override/defer
451             # when a definition is found in the simple_scan input file.
452             $self->{Options}->{'define=s%'} = ($self->{Predefs} = {});
453              
454             foreach my $plugin (__PACKAGE__->plugins) {
455             if ($plugin->can('options')) {
456             $self->install_options($plugin->options);
457             }
458             }
459              
460             $self->parse_command_line;
461              
462             foreach my $plugin (__PACKAGE__->plugins) {
463             if ($plugin->can('validate_options')) {
464             $plugin->validate_options($self);
465             }
466             }
467              
468             # If anything was predefined, save it in the substitutions.
469             for my $def (keys %{$self->{Predefs}}) {
470             $self->sub_engine->substitution_value($def,
471             (split /\s+/mx, $self->{Predefs}->{$def}));
472             }
473              
474             if (${$self->no_agent}) {
475             $self->sub_engine->substitution_value('agent', 'WWW::Mechanize::Pluggable');
476             }
477             else {
478             $self->sub_engine->substitution_value('agent', 'Windows IE 6');
479             $self->stack_code("mech->agent_alias('Windows IE 6');\n");
480             }
481            
482             $self->app_defaults;
483             return;
484             }
485              
486             # Set up application defaults.
487             sub app_defaults {
488             my ($self) = @_;
489             # Assume --run if neither --run nor --generate.
490             if (!defined ${$self->generate()} and
491             !defined ${$self->run()}) {
492             $self->run(\1);
493             }
494              
495             # Assume --defer if neither --defer nor --override.
496             if (!defined ${$self->defer()} and
497             !defined ${$self->override()}) {
498             $self->defer(\1);
499             }
500              
501             # if --cache was supplied, turn caching on.
502             if (${$self->autocache}) {
503             $self->stack_code(qq(cache;\n));
504             }
505              
506             return;
507             }
508              
509             # Transform the options specs (whether from here or
510             # from plugins) into methods that we can call to
511             # set/get the option values.
512             sub install_options {
513             my ($self, @options) = @_;
514             if (! defined $self->{Options}) {
515             $self->{Options} = {};
516             }
517              
518             # precompilation versions of the possible methods. These
519             # get compiled right when we need them, causing $option
520             # to be capturd as a closure.
521              
522             while (@options) {
523             # This coding is deliberate.
524             #
525             # We want a separate copy of $option and
526             # $receiver each time; we don't want a new
527             # copy of @options, because we want to keep
528             # effectively shifting two values off each
529             # time around the loop.
530             #
531             # Note that the generated method returns a
532             # reference to the variable that the option is
533             # stored into; this makes it simpler to code
534             # the accessor here. If you use an array or
535             # hash to receive values in your Getopt spec,
536             # you'll have to dereference it properly in
537             # your code.
538             (my($option, $receiver), @options) = @options;
539              
540             # Method names containing dashes are a no-no;
541             # swap them to underscores. (This is okay because
542             # no one outside this module should be trying to
543             # call these methods directly.)
544             $option =~ s/-/_/mxg;
545              
546             $self->{Options}->{$option} = $receiver;
547              
548             # Ensure that the variables have been cleared if we create another
549             # App::SimpleScan object (normally we won't, but our tests do).
550             ${ $receiver } = undef;
551              
552             # Create method if it doesn't exist.
553             if (! $self->can($option)) {
554             use Sub::Installer;
555             __PACKAGE__->install_sub(
556             { $option => sub {
557             my ($self, $value) = @_;
558             if (defined $value) {
559             $self->{Options}->{$option} = $value;
560             }
561             return $self->{Options}->{$option};
562             }
563             }
564             );
565             }
566             }
567             return;
568             }
569              
570             # Load all the plugins.
571             sub _load_plugins {
572             my($self) = @_;
573              
574             # Load plugins.
575             foreach my $plugin (__PACKAGE__->plugins) {
576             eval "use $plugin"; ##no critic
577             $EVAL_ERROR and die "Plugin $plugin failed to load: $EVAL_ERROR\n";
578             }
579              
580             # Install source filters
581             $self->{Filters} = [];
582             foreach my $plugin (__PACKAGE__->plugins) {
583             if ($plugin->can('filters')) {
584             push @{$self->{Filters}}, $plugin->filters();
585             }
586             # Initialize plugin data if possible.
587             if ($plugin->can('init')) {
588             $plugin->init($self);
589             }
590             }
591              
592             return;
593             }
594              
595             # Call Getopt::Long to parse the command line.
596             sub parse_command_line {
597             my ($self) = @_;
598             return GetOptions(%{$self->{Options}});
599             }
600              
601             # Install any pragmas supplied by plugins.
602             # We reuse this same code to install all of
603             # the locally defined pragmas.
604             sub install_pragma_plugins {
605             my ($self) = @_;
606              
607             foreach my $plugin (@local_pragma_support,
608             __PACKAGE__->plugins) {
609             if (ref $plugin eq 'ARRAY') {
610             $self->pragma(@{ $plugin });
611             }
612             elsif ($plugin->can('pragmas')) {
613             foreach my $pragma_spec ($plugin->pragmas) {
614             $self->pragma(@{ $pragma_spec });
615             }
616             }
617             }
618             return;
619             }
620              
621             ########################
622             # Pragma methods and handlers
623              
624             # Find the pragma code associated with the name.
625             sub pragma {
626             my ($self, $name, $pragma) = @_;
627             die "You forgot the pragma name\n" if ! defined $name;
628             if (defined $pragma) {
629             $self->{Pragma}->{$name} = $pragma;
630             }
631             return $self->{Pragma}->{$name};
632             }
633              
634             # %%agent pragma handler. Verify that the argument
635             # is a valid WW::Mechanize agent alias string, and
636             # stack code to change it as appropriate.
637             sub _do_agent {
638             my ($self, $rest) = @_;
639             $rest = reverse $rest;
640             my ($maybe_agent) = ($rest =~/^\s*(.*)$/mx);
641            
642             $maybe_agent = reverse $maybe_agent;
643             if (grep { $_ eq $maybe_agent } $reference_mech->known_agent_aliases) {
644             $self->_substitution_data('agent', $maybe_agent)
645             }
646             $self->stack_code(qq(user_agent("$maybe_agent");\n));
647             return;
648             }
649              
650             # %%cache - turn on Test::WWW::Simple's cache.
651             sub _do_cache {
652             my ($self,$rest) = @_;
653             $self->stack_code("cache();\n");
654             return;
655             }
656              
657             # %%nocache - turn off Test::WWW::Simple's cache.
658             sub _do_nocache {
659             my ($self,$rest) = @_;
660             $self->stack_code("no_cache();\n");
661             return;
662             }
663              
664             ##########################
665             # Input queueing
666              
667             # Handle input queueing. If there's anything queued,
668             # return it first; otherwise, just read another line
669             # from the magic input filehandle.
670             sub next_line {
671             my ($self) = shift;
672             my $next_line;
673              
674             # Call and plugin-installed input callbacks.
675             # These can do whatever they like to the line stack, the
676             # object, etc.
677             foreach my $callback (@ {$self->next_line_callbacks() }) {
678             $callback->($self);
679             }
680              
681             # If we have lines on the input queue, read from there.
682             if (defined $self->{InputQueue}->[0] ) {
683             $next_line = shift @{ $self->{InputQueue} };
684             }
685              
686             # Else we read lines from the standard input.
687             else {
688             $next_line = <>;
689             if (defined $next_line) {
690             $next_line =~ s/\n//mx;
691             if ($run_status) {
692             print STDERR "# |Processing '$next_line' (line $.)\n";
693             }
694             }
695             }
696              
697             # record the text of the last line read for plugins to access
698             # if they need it.
699             $self->last_line($next_line);
700              
701             return $next_line;
702             }
703              
704             # Preserve current line so that plugins can look at it
705             # if they want to.
706             sub last_line {
707             my ($self, $line) = @_;
708             if (defined $line) {
709             $self->{CurrentLine} = $line;
710             }
711             return $self->{CurrentLine};
712             }
713              
714             # Handle input stacking by pragmas. Add any new lines
715             # to the head of the queue.
716             sub queue_lines {
717             my ($self, @lines) = @_;
718             $self->{InputQueue} = [ @lines, @{ $self->{InputQueue} } ];
719             return;
720             }
721              
722             ###########################
723             # Output queueing
724              
725             # stack_code just adds code to the array holding
726             # the generated program.
727             sub stack_code {
728             my ($self, @code) = @_;
729             my @old_code = @{$self->tests};
730             $self->tests([@old_code, @code]);
731             return
732             }
733              
734             # stack_test adds code to the array holding
735             # the generated program, and bumps the test
736             # count so we can use the proper number of tests
737             # in our test plan.
738             sub stack_test {
739             my($self, @code) = @_;
740             for my $filter (@{$self->{Filters}}) {
741             # Called with $self to make it appear
742             # as if it's a method call from this package.
743             @code = $filter->($self, @code);
744             }
745             $self->stack_code(@code);
746             return $self->test_count($self->test_count()+1);
747             }
748              
749             ##################################
750             # Dependency checking
751              
752             # It's necessary to make sure that the substitution pragmas
753             # don't have looping dependencies; these would cause the
754             # input stack to grow without limit as it tries to resolve
755             # all of the substitutions.
756             #
757             # All we have to do is make sure that the graph of variable
758             # relations is a directed acyclic graph. Since actually writing
759             # all that would be a pain, we use Graph.pm to manage it for us.
760              
761             sub _check_dependencies {
762             my ($self, $var, @dependencies) = @_;
763             my $graph = $self->_deps;
764              
765             # drop anything that's not a variable definition.
766             @dependencies = grep { /^<.*>$/mx } @dependencies;
767              
768             # No variables, no dependencies.
769             if (! @dependencies) {
770             return 1, 'no dependencies';
771             }
772              
773             # Add the new dependencies.
774             $self->_depend($var, @dependencies);
775            
776             # Run a topological sort to make sure that the
777             # graph remains a DAG. If not, returns a cycle.
778             # Note that it's possible that there's more than one
779             # cycle, though not liekely, since we're checking
780             # every time we add a new variable.
781             unless ($graph->is_dag) {
782             return (0, $graph->find_a_cycle);
783             }
784              
785             return 1, "dependencies OK";
786             }
787              
788             sub _depend {
789             my($self, $item, @dependencies) = @_;
790              
791             if (!defined $item) {
792             die "You don't want to do that anymore";
793             }
794              
795             if (!@dependencies) {
796             return ([ $self->_deps->successors($item) ]);
797             }
798              
799             # Add these dependencies for the item.
800             $self->_deps->add_edge($item, $_) for @dependencies; ## no critic
801             return;
802             }
803              
804             1; # Magic true value required at end of module
805             __END__