File Coverage

blib/lib/JOAP/Server/Object.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # JOAP::Server::Object -- Base Class for Things Servable By JOAP Servers
2             #
3             # Copyright (c) 2003, Evan Prodromou
4             #
5             # This library is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU Lesser General Public
7             # License as published by the Free Software Foundation; either
8             # version 2.1 of the License, or (at your option) any later version.
9             #
10             # This library is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             # Lesser General Public License for more details.
14             #
15             # You should have received a copy of the GNU Lesser General Public
16             # License along with this library; if not, write to the Free Software
17             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18              
19             # tag: JOAP server object base class
20              
21             package JOAP::Server::Object;
22 6     6   28042 use base qw/Exporter Class::Data::Inheritable/;
  6         12  
  6         7162  
23              
24 6     6   2646 use 5.008;
  6         22  
  6         231  
25 6     6   35 use strict;
  6         11  
  6         630  
26 6     6   38 use warnings;
  6         9  
  6         255  
27 6     6   5967 use Net::Jabber qw/Component/;
  0            
  0            
28             use JOAP;
29             use Error qw(:try);
30             use Symbol;
31              
32             our %EXPORT_TAGS = ( 'all' => [ qw// ] );
33             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
34             our @EXPORT = qw//;
35              
36             our $VERSION = $JOAP::VERSION;
37             our $AUTOLOAD;
38              
39             # Class data
40              
41             JOAP::Server::Object->mk_classdata('Description');
42             JOAP::Server::Object->mk_classdata('Attributes');
43             JOAP::Server::Object->mk_classdata('Methods');
44              
45             # Set these up -- default to none
46              
47             JOAP::Server::Object->Description(undef);
48             JOAP::Server::Object->Attributes({});
49             JOAP::Server::Object->Methods({});
50              
51             # Simple, straightforward constructor. I think.
52              
53             sub new {
54              
55             my($proto) = shift;
56             my($class) = ref($proto) || $proto;
57             my($self) = {};
58              
59             bless($self, $class);
60              
61             %$self = (ref($proto)) ? %$proto : @_;
62              
63             return $self;
64             }
65              
66             # Translucent accessor. If this is an object, and there are attributes
67             # defined for the object, use those. Otherwise, return the class's
68             # attribute descriptor hash.
69             # For classes, just passes through to Attributes.
70              
71             sub attributes {
72              
73             my($self) = shift;
74              
75             if (ref($self)) {
76             $self->{attributes} = shift if @_;
77             return (defined $self->{attributes}) ?
78             $self->{attributes} : $self->Attributes;
79             } else {
80             return $self->Attributes(@_);
81             }
82             }
83              
84             # Like attributes, except for methods.
85              
86             sub methods {
87              
88             my($self) = shift;
89              
90             if (ref($self)) {
91             $self->{methods} = shift if @_;
92             return (defined $self->{methods}) ?
93             $self->{methods} : $self->Methods;
94             } else {
95             return $self->Methods(@_);
96             }
97             }
98              
99             # similarly for the description
100              
101             sub description {
102              
103             my($self) = shift;
104              
105             if (ref($self)) {
106             $self->{description} = shift if @_;
107             return (defined $self->{description}) ?
108             $self->{description} : $self->Description;
109             } else {
110             return $self->Description(@_);
111             }
112             }
113              
114             # What to do when we get an IQ.
115              
116             sub on_iq {
117             my($self) = shift;
118             my($iq) = shift;
119             my($ns) = $iq->GetQuery()->GetXMLNS();
120              
121             if ($ns eq 'jabber:iq:rpc') {
122             return $self->on_method($iq);
123             } elsif ($ns eq $JOAP::NS) {
124             my($verb) = $iq->GetQuery()->GetTag();
125             if ($verb eq 'read') {
126             return $self->on_read($iq);
127             } elsif ($verb eq 'edit') {
128             return $self->on_edit($iq);
129             } elsif ($verb eq 'add') {
130             return $self->on_add($iq);
131             } elsif ($verb eq 'search') {
132             return $self->on_search($iq);
133             } elsif ($verb eq 'delete') {
134             return $self->on_delete($iq);
135             } elsif ($verb eq 'describe') {
136             return $self->on_describe($iq);
137             }
138             }
139              
140             return undef;
141             }
142              
143             # Everything can be read, so we implement here. Since this is pretty
144             # complete, subclasses should probably just implement the attribute_*
145             # methods.
146              
147             sub on_read {
148              
149             my($self) = shift;
150             my($reqiq) = shift;
151              
152             my $respiq = $self->reply($reqiq);
153              
154             if (my($code, $text) = $self->_validate_read($reqiq)) {
155             $respiq->SetType('error');
156             $respiq->SetErrorCode($code);
157             $respiq->SetError($text);
158             return $respiq;
159             }
160              
161             # use the names in the request, or default read names.
162              
163             my @names = $reqiq->GetQuery->GetName;
164              
165             if (!@names) {
166             @names = $self->_attribute_read_names;
167             }
168              
169             my($resp) = $respiq->GetQuery;
170              
171             foreach my $respattr (@names) {
172             my $value = $self->_attribute_get_value($respattr);
173             my $v = $resp->AddAttribute(name => $respattr)->AddValue();
174             # I wish there were an easier way to do this
175             JOAP->copy_value($value, $v);
176             }
177              
178             $resp->SetTimestamp($self->timestamp());
179              
180             return $respiq;
181             }
182              
183              
184             # Everything can be edited, so we implement here. Since this is
185             # pretty complete, subclasses should probably just implement the
186             # attribute_* methods.
187              
188             sub on_edit {
189              
190             my($self) = shift;
191             my($reqiq) = shift;
192              
193             my($respiq) = $self->reply($reqiq);
194              
195             if (my($code, $text) = $self->_validate_edit($reqiq, $respiq)) {
196             $respiq->SetType('error');
197             $respiq->SetErrorCode($code);
198             $respiq->SetError($text);
199             return $respiq;
200             }
201              
202             # Set the values.
203              
204             foreach my $toset ($reqiq->GetQuery->GetAttribute) {
205             $self->_attribute_set_value($toset->GetName(), $toset->GetValue());
206             }
207              
208             # Return the response.
209              
210             return $respiq;
211             }
212              
213             # everything can be described, and the mechanism is simple, so we do
214             # it here. subclasses like ::Server and ::Class add on extra info
215             # after calling this default.
216              
217             sub on_describe {
218              
219             my($self) = shift;
220             my($reqiq) = shift;
221             my($respiq) = $self->reply($reqiq);
222             my($desc) = $respiq->GetQuery;
223              
224             if ($reqiq->GetType ne 'get') {
225             $respiq->SetType('error');
226             $respiq->SetErrorCode(406); # Not acceptable
227             $respiq->SetError('Describe verbs must have type get');
228             return $respiq;
229             }
230              
231             if ($self->description) {
232             $desc->SetDesc($self->description);
233             }
234              
235             foreach my $name ($self->_attribute_names()) {
236             $desc->AddAttributeDescription(name => $name,
237             type => $self->_attribute_type($name),
238             writable => $self->_attribute_writable($name),
239             required => $self->_attribute_required($name),
240             allocation => $self->_attribute_allocation($name),
241             desc => $self->_attribute_desc($name));
242             }
243              
244             foreach my $meth ($self->_method_names()) {
245              
246             my $m = $desc->AddMethodDescription(name => $meth,
247             returnType => $self->_method_returntype($meth),
248             allocation => $self->_method_allocation($meth),
249             desc => $self->_method_desc($meth));
250              
251             my $p = $m->AddParams();
252              
253             foreach my $param (@{$self->_method_params($meth)}) {
254             $p->AddParam(name => $param->{name},
255             type => $param->{type},
256             desc => $param->{desc});
257             }
258             }
259              
260             # subclasses will use this to add superclasses, classes
261              
262             return $respiq;
263             }
264              
265             # This can only be sent to instances, so by default we return a 405.
266              
267             sub on_delete {
268              
269             my($self) = shift;
270             my($iq) = shift;
271             my($respiq) = $self->reply($iq);
272              
273             $respiq->SetType('error');
274             $respiq->SetErrorCode(405); # Not allowed
275             $respiq->SetError("Not allowed.");
276              
277             return $respiq;
278             }
279              
280             # This can only be sent to classes, so by default we return a 405.
281              
282             sub on_add {
283              
284             my($self) = shift;
285             my($iq) = shift;
286             my($respiq) = $self->reply($iq);
287              
288             $respiq->SetType('error');
289             $respiq->SetErrorCode(405); # Not allowed
290             $respiq->SetError("Not allowed.");
291              
292             return $respiq;
293             }
294              
295             # This can only be sent to classes, so by default we return a 405.
296              
297             sub on_search {
298              
299             my($self) = shift;
300             my($iq) = shift;
301             my($respiq) = $self->reply($iq);
302              
303             $respiq->SetType('error');
304             $respiq->SetErrorCode(405); # Not implemented
305             $respiq->SetError("Not allowed."); # Not implemented
306              
307             return $respiq;
308             }
309              
310             # This is called when we get a method.
311              
312             sub on_method {
313              
314             my $self = shift;
315             my $iq = shift;
316             my $respiq = $self->reply($iq);
317              
318             if (my($code, $text) = $self->_validate_method($iq)) {
319             $respiq->SetType('error');
320             $respiq->SetErrorCode($code);
321             $respiq->SetError($text);
322             return $respiq;
323             }
324              
325             my $query = $iq->GetQuery;
326             my $call = $query->GetMethodCall;
327             my $meth = $call->GetMethodName;
328             my $fn = $self->_method_function($meth);
329              
330             my @actuals = $call->GetParams->GetParams; # Ugh, that's so dumb
331              
332             my @trans = map {JOAP->decode($_->GetValue)} @actuals;
333              
334             my $resp = $respiq->GetQuery->AddMethodResponse;
335             my @results;
336              
337             try {
338             @results = $self->$fn(@trans);
339             my $v = $resp->AddParams->AddParam->AddValue;
340             JOAP->copy_value(JOAP->encode($self->_method_returntype($meth), @results), $v);
341             } catch Error with {
342             my $err = shift;
343             my $str = $resp->AddFault->AddValue->AddStruct;
344             $str->AddMember(name => 'faultCode')->AddValue(i4 => $err->value);
345             $str->AddMember(name => 'faultString')->AddValue(string => $err->text);
346             };
347              
348             return $respiq;
349             }
350              
351             # Utility to create replies; By default, Net::Jabber::IQ leaves all
352             # the bits inside the query in the reply, too. And "Remove" doesn't
353             # work for "children" elements.
354              
355             sub reply {
356             my $self = shift;
357             my $iq = shift;
358              
359             my $reply = new Net::Jabber::IQ();
360              
361             $reply->SetTo($iq->GetFrom);
362             $reply->SetFrom($iq->GetTo);
363             $reply->SetID($iq->GetID) if $iq->GetID;
364             $reply->SetType('result');
365              
366             my $query = $iq->GetQuery;
367             $reply->NewQuery($query->GetXMLNS, $query->GetTag);
368              
369             return $reply;
370             }
371              
372             # utility for creating timestamps
373              
374             sub timestamp {
375              
376             my $self = shift;
377             # just reuse the main modules conversion.
378             return JOAP->coerce('dateTime.iso8601', time);
379             }
380              
381             # This helps let subclasses add validation code
382              
383             sub _validate_read {
384              
385             my $self = shift;
386             my $reqiq = shift;
387              
388             if ($reqiq->GetType ne 'get') {
389             return(406, 'Read verbs must have type get');
390             }
391              
392             my(@attrs);
393             @attrs = $self->_attribute_names();
394              
395             my(@names);
396             @names = $reqiq->GetQuery()->GetName();
397              
398             my(@unmatched);
399              
400             @unmatched = grep { my($a) = $_; ! grep {/$a/} @attrs } @names;
401              
402             if (@unmatched) {
403             return(406, join("\n", map {"No such attribute '$_'."} @unmatched));
404             }
405              
406             return ();
407             }
408              
409             # again, validation code can be done in subclasses
410              
411             sub _validate_method {
412              
413             my $self = shift;
414             my $iq = shift;
415              
416             if ($iq->GetType ne 'set') {
417             return (406, 'RPC calls must be of type set');
418             }
419              
420             my $query = $iq->GetQuery;
421             my $call = $query->GetMethodCall;
422              
423             if (!$call) {
424             return (406, 'No method call');
425             }
426              
427             my $meth = $call->GetMethodName;
428              
429             if (!$meth) {
430             return (406, 'No method name');
431             }
432              
433             my $fn = $self->_method_function($meth);
434              
435             if (!$fn) {
436             return (406, 'No such method');
437             }
438              
439             my $params = $self->_method_params($meth);
440              
441             my @actuals = $call->GetParams->GetParams; # Ugh, that's so dumb
442              
443             if (scalar(@actuals) != scalar(@$params)) {
444             return (406, 'Wrong number of parameters');
445             }
446              
447             # check param types
448              
449             my $i;
450             my @badvals;
451              
452             for ($i = 0; $i <= $#actuals; $i++) {
453             if (! $self->_type_match($params->[$i]->{type}, $actuals[$i]->GetValue)) {
454             push @badvals, $params->[$i]->{name};
455             }
456             }
457              
458             if (@badvals) {
459             return (406, join("\n", map { "Bad value for parameter $_" } @badvals));
460             }
461              
462             # empty list means OK
463              
464             return ();
465             }
466              
467             # validate an ; validation code can be done in subclasses
468              
469             sub _validate_edit {
470              
471             my $self = shift;
472             my $reqiq = shift;
473              
474             if ($reqiq->GetType ne 'set') {
475             return (406, 'Edit verbs must have type get');
476             }
477              
478             my(@attrs);
479             @attrs = $self->_attribute_names();
480              
481             my(@toset);
482             @toset = $reqiq->GetQuery()->GetAttribute();
483              
484             my(@names);
485             @names = map { $_->GetName() } @toset;
486              
487             # Check for attribute names that aren't in our object.
488              
489             my(@unmatched);
490             @unmatched = grep { my($a) = $_; ! grep {/$a/} @attrs } @names;
491              
492             if (@unmatched) {
493             return (406, join("\n", map {"No such attribute '$_'."} @unmatched));
494             }
495              
496             # Check for stuff that isn't writable.
497              
498             my(@notallowed);
499             @notallowed = grep { !$self->_attribute_writable($_) } @names;
500              
501             if (@notallowed) {
502             return (403, join("\n", map {"Cannot edit attribute '$_'."} @notallowed));
503             }
504              
505             # Check for attribute values that are of the wrong type, or
506             # invalid in some other way.
507              
508             my(@notok);
509             @notok = grep {!$self->_attribute_ok($_->GetName(), $_->GetValue())} @toset;
510              
511             if (@notok) {
512             return (406, join("\n", map {"Value for attribute '" . $_->GetName . "' not acceptable."} @notok));
513             }
514              
515             return ();
516             }
517              
518             # a general attribute validator, used in _validate_* above
519              
520             sub _attribute_ok {
521              
522             my $self = shift;
523             my $name = shift;
524             my $value = shift;
525              
526             my $type = $self->_attribute_type($name);
527              
528             # right now, just check for type match
529              
530             return $self->_type_match($type, $value);
531             }
532              
533             # check to see if a value (as an XML thingy) matches the type
534              
535             sub _type_match {
536              
537             my $self = shift;
538             my $type = shift;
539             my $value = shift;
540              
541             if ($type eq 'i4' || $type eq 'int') {
542             return $value->DefinedI4 &&
543             ($value->GetI4 =~ /^[+-]?\d+$/);
544             }
545             elsif ($type eq 'boolean') {
546             return $value->DefinedBoolean &&
547             ($value->GetBoolean =~ /^[10]$/);
548             }
549             elsif ($type eq 'string') {
550             return $value->DefinedString; # can't mess up a string
551             }
552             elsif ($type eq 'double') {
553             return $value->DefinedDouble &&
554             ($value->GetDouble =~ /^(-?(?:\d+(?:\.\d*)?|\.\d+)|([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?)$/);
555             }
556             elsif ($type eq 'dateTime.iso8601') {
557             return $value->DefinedDateTime &&
558             (JOAP->datetime_to_array($value->GetDateTime));
559             }
560             elsif ($type eq 'struct') {
561             return $value->DefinedStruct;
562             }
563             elsif ($type eq 'array') {
564             return $value->DefinedArray;
565             }
566             elsif ($type eq 'base64') {
567             return $value->DefinedBase64;
568             }
569              
570             return undef;
571             }
572              
573             # These methods are here to allow a JOAP server class (not necessarily
574             # ::Class!) to customize the admittedly primitive attribute definition
575             # method with a more robust mechanism.
576              
577             sub _attribute_names {
578              
579             my $self = shift;
580              
581             # Attributes is a hash ref.
582              
583             return keys %{$self->attributes};
584             }
585              
586             sub _attribute_read_names {
587             my $self = shift;
588             return $self->_attribute_names;
589             }
590              
591             sub _attribute_descriptor {
592              
593             my ($self) = shift;
594             my ($name) = shift;
595              
596             return $self->attributes->{$name};
597             }
598              
599             sub _attribute_get_value {
600              
601             my($self) = shift;
602             my($attr) = shift;
603              
604             my($type) = $self->_attribute_type($attr);
605             my @raw = $self->_attribute_get($attr);
606              
607             return JOAP->encode($type, @raw);
608             }
609              
610             sub _attribute_set_value {
611              
612             my($self) = shift;
613             my($attr) = shift;
614             my($value) = shift;
615              
616             my($raw) = JOAP->decode($value);
617              
618             return $self->_attribute_set($attr, $raw);
619             }
620              
621             sub _attribute_get {
622              
623             my($self) = shift;
624             my($attr) = shift;
625              
626             my($getter) = $self->_attribute_getter($attr);
627              
628             if (!$getter) {
629             throw Error::Simple("No way to get attribute $attr");
630             }
631              
632             no strict 'refs';
633              
634             return $self->$getter();
635             }
636              
637             sub _attribute_set {
638              
639             my($self) = shift;
640             my($attr) = shift;
641              
642             my(@value) = @_;
643             my($setter) = $self->_attribute_setter($attr);
644              
645             # XXX: strict refs
646              
647             if (!$setter) {
648             throw Error::Simple("No way to set attribute $attr");
649             }
650              
651             no strict 'refs';
652              
653             return $self->$setter(@value);
654             }
655              
656             # extract the fields of an attribute description. It's probably
657             # not really a great idea to try to overload them, since it's not
658             # guaranteed that they'll be used rather than just retrieving the
659             # field value in the descriptor directly.
660              
661             sub _attribute_writable {
662             my ($self) = shift;
663             my ($attr) = shift;
664              
665             my ($desc) = $self->_attribute_descriptor($attr) || return undef;
666              
667             return (!exists $desc->{writable}) ? 1 :
668             $desc->{writable};
669             }
670              
671             sub _attribute_required {
672             my ($self) = shift;
673             my ($attr) = shift;
674              
675             my ($desc) = $self->_attribute_descriptor($attr) || return undef;
676              
677             return (!exists $desc->{required}) ? 0 :
678             $desc->{required};
679             }
680              
681             sub _attribute_allocation {
682             my ($self) = shift;
683             my ($attr) = shift;
684              
685             my ($desc) = $self->_attribute_descriptor($attr) || return undef;
686              
687             return (!exists $desc->{allocation}) ? 'instance' :
688             $desc->{allocation};
689             }
690              
691             sub _attribute_desc {
692             my ($self) = shift;
693             my ($attr) = shift;
694              
695             my ($desc) = $self->_attribute_descriptor($attr) || return undef;
696              
697             return (!exists $desc->{desc}) ? undef :
698             $desc->{desc};
699             }
700              
701             sub _attribute_type {
702              
703             my($self) = shift;
704             my($attr) = shift;
705              
706             my($desc) = $self->_attribute_descriptor($attr) || return undef;
707              
708             return $desc->{type};
709             }
710              
711             # returns a "getter" method for the attribute. By default, the name of
712             # the getter is the name of the attribute. It can also be defined in
713             # the attribute description.
714              
715             sub _attribute_getter {
716              
717             my($self) = shift;
718             my($attr) = shift;
719             my($desc) = $self->_attribute_descriptor($attr) || return undef;
720              
721             my($getter) = $desc->{getter} || $desc->{accessor} || $attr;
722              
723             return $getter;
724             }
725              
726             # returns a "setter" method for the attribute. By default, the name of
727             # the setter is the name of the attribute. It can be defined in the
728             # attribute description.
729              
730             sub _attribute_setter {
731              
732             my($self) = shift;
733             my($attr) = shift;
734             my($desc) = $self->_attribute_descriptor($attr) || return undef;
735              
736             my($setter) = $desc->{setter} || $desc->{accessor} || $attr;
737              
738             return $setter;
739             }
740              
741             # These methods are here to allow a JOAP server class (not necessarily
742             # ::Class!) to customize the admittedly primitive method definition
743             # method with a more robust mechanism.
744              
745             sub _method_names {
746              
747             my ($self) = shift;
748             my ($name) = shift;
749              
750             # Methods is a hash ref
751              
752             return keys %{$self->methods};
753             }
754              
755             sub _method_descriptor {
756              
757             my ($self) = shift;
758             my ($name) = shift;
759              
760             # Methods is a hash ref
761              
762             return $self->methods->{$name};
763             }
764              
765             # These just grab things from the descriptor, or make up semi-reasonable
766             # defaults.
767              
768             sub _method_function {
769              
770             my $self = shift;
771             my $name = shift;
772              
773             my $desc = $self->_method_descriptor($name) ||
774             return undef;
775              
776             return $desc->{function} || $self->can($name);
777             }
778              
779             sub _method_returntype {
780             my $self = shift;
781             my $name = shift;
782              
783             my $desc = $self->_method_descriptor($name) ||
784             return undef;
785              
786             return $desc->{returnType} || 'array';
787             }
788              
789             sub _method_params {
790              
791             my $self = shift;
792             my $name = shift;
793              
794             my $desc = $self->_method_descriptor($name) ||
795             return undef;
796              
797             return $desc->{params} || {default => {type => 'array'}};
798             }
799              
800             sub _method_allocation {
801              
802             my $self = shift;
803             my $name = shift;
804              
805             my $desc = $self->_method_descriptor($name) ||
806             return undef;
807              
808             return $desc->{allocation} || 'instance';
809             }
810              
811             sub _method_desc {
812              
813             my $self = shift;
814             my $name = shift;
815              
816             my $desc = $self->_method_descriptor($name) ||
817             return undef;
818              
819             return $desc->{desc};
820             }
821              
822             # This allows us to say $self->can('autoloadedmethod'). AUTOLOAD (below)
823             # uses this method to create methods if necessary.
824              
825             sub can {
826              
827             my($self) = shift;
828             my($name) = shift;
829             my($func) = $self->SUPER::can($name); # See if it's findable by standard lookup.
830              
831             if (!$func) { # Otherwise, see if it's an attribute.
832             my $desc = $self->_attribute_descriptor($name);
833             if ($desc) {
834             if (($self->_attribute_allocation($name) eq 'class')) {
835             my $pkg = ref($self) || $self;
836             my $globref = qualify_to_ref($pkg . "::" . $name);
837             my $sref = *$globref{SCALAR};
838             $func = sub {
839             my $self = shift;
840             return (@_) ? ($$sref = shift) : $$sref;
841             };
842             } elsif ($self->_attribute_allocation($name) eq 'instance' && ref($self)) {
843             $func = sub {
844             my($self) = shift;
845             return (@_) ? ($self->{$name} = shift) : $self->{$name};
846             };
847             }
848             }
849             }
850              
851             return $func;
852             }
853              
854             # use can() to build a closure, install it in the package, call it
855              
856             sub AUTOLOAD {
857              
858             my ($self) = $_[0];
859             my ($sub) = $AUTOLOAD;
860              
861             my ($pkg,$name) = ($sub =~ /(.*)::([^:]+)$/);
862             my ($func) = $self->can($name);
863              
864             if ($func) {
865             no strict 'refs';
866             *$sub = $func; # save it for later
867             goto &$sub;
868             } else {
869             throw Error::Simple("No method to get $name");
870             }
871             }
872              
873             # This keeps us from calling AUTOLOAD for DESTROY
874              
875             sub DESTROY { }
876              
877             1;
878             __END__