home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / Fields.pm < prev    next >
Encoding:
Perl POD Document  |  2003-09-16  |  12.1 KB  |  471 lines

  1. # $Id: Fields.pm,v 1.2 2003/09/16 13:01:43 joker Exp $ 
  2.  
  3. package Class::Fields;
  4.  
  5. use strict;
  6. no strict 'refs';
  7.  
  8. use vars qw(@ISA @EXPORT $VERSION);
  9. require Exporter;
  10. @ISA = qw(Exporter);
  11.  
  12. # is_* will push themselves onto @EXPORT
  13. @EXPORT = qw( field_attrib_mask
  14.               field_attribs
  15.               dump_all_attribs
  16.               show_fields
  17.               is_public
  18.               is_private
  19.               is_protected
  20.               is_inherited
  21.               is_field
  22.             );
  23.  
  24. $VERSION = '0.15';
  25.  
  26. use Class::Fields::Fuxor;
  27. use Class::Fields::Attribs;
  28.  
  29. # Mapping of attribute names to their internal values.
  30. use vars qw(%NAMED_ATTRIBS);
  31. BEGIN {
  32.     %NAMED_ATTRIBS = (
  33.                       Public    =>  PUBLIC,
  34.                       Private   =>  PRIVATE,
  35.                       Inherited =>  INHERITED,
  36.                       Protected =>  PROTECTED,
  37.                      );
  38. }
  39.  
  40. =pod
  41.  
  42. =head1 NAME
  43.  
  44. Class::Fields - Inspect the fields of a class.
  45.  
  46.  
  47. =head1 SYNOPSIS
  48.  
  49.     use Class::Fields;
  50.  
  51.     is_field    ($class, $field);
  52.     is_public   ($class, $field);
  53.     is_private  ($class, $field);
  54.     is_protected($class, $field);
  55.     is_inherited($class, $field);
  56.  
  57.     @fields = show_fields($class, @attribs);
  58.  
  59.     $attrib     = field_attrib_mask($class, $field);
  60.     @attribs    = field_attribs($class, $field);
  61.  
  62.     dump_all_attribs(@classes);
  63.  
  64.  
  65.     # All functions also work as methods.
  66.     package Foo;
  67.     use base qw( Class::Fields );
  68.  
  69.     Foo->is_public($field);
  70.     @fields = Foo->show_fields(@attribs);
  71.     # ...etc...
  72.  
  73.  
  74. =head1 DESCRIPTION
  75.  
  76. A collection of utility functions/methods for examining the data
  77. members of a class.  It provides a nice, high-level interface that
  78. should stand the test of time and Perl upgrades nicely.
  79.  
  80. The functions in this module also serve double-duty as methods and can
  81. be used that way by having your module inherit from it.  For example:
  82.  
  83.     package Foo;
  84.     use base qw( Class::Fields );
  85.     use fields qw( this that _whatever );
  86.  
  87.     print "'_whatever' is a private data member of 'Foo'" if
  88.         Foo->is_private('_whatever');
  89.  
  90.     # Let's assume we have a new() method defined for Foo, okay?
  91.     $obj = Foo->new;
  92.     print "'this' is a public data member of 'Foo'" if
  93.         $obj->is_public('this');
  94.  
  95. =over 4
  96.  
  97. =item B<is_field>
  98.  
  99.   is_field($class, $field);
  100.   $class->is_field($field);
  101.  
  102. Simply asks if a given $class has the given $field defined in it.
  103.  
  104. =cut
  105.  
  106. sub is_field {
  107.     my($proto, $field) = @_;
  108.  
  109.     my($class) = ref $proto || $proto;
  110.     return defined field_attrib_mask($class, $field) ? 1 : 0;
  111. }
  112.  
  113. =pod
  114.  
  115. =item B<is_public>
  116.  
  117. =item B<is_private>
  118.  
  119. =item B<is_protected>
  120.  
  121. =item B<is_inherited>
  122.  
  123.   is_public($class, $field);
  124.   is_private($class, $field);
  125.   ...etc...
  126.         or
  127.   $obj->is_public($field);
  128.         or
  129.   Class->is_public($field);
  130.  
  131. A bunch of functions to quickly check if a given $field in a given $class
  132. is of a given type.  For example...
  133.  
  134.   package Foo;
  135.   use public  qw( Ford   );
  136.   use private qw( _Nixon );
  137.  
  138.   package Bar;
  139.   use base qw(Foo);
  140.  
  141.   # This will print only 'Ford is public' because Ford is a public
  142.   # field of the class Bar.  _Nixon is a private field of the class
  143.   # Foo, but it is not inherited.
  144.   print 'Ford is public'        if is_public('Bar', 'Ford');
  145.   print '_Nixon is inherited'   if is_inherited('Foo', '_Nixon');
  146.  
  147.  
  148. =cut
  149.  
  150. # Generate is_public, etc... from %NAMED_ATTRIBS For each attribute we
  151. # generate a simple named closure.  Seemed the laziest way to do it,
  152. # lets us update %NAMED_ATTRIBS without having to make a new function.
  153. while ( my($attrib, $attr_val) = each %NAMED_ATTRIBS ) {
  154.     no strict 'refs';
  155.     my $fname = 'is_'.lc $attrib;
  156.     *{$fname} = sub {
  157.         my($proto, $field) = @_;
  158.         
  159.         # So we can be called either as a function or a method from
  160.         # a class name or an object.
  161.         my($class) = ref $proto || $proto;
  162.         my $fattrib = field_attrib_mask($class, $field);
  163.         
  164.         return unless defined $fattrib;
  165.         
  166.         return $fattrib & $attr_val;
  167.     };
  168.       
  169.     push @EXPORT, $fname;
  170. }
  171.  
  172.  
  173. =pod
  174.  
  175. =item B<show_fields>
  176.  
  177.   @all_fields   = show_fields($class);
  178.   @fields       = show_fields($class, @attribs);
  179.         or
  180.   @all_fields   = $obj->show_fields;
  181.   @fields       = $obj->show_fields(@attribs);
  182.         or
  183.   @all_fields   = Class->show_fields;
  184.   @fields       = Class->show_fields(@attribs);
  185.  
  186. This will list all fields in a given $class that have the given set of
  187. @attribs.  If @attribs is not given it will simply list all fields.
  188.  
  189. The currently available attributes are:
  190.     Public, Private, Protected and Inherited
  191.  
  192. For example:
  193.  
  194.     package Foo;
  195.     use fields qw(this that meme);
  196.  
  197.     package Bar;
  198.     use Class::Fields;
  199.     use base qw(Foo);
  200.     use fields qw(salmon);
  201.  
  202.     # @fields contains 'this', 'that' and 'meme' since they are Public and
  203.     # Inherited.  It doesn't contain 'salmon' since while it is
  204.     # Public it is not Inherited.
  205.     @fields = show_fields('Bar', qw(Public Inherited));
  206.  
  207. =cut
  208.  
  209. sub show_fields {
  210.     my($proto, @attribs) = @_;
  211.  
  212.     # Allow its tri-nature.
  213.     my($class) = ref $proto || $proto;
  214.  
  215.     return unless has_fields($class);
  216.  
  217.     my $fields  = get_fields($class);
  218.  
  219.     # Shortcut:  Return all fields if they don't specify a set of
  220.     # attributes.
  221.     return keys %$fields unless @attribs;
  222.     
  223.     # Figure out the bitmask for the attribute set they'd like.
  224.     my $want_attr = 0;
  225.     foreach my $attrib (@attribs) {
  226.         unless( defined $NAMED_ATTRIBS{$attrib} ) {
  227.             require Carp;
  228.             Carp::croak("'$attrib' is not a valid field attribute");
  229.         }
  230.         $want_attr |= $NAMED_ATTRIBS{$attrib};
  231.     }
  232.  
  233.     # Return all fields with the requested bitmask.
  234.     my $fattr   = get_attr($class);
  235.     return grep { ($fattr->[$fields->{$_}] & $want_attr) == $want_attr} 
  236.                 keys %$fields;
  237. }
  238.  
  239. =pod
  240.  
  241. =item B<field_attrib_mask>
  242.  
  243.   $attrib = field_attrib_mask($class, $field);
  244.         or
  245.   $attrib = $obj->field_attrib_mask($field);
  246.         or
  247.   $attrib = Class->field_attrib_mask($field);
  248.  
  249. It will tell you the numeric attribute for the given $field in the
  250. given $class.  $attrib is a bitmask which must be interpreted with
  251. the PUBLIC, PRIVATE, etc... constants from Class::Fields::Attrib.
  252.  
  253. field_attribs() is probably easier to work with in general.
  254.  
  255. =cut
  256.  
  257. sub field_attrib_mask {
  258.     my($proto, $field) = @_;
  259.     my($class) = ref $proto || $proto;
  260.     my $fields  = get_fields($class);
  261.     my $fattr   = get_attr($class);
  262.     return unless defined $fields->{$field};
  263.     return $fattr->[$fields->{$field}];
  264. }
  265.  
  266. =pod
  267.  
  268. =item B<field_attribs>
  269.  
  270.   @attribs = field_attribs($class, $field);
  271.         or
  272.   @attribs = $obj->field_attribs($field);
  273.         or
  274.   @attribs = Class->field_attribs($field);
  275.  
  276. Exactly the same as field_attrib_mask(), except that instead of
  277. returning a bitmask it returns a somewhat friendlier list of
  278. attributes which are applied to this field.  For example...
  279.  
  280.   package Foo;
  281.   use fields qw( yarrow );
  282.  
  283.   package Bar;
  284.   use base qw(Foo);
  285.  
  286.   # @attribs will contain 'Public' and 'Inherited'
  287.   @attribs = field_attribs('Bar', 'yarrow');
  288.  
  289. The attributes returned are the same as those taken by show_fields().
  290.  
  291. =cut
  292.  
  293. sub field_attribs {
  294.     my($proto, $field) = @_;
  295.     my($class) = ref $proto || $proto;
  296.  
  297.     my @attribs = ();
  298.     my $attr_mask = field_attrib_mask($class, $field);
  299.     
  300.     while( my($attr_name, $attr_val) = each %NAMED_ATTRIBS ) {
  301.         push @attribs, $attr_name if $attr_mask & $attr_val;
  302.     }
  303.  
  304.     return @attribs;
  305. }
  306.  
  307. =pod
  308.  
  309. =item B<dump_all_attribs>
  310.  
  311.   dump_all_attribs;
  312.   dump_all_attribs(@classes);
  313.         or
  314.   Class->dump_all_attribs;
  315.         or
  316.   $obj->dump_all_attribs;
  317.  
  318. A debugging tool which simply prints to STDERR everything it can about
  319. a given set of @classes in a relatively formated manner.
  320.  
  321. Alas, this function works slightly differently if used as a function
  322. as opposed to a method:
  323.  
  324. When called as a function it will print out attribute information
  325. about all @classes given.  If no @classes are given it will print out
  326. the attributes of -every- class it can find that has attributes.
  327.  
  328. When uses as a method, it will print out attribute information for the
  329. class or object which uses the method.  No arguments are accepted.
  330.  
  331. I'm not entirely happy about this split and I might change it in the
  332. future.
  333.  
  334. =cut
  335.  
  336. # Backwards compatiblity.
  337. *_dump = \&dump_all_attribs;
  338.  
  339. #'#
  340. sub dump_all_attribs {
  341.     my @classes = @_;
  342.  
  343.     # Everything goes to STDERR.
  344.     my $old_fh = select(STDERR);
  345.  
  346.     # Disallow $obj->dump_all_attribs(@classes);  Too ambiguous to live.
  347.     # Alas, I can't check for Class->dump_all_attribs(@classes).
  348.     if ( @classes > 1 and ref $classes[0] ) {
  349.         require Carp;
  350.         Carp::croak('$obj->dump_all_attribs(@classes) is too ambiguous.'.
  351.                     'Use only as $obj->dump_all_attribs()');
  352.     }
  353.  
  354.     # Allow $obj->dump_all_attribs; to work.
  355.     $classes[0] = ref $classes[0] || $classes[0] if @classes == 1;
  356.  
  357.     # Have to do a little encapsulation breaking here.  Oh well, at least
  358.     # its keeping it in the family.
  359.     @classes = sort keys %fields::attr unless @classes;
  360.  
  361.     for my $class (@classes) {
  362.         print "\n$class";
  363.         if (@{"$class\::ISA"}) {
  364.             print " (", join(", ", @{"$class\::ISA"}), ")";
  365.         }
  366.         print "\n";
  367.         my $fields = get_fields($class);
  368.         for my $f (sort {$fields->{$a} <=> $fields->{$b}} keys %$fields) {
  369.             my $no = $fields->{$f};
  370.             print "   $no: $f";
  371.             print "\t(", join(", ", field_attribs($class, $f)), ")";
  372.             print "\n";
  373.         }
  374.     }
  375.         
  376.     select($old_fh);
  377. }
  378.  
  379. =pod
  380.  
  381. =head1 EXAMPLES
  382.  
  383. Neat tricks that can be done with this module:
  384.  
  385. =over 4
  386.  
  387. =item An integrity check for your object.
  388.  
  389. Upon destruction, check to make sure no strange keys were added to
  390. your object hash.  This is a nice check against typos and other
  391. modules sticking their dirty little fingers where they shouldn't be
  392. if you're not using a pseudo-hash.
  393.  
  394.     sub DESTROY {
  395.         my($self) = @_;
  396.         my($class) = ref $self;
  397.  
  398.         my %fields = map { ($_,1) } $self->show_fields;
  399.         foreach my $key ( keys %$self ) {
  400.             warn "Strange key '$key' found in object '$self' ".
  401.                   "of class '$class'" unless
  402.                 exists $fields{$key};
  403.         }
  404.     }
  405.  
  406. =item Autoloaded accessors for public data members.
  407.  
  408. Proper OO dogma tells you to do all public data access through
  409. accessors (methods who's sole purpose is to get and set data in your
  410. object).  This can be a royal pain in the ass to write and can also
  411. get rapidly unmaintainable since you wind up with a series of nearly
  412. identical methods.
  413.  
  414. *Perfect* for an autoloader!
  415.  
  416.     package Test::Autoload::Example;
  417.     use base qw(Class::Fields);
  418.     use public qw(this that up down);
  419.     use private qw(_left _right);
  420.  
  421.     sub AUTOLOAD {
  422.         my $self = $_[0];
  423.         my $class = ref $self;
  424.  
  425.         my($field) = $AUTOLOAD =~ /::([^:]+)$/;
  426.  
  427.         return if $field eq 'DESTROY';
  428.  
  429.         # If its a public field, set up a named closure as its
  430.         # data accessor.
  431.         if ( $self->is_public($field) ) {
  432.             *{$class."::$field"} = sub {
  433.                 my($self) = shift;
  434.                 if (@_) {
  435.                     $self->{$field} = shift;
  436.                 }
  437.                 return $self->{$field};
  438.             };
  439.             goto &{$class."::$field"};
  440.         } else {
  441.             die "'$field' is not a public data member of '$class'";
  442.         }
  443.     }
  444.  
  445. L<Class::Accessor/EXAMPLES> for a much simpler version of this same
  446. technique.
  447.  
  448. =back
  449.  
  450. =head1 AUTHOR
  451.  
  452. Michael G Schwern <schwern@pobox.com> with much code liberated from the
  453. original fields.pm.
  454.  
  455.  
  456. =head1 THANKS
  457.  
  458. Thanks to Tels for his big feature request/bug report.
  459.  
  460.  
  461. =head1 SEE ALSO
  462.  
  463. L<fields>, L<public>, L<private>, L<protected>
  464.  
  465. Modules with similar effects...
  466. L<Tie::SecureHash>, L<Class::Contract>
  467.  
  468. =cut
  469.  
  470. return q|I'll get you next time, Gadget!|;
  471.