File Coverage

blib/lib/Marmoset.pm
Criterion Covered Total %
statement 169 184 91.8
branch 38 64 59.3
condition 19 37 51.3
subroutine 31 35 88.5
pod 1 1 100.0
total 258 321 80.3


line stmt bran cond sub pod time code
1 2     2   330286 use 5.008003;
  2         8  
  2         88  
2 2     2   11 use strict qw( subs vars );
  2         4  
  2         63  
3 2     2   10 use warnings;
  2         9  
  2         72  
4 2     2   10 no warnings qw( once uninitialized );
  2         3  
  2         330  
5              
6             package Marmoset;
7              
8             our $AUTHORITY = 'cpan:TOBYINK';
9             our $VERSION = '0.000_01';
10              
11             our (%ATTRIBUTES, %IS_FINAL, %OFFSETS, %SIZES);
12              
13 8     8   15 sub _class_for_objects { 'Marmoset::Object' }
14 4     4   43 sub _class_for_fields { 'Marmoset::Attribute::Field' }
15 1     1   2 sub _class_for_attributes { 'Marmoset::Attribute::InsideOut' }
16              
17 2     2   11 use B qw();
  2         3  
  2         36  
18 2     2   10 use Carp qw(croak);
  2         4  
  2         138  
19 2     2   2333 use Eval::TypeTiny qw(eval_closure);
  2         2429  
  2         11  
20 2     2   2204 use Exporter::Shiny our(@EXPORT) = qw( has extends );
  2         933  
  2         15  
21 2     2   6113 use Sub::Accessor::Small 0.008 ();
  2         67252  
  2         119  
22              
23             BEGIN {
24 2 50   2   24 ($] >= 5.010) ? do { require mro } : do { require MRO::Compat };
  2         11015  
  0         0  
25             }
26              
27             sub _exporter_validate_opts
28             {
29 4     4   528 my $me = shift;
30 4         6 my ($globals) = @_;
31 4         9 my $caller = $globals->{into};
32            
33 4 50       15 ref($caller)
34             and croak("Cannot import Marmoset into a reference; bailing out");
35            
36 4         15 @{"$caller\::ISA"} = $me->_class_for_objects;
  4         86  
37             }
38              
39             sub _generate_has
40             {
41 4     4   345 my $me = shift;
42 4         7 my ($name, undef, $globals) = @_;
43 4         9 my $caller = $globals->{into};
44            
45             return sub
46             {
47 6 100   6   33282 $IS_FINAL{$caller}
48             and croak("$caller has already been instantiated; cannot add attributes; bailing out");
49            
50 5         35 my ($name, %opts) = @_;
51 5 100       39 my $attr_class = exists($opts{pack})
52             ? $me->_class_for_fields
53             : $me->_class_for_attributes;
54            
55 5         73 $attr_class->has($globals, $name, %opts);
56 4         39 };
57             }
58              
59             sub _generate_extends
60             {
61 4     4   254 my $me = shift;
62 4         9 my ($name, undef, $globals) = @_;
63 4         6 my $caller = $globals->{into};
64            
65 4         14 my $need = $me->_class_for_objects;
66            
67             return sub
68             {
69 1 50   1   8 $IS_FINAL{$caller}
70             and croak("$caller has already been instantiated; cannot change inheritance; bailing out");
71            
72 1         4 my @parents = @_;
73             $_->isa($need)
74             or croak("Cannot inherit from $need; it is not a $me class (does not inherit from $need); bailing out")
75 1   33     21 for @parents;
76 1         2 @{"$caller\::ISA"} = @parents;
  1         35  
77 4         32 };
78             }
79              
80             sub make_immutable
81             {
82 4     4 1 9 my $me = shift;
83 4         9 my ($class) = @_;
84 4         7 $IS_FINAL{$_} = 1 for @{mro::get_linear_isa($class)};
  4         53  
85 4         20 $me->_initialize_slots($class);
86 4         20 return $me->_build_constructor($class);
87             }
88              
89             sub _initialize_slots
90             {
91 4     4   10 my $me = shift;
92 4         7 my ($class) = @_;
93            
94 10 100       71 my @fields =
95             grep exists($_->{pack}),
96 4         17 map @{ $ATTRIBUTES{$_} or [] },
97 4         8 @{mro::get_linear_isa($class)};
98            
99 4         10 my ($n, @sizes, @offsets) = 0;
100             $sizes[@sizes] = $_,
101             $offsets[@offsets] = $n,
102             $n += $_
103 4         57 for map length(pack $_->{pack}, 0), @fields;
104            
105 4         22 for my $i (0 .. $#fields)
106             {
107 6         13 local $Marmoset::REALLY_INSTALL = 1;
108 6         11 my $f = $fields[$i];
109            
110 6         20 (my($tmp), $f->{package}) = ($f->{package}, $class);
111 6         20 ($f->{OFFSET}, $f->{SIZE}) = ($offsets[$i], $sizes[$i]);
112            
113 6         17 $f->install_accessors;
114            
115 6         2331 delete($f->{$_}) for qw( OFFSET SIZE );
116 6         23 $f->{package} = $tmp;
117             }
118             }
119              
120             sub _build_constructor
121             {
122 4     4   8 my $me = shift;
123 4         6 my ($class) = @_;
124            
125 4         6 my @isa = @{mro::get_linear_isa($class)};
  4         18  
126 4 100       9 my @attr = map @{ $ATTRIBUTES{$_} or [] }, @isa;
  10         44  
127 4         13 my @fields = grep exists($_->{pack}), @attr;
128 4         36 my $template = B::perlstring( join q[ ], map $_->{pack}, @fields );
129 4         14 my @others = grep !exists($_->{pack}), @attr;
130 10         28 my @build_methods =
131             map "$_\::BUILD",
132 4         10 grep exists(&{"$_\::BUILD"}),
133             @isa;
134 7 100       35 my %defaults = map {
135 4         9 exists($_->{default}) ? ($_->{slot}, $_->{default}) : ()
136             } @attr;
137 7 50       21 my %triggers = map {
138 4         10 exists($_->{trigger}) ? ($_->{slot}, $_->{trigger}) : ()
139             } @attr;
140 7 100       23 my %types = map {
141 4         9 exists($_->{isa}) ? ($_->{slot}, $_->{isa}) : ()
142             } @attr;
143            
144 4         7 my @code;
145 4         7 push @code, 'sub {';
146 4         7 push @code, ' my $class = shift;';
147 4         19 push @code, ' if ($class ne '.B::perlstring($class).') {';
148 4         7 push @code, ' my $new = "Marmoset"->make_immutable($class);';
149 4         9 push @code, ' goto $new;';
150 4         6 push @code, ' }';
151 4         7 push @code, ' my $str = "";';
152 4         7 push @code, ' my $self = \$str;';
153 4         7 push @code, ' bless $self, $class;';
154 4         6 push @code, ' my $params = $class->BUILDARGS(@_);';
155 4         8 push @code, ' my (@f, $tmp);';
156 4         8 for my $f (@fields) {
157 6         34 push @code, ' if (exists($params->{'.B::perlstring($f->{init_arg}).'})) {';
158 6         25 push @code, ' $$tmp = $params->{'.B::perlstring($f->{init_arg}).'};';
159 6 50       19 push @code, ' $triggers{'.B::perlstring($f->{slot}).'}->($self, $params->{'.B::perlstring($f->{init_arg}).'});'
160             if $triggers{$f->{slot}};
161 6         10 push @code, ' }';
162 6         8 push @code, ' else {';
163 6 100 66     47 if ($f->{default} and not $f->{lazy}) {
    50 33        
164 5         25 push @code, ' $$tmp = scalar($defaults{'.B::perlstring($f->{slot}).'}->($self));';
165             }
166             elsif ($f->{builder} and not $f->{lazy}) {
167 0         0 push @code, ' $$tmp = scalar($self->'.$f->{builder}.'());';
168             }
169             else {
170 1         6 push @code, ' Carp::croak(sprintf "Required parameter \\"%s\\" not supplied to constructor; stopped", '.B::perlstring($f->{init_arg}).');';
171             }
172 6         12 push @code, ' }';
173            
174 6 100       23 if (my $type = $f->{isa})
175             {
176 1 50       9 if ($f->{coerce})
177             {
178 1 50       15 push @code, ' $$tmp = '.(
179             $type->coercion->can_be_inlined
180             ? $type->coercion->inline_coercion('${$tmp}')
181             : '$types{'.B::perlstring($f->{slot}).'}->coerce($$tmp)'
182             ).';';
183             }
184 1 50       203 push @code, ' '.(
185             $type->can_be_inlined
186             ? $type->inline_assert('${$tmp}')
187             : ('$types{'.B::perlstring($f->{slot}).'}->assert_value($$tmp)')
188             ).';';
189             }
190            
191 6         128 push @code, ' push @f, $$tmp;';
192 6         18 push @code, ' undef $tmp;';
193             }
194 4         10 push @code, ' $str = pack('.$template.', @f);';
195 4 100       11 push @code, ' @_ = $self;' if @others;
196 4         9 for my $f (@others) {
197 1         7 push @code, ' if (exists($params->{'.B::perlstring($f->{init_arg}).'})) {';
198 1         6 push @code, ' $$tmp = $params->{'.B::perlstring($f->{init_arg}).'};';
199 1 50       5 push @code, ' $triggers{'.B::perlstring($f->{slot}).'}->($self, $params->{'.B::perlstring($f->{init_arg}).'});'
200             if $triggers{$f->{slot}};
201 1         2 push @code, ' }';
202 1         3 push @code, ' else {';
203 1 50 33     28 if ($f->{default} and not $f->{lazy}) {
    50 33        
    50          
204 0         0 push @code, ' $$tmp = scalar($defaults{'.B::perlstring($f->{slot}).'}->($self));';
205             }
206             elsif ($f->{builder} and not $f->{lazy}) {
207 0         0 push @code, ' $$tmp = scalar($self->'.$f->{builder}.'());';
208             }
209             elsif ($f->{required}) {
210 0         0 push @code, ' Carp::croak(sprintf "Required parameter \\"%s\\" not supplied to constructor; stopped", '.B::perlstring($f->{init_arg}).');';
211             }
212             else {
213 1         4 push @code, ' ();';
214             }
215 1         7 push @code, ' }';
216            
217 1 50       5 if (my $type = $f->{isa})
218             {
219 0         0 push @code, ' if ($tmp) {';
220 0 0       0 if ($f->{coerce})
221             {
222 0 0       0 push @code, ' $$tmp = '.(
223             $type->coercion->can_be_inlined
224             ? $type->coercion->inline_coercion('${$tmp}')
225             : '$types{'.B::perlstring($f->{slot}).'}->coerce($$tmp)'
226             ).';';
227             }
228 0 0       0 push @code, ' '.(
229             $type->can_be_inlined
230             ? $type->inline_assert('${$tmp}')
231             : ('$types{'.B::perlstring($f->{slot}).'}->assert_value($$tmp)')
232             ).';';
233 0         0 push @code, ' }';
234             }
235            
236 1         7 push @code, ' '.$f->inline_access.' = $$tmp if $tmp;';
237 1         11 push @code, ' undef $tmp;';
238             }
239 4         12 push @code, " \$self->$_\(\$params);" for reverse @build_methods;
240 4         8 push @code, ' $self;';
241 4         7 push @code, '}';
242            
243 4         47 my $coderef = eval_closure(
244             source => join("\n", @code),
245             environment => {
246             '%defaults' => \%defaults,
247             '%triggers' => \%triggers,
248             '%types' => \%types,
249             },
250             );
251            
252 4 50       1766 *{"$class\::new"} = Marmoset::Attribute->HAS_SUB_NAME
  4         19  
253             ? Sub::Name::subname("$class\::new", $coderef)
254             : $coderef;
255 4         25 return $coderef;
256             }
257              
258             {
259             package Marmoset::Object;
260             our $AUTHORITY = 'cpan:TOBYINK';
261             our $VERSION = '0.000_01';
262            
263             sub new
264             {
265 4     4   19065 my ($class) = @_;
266 4         22 my $new = 'Marmoset'->make_immutable($class);
267 4         98 goto $new;
268             }
269            
270             sub BUILDARGS
271             {
272 9     9   40473 shift;
273             +{
274 9 50 33     236 (@_==1 and ref($_[0]) eq q(HASH)) ? %{$_[0]} : @_
  0         0  
275             };
276             }
277            
278 0     0   0 sub DESTROY { return; }
279             }
280              
281             {
282             package Marmoset::Attribute;
283             our $AUTHORITY = 'cpan:TOBYINK';
284             our $VERSION = '0.000_01';
285             our @ISA = qw(Sub::Accessor::Small);
286            
287 0     0   0 sub accessor_kind { 'Marmoset' }
288            
289             sub canonicalize_opts
290             {
291 5     5   40 my $me = shift;
292            
293 5         29 $me->SUPER::canonicalize_opts(@_);
294 5 50       377 $me->{init_arg} = $me->{slot} unless exists $me->{init_arg};
295            
296             # Save options
297 5   100     8 push @{ $ATTRIBUTES{$me->{package}} ||= [] }, $me;
  5         34  
298             }
299             }
300              
301             {
302             package Marmoset::Attribute::Field;
303             our $AUTHORITY = 'cpan:TOBYINK';
304             our $VERSION = '0.000_01';
305             our @ISA = qw(Marmoset::Attribute);
306            
307 2     2   6260 use Carp qw(croak);
  2         4  
  2         1021  
308            
309 6     6   66 sub accessor_kind { 'Marmoset packed' }
310            
311             sub canonicalize_opts
312             {
313 4     4   139 my $me = shift;
314 4         20 $me->SUPER::canonicalize_opts(@_);
315            
316 4 0 66     23 croak "Attribute '$me->{slot}' is a field, therefore must be required or have a default; bailing out"
      33        
      33        
317             if exists($me->{required})
318             && !$me->{required}
319             && !$me->{builder}
320             && !$me->{default};
321            
322 4 50       17 croak "Attribute '$me->{slot}' is a field, therefore cannot be cleared; bailing out"
323             if defined($me->{clearer});
324             }
325            
326             sub inline_clearer
327             {
328 0     0   0 croak "This class cannot generate a clearer; bailing out";
329             }
330            
331             sub inline_predicate
332             {
333 0     0   0 return q[ 1 ];
334             }
335            
336             sub inline_access
337             {
338 7     7   163 my $me = shift;
339            
340 7   100     70 sprintf(
      50        
341             'unpack(q(%s), substr(${$_[0]}, %d, %d))',
342             $me->{pack},
343             $me->{OFFSET} || 0,
344             $me->{SIZE} || 0,
345             );
346             }
347            
348             sub inline_access_w
349             {
350 3     3   451 my $me = shift;
351 3         6 my ($expr) = @_;
352            
353 3   100     48 sprintf(
      50        
354             'substr(${$_[0]}, %d, %d) = pack(q(%s), %s)',
355             $me->{OFFSET} || 0,
356             $me->{SIZE} || 0,
357             $me->{pack},
358             $expr,
359             );
360             }
361            
362             sub install_accessors
363             {
364 10 100   10   65 return unless $Marmoset::REALLY_INSTALL;
365 6         39 shift->SUPER::install_accessors(@_);
366             }
367             }
368              
369             {
370             package Marmoset::Attribute::InsideOut;
371             our $AUTHORITY = 'cpan:TOBYINK';
372             our $VERSION = '0.000_01';
373             our @ISA = qw(Marmoset::Attribute);
374            
375 2     2   514 sub accessor_kind { 'Marmoset inside-out' }
376             }
377              
378              
379             1;
380              
381             __END__
382              
383             =pod
384              
385             =encoding utf-8
386              
387             =for stopwords featureful
388              
389             =head1 NAME
390              
391             Marmoset - class builder for memory-efficient objects
392              
393             =head1 SYNOPSIS
394              
395             use v5.14;
396             use warnings;
397            
398             package MyClass {
399             use Marmoset;
400             has id => (is => 'ro', pack => 'L');
401             has name => (is => 'rw', pack => 'Z32');
402             }
403            
404             my $obj = MyClass->new(id => 42, name => "The Answer");
405              
406             =head1 DESCRIPTION
407              
408             =begin html
409              
410             <p
411             id="#eating-marmoset-ii"
412             style="float:right">
413             <a
414             href="http://www.flickr.com/photos/tambako/10655212644/"
415             title="Eating marmoset II by Tambako the Jaguar, on Flickr">
416             <img
417             alt="Eating marmoset II"
418             src="http://farm4.staticflickr.com/3834/10655212644_ae115ce604_n.jpg"
419             width="213" height="320">
420             </a>
421             </p>
422              
423             =end html
424              
425             B<Marmoset> is a slightly less featureful version of class builders
426             like L<Moose>, L<Mouse>, and L<Moo>, designed for efficient memory
427             usage when you need to deal with many thousands of very simple objects.
428              
429             Attributes are stored using a variation on the C<pack>/C<unpack> shown
430             by L<BrowserUK|http://www.perlmonks.org/?node_id=171588> on PerlMonks
431             at L<http://www.perlmonks.org/?node_id=1040313>.
432              
433             However, inside-out attributes are also offered for data which cannot
434             be reasonably serialized to a string.
435              
436             =head2 Keywords provided by Marmoset
437              
438             =over
439              
440             =item C<< extends(@classes) >>
441              
442             Set inheritance for the current class. Currently you may only inherit
443             from other Marmoset classes.
444              
445             If you don't specify a class to inherit from, your class will inherit
446             from Marmoset::Object. (See L</"Methods provided by Marmoset::Object">.)
447              
448             =item C<< has $attribute => %specification >>
449              
450             Creates an attribute for your class, using a Moose-like specification.
451             There is a convention that attributes named with a leading underscore
452             are undocumented, unsupported or "private". (see L<Lexical::Accessor>
453             for true private attributes though.)
454              
455             The following keys are supported in the specification hash:
456              
457             =over
458              
459             =item C<< is => "ro"|"rw"|"rwp"|"lazy" >>
460              
461             Shortcuts for common patterns of accessors. As documented in
462             L<Moo> and L<MooseX::AttributeShortcuts>.
463              
464             =item C<< pack => $template >>
465              
466             The presence of this key in the specification makes your attribute
467             be stored in the object's packed string.
468              
469             Attributes suitable for packed storage include numbers and small
470             (especially fixed-length) strings.
471              
472             Templates are as defined in L<perlfunc/"pack">.
473              
474             =item C<< reader => $name|1 >>
475              
476             Specify the name for a read-only accessor method. Passing the
477             value "1" names the accessor "get_${attribute}" (for "private"
478             attributes, "_get${attribute}").
479              
480             =item C<< writer => $name|1 >>
481              
482             Specify the name for a write-only accessor method. Passing the
483             value "1" names the accessor "set_${attribute}" (for "private"
484             attributes, "_set${attribute}").
485              
486             =item C<< accessor => $name|1 >>
487              
488             Specify the name for a read/write accessor method. Passing the
489             value "1" names the accessor "${attribute}".
490              
491             =item C<< predicate => $name|1 >>
492              
493             Specify the name for a predicate method. Passing the value "1"
494             names the predicate "has_${attribute}" (for "private" attributes,
495             "_has${attribute}").
496              
497             For any attributes which are stored packed, it makes little
498             sense to define a predicate. The predicate will always return
499             true.
500              
501             =item C<< clearer => $name|1 >>
502              
503             Specify the name for a clearer method. Passing the value "1"
504             names the predicate "clear_${attribute}" (for "private" attributes,
505             "_clear${attribute}").
506              
507             For any attributes which are stored packed, it makes little
508             sense to define a clearer. The clearer will always throw an
509             exception.
510              
511             =item C<< trigger => $coderef|$name|1 >>
512              
513             A coderef to call after the value for the attribute has been set.
514             Alternatively, a method name may be supplied. Passing the value
515             "1" is equivalent to the method name "_trigger_${attribute}".
516              
517             =item C<< builder => $coderef|$name|1 >>
518              
519             A method name to call to build a default value for the attribute.
520             Passing the value "1" is equivalent to the method name
521             "_build_${attribute}". If a coderef is supplied, this will be
522             installed into the class as "_build_${attribute}".
523              
524             =item C<< default => $coderef|$nonref >>
525              
526             Similar to C<builder>, but the coderef will not be installed as
527             a class method. Non-reference values (i.e. undef, numbers, strings),
528             may be supplied as a simple value instead of a coderef.
529              
530             =item C<< isa => $constraint|$coderef|$typename >>
531              
532             A type constraint to validate values for the attribute. Any
533             constraint object which meets the L<Type::API::Constraint> specification
534             can be provided, including L<Type::Tiny>, L<MooseX::Types>,
535             L<MouseX::Types>, and L<Specio> type constraint objects.
536              
537             Alternatively a validation coderef may be provided, which must
538             return true to indicate a valid value, and either return false or
539             throw an exception to indicate an invalid one.
540              
541             If L<Type::Utils> is installed, this may be provided as a string,
542             which will be expanded to a type constraint object using C<dwim_type>.
543             (See L<Type::Utils/"dwim_type">.)
544              
545             =item C<< does => $role >>
546              
547             Shorthand for C<< isa => ConsumerOf[$role] >>.
548              
549             =item C<< coerce => $coercion|$coderef|0|1 >>
550              
551             Indicates whether type coercion should be attempted before validating
552             values.
553              
554             If an object meeting the L<Type::API::Constraint::Coercible>
555             specification has been provided for C<isa>, then the value "1" will
556             reuse any coercion attached to the type constraint object.
557              
558             Otherwise, an type coercion object (i.e. with a C<coerce> method) may
559             be provided, or a coderef which accepts a value and returns the coerced
560             value.
561              
562             =item C<< handles => $arrayref|$hashref >>
563              
564             Delegates methods to the attribute value.
565              
566             =item C<< weak_ref => 0|1 >>
567              
568             Indicates whether the attribute value should be weakened. Only makes
569             sense for attributes which are not stored packed.
570              
571             =item C<< init_arg => $name|undef >>
572              
573             The named constructor argument that will provide an initial value for
574             the attribute. If omitted, will default to $attribute.
575              
576             =item C<< required => 0|1 >>
577              
578             Indicates whether it is necessary to set the attribute when constructing
579             objects.
580              
581             Attributes which are stored packed I<must> be required unless they
582             provide a default/builder.
583              
584             =back
585              
586             =back
587              
588             =head2 Keywords NOT provided by Marmoset
589              
590             Unlike L<Moose>, L<Mouse>, and L<Moo>, Marmoset does not provide native
591             support for method modifiers or roles. Instead, it recommends the use of
592             L<Class::Method::Modifiers> and L<Role::Tiny> respectively.
593              
594             Note that Marmoset is sometimes forced to rebuild constructors and
595             accessors at run-time, which may lead to your method modifiers being
596             overwritten, if you have tried to apply any modifiers to them.
597              
598             It may be useful to force Marmoset to perform its rebuilding early; after
599             you've finished defining your class' inheritance and attributes, but
600             before applying any roles or method modifiers. To do this, call:
601              
602             Marmoset->make_immutable(__PACKAGE__);
603              
604             =begin trustme
605              
606             =item make_immutable
607              
608             =end trustme
609              
610             =head2 Methods provided by Marmoset::Object
611              
612             Marmoset::Object is your object's base class. It provides the
613             following methods:
614              
615             =over
616              
617             =item C<< new(%attributes) >>
618              
619             Your class' constructor.
620              
621             =item C<< BUILDARGS(@args) >>
622              
623             This is the proper way to alter incoming arguments to get them into a
624             format that Marmoset::Object's default constructor will recognize. This
625             class method is passed the list of constructor arguments as-is, and
626             expected to return a hashref of parameters which will be used to
627             initialize attributes.
628              
629             =item C<< BUILD($parameters) >>
630              
631             (Actually Marmoset::Object doesn't provide this, but your class may.)
632              
633             This is the proper way to perform any additional initialization on
634             your objects. It is called as an object method. If you're inheriting
635             from another Marmoset class, you I<< must not >> call
636             C<< $self->SUPER::BUILD(@_) >>. Marmoset will do that for you!
637              
638             =item C<< DESTROY >>
639              
640             TODO - not implemented
641              
642             =item C<< DEMOLISH >>
643              
644             TODO - not implemented
645              
646             =back
647              
648             =head1 BUGS
649              
650             Please report any bugs to
651             L<http://rt.cpan.org/Dist/Display.html?Queue=Marmoset>.
652              
653             =head1 SEE ALSO
654              
655             L<Moose>,
656             L<Moo>,
657             L<Mouse>,
658             L<Class::Tiny>.
659              
660             L<http://www.perlmonks.org/?node_id=1040313>.
661              
662             L<http://www.flickr.com/photos/tambako/10655212644/>.
663              
664             =head1 AUTHOR
665              
666             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
667              
668             =head1 COPYRIGHT AND LICENCE
669              
670             This software is copyright (c) 2014 by Toby Inkster.
671              
672             This is free software; you can redistribute it and/or modify it under
673             the same terms as the Perl 5 programming language system itself.
674              
675             =head1 DISCLAIMER OF WARRANTIES
676              
677             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
678             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
679             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
680