| Category: | Misc/OO programming |
| Author/Contact Info | Thomas Klausner domm@zsi.at |
| Description: | Class::Data::TIN implements Translucent Inheritable Nonpolluting Class Data.
See perltootc for an intro to Class Data The thing I don't like with Class::Data::Inheritable or the implementations suggested in perltootc is that you end up with lots of accessor routines in your namespace. Class::Data::TIN works around this "problem" by storing the Class Data in its own namespace (mirroring the namespace and @ISA hierarchies of the modules using it) and supplying the using packages with (at this time) three meta-accessors called get_classdata, set_classdata and append_classdata. It achieves this with some black magic (namespace munging & evaling). This module is on CPAN, e.g. here, including a test.pl that shows the functioniality quite well. |
#----------------------------------------------------------------- # Class::Data::TIN #----------------------------------------------------------------- # Copyright Thomas Klausner / ZSI 2001, 2002 # You may use and distribute this module according to the same terms # that Perl is distributed under. # # Thomas Klausner domm@zsi.at http://domm.zsi.at # # $Author: domm $ # $Date: 2002/01/29 22:03:35 $ # $Revision: 1.9 $ #----------------------------------------------------------------- # Class::Data::TIN - T_ranslucent I_nheritable N_onpolluting #----------------------------------------------------------------- package Class::Data::TIN; use 5.006; use strict; use warnings; require Exporter; use Carp; use Data::Dumper; our @ISA = qw(Exporter); our @EXPORT_OK = qw(get get_classdata set set_classdata append append_ +classdata); our $VERSION = '0.02'; # not exported, has to be called explicitly with Class::Data::TIN->new +() sub new { shift; # remove own ClassName 'Class::Data::TIN' my $org_package=shift; # get name of package to store vars in my $data; if (@_ == 1) { # one param passed my $param=shift; if (ref($param) eq 'HASH') { # is it a HASH ref ? $data=$param; } elsif (-e $param) { # or is it a file ? $data=do $param; # TODO some error checking } else { # then something is wrong croak("param is neither HASH REF nor file ..."); } } else { # more params passed, treat as HASH $data={@_}; } croak("data structure must be a hashref") if ($data && ref($data) n +e "HASH"); my $tin_package=__PACKAGE__."::".$org_package; ### put data into TIN # start eval-string my $install="package $tin_package;"; # add ISA's my @isa=eval "@".$org_package."::ISA"; my @isa_tin; foreach (@isa) { push(@isa_tin,__PACKAGE__."::".$_); } $install.='our @ISA=(qw ('."@isa_tin".'));' if @isa_tin; $install.='our $_tin;'; $install.='$_tin=$data;' if $data; eval $install; croak $@ if $@; # generate accessor methods in $tin_package for my $key (keys %$data) { _make_accessor($tin_package,$key); } # return empty fake pseudo obj, to make calling get/set/append easi +er # this is /not/ blessed, in fact, its just an alias to __PACKAGE__ return $org_package; } # not exported sub _make_accessor { my ($pkg,$key)=@_; # to enable black symbol table magic no strict "refs"; my $accessor=$pkg."::".$key; return if *$accessor{CODE}; # there is allready an accessor my $r_tin=eval '$'."$pkg".'::_tin'; *$accessor = sub { my $self=shift; $r_tin->{$key} = shift if @_; return $r_tin->{$key}; } } # exported, has to be called on object or class, NOT on Class::Data::T +IN sub get_classdata { my ($self,$key)=@_; my $package=ref($self) || $self; my $tin=__PACKAGE__."::".$package; if ($tin->can($key)) { return $tin->$key(); } return; } # alias *get=*get_classdata; # exported, has to be called on object or class, NOT on Class::Data::T +IN sub set_classdata { my $self=shift; my $package=ref($self) || $self; croak "object not allowed to modify class data" if (ref($self)); my $tin=__PACKAGE__."::".$package; my ($key,$val)=@_; # copy on write: _make_accessor($tin,$key); return $tin->$key($val); } # alias *set=*set_classdata; # exported, has to be called on object or class, NOT on Class::Data::T +IN sub append_classdata { my $self=shift; my $package=ref($self) || $self; croak "object not allowed to modify class data" if (ref($self)); my $tin=__PACKAGE__."::".$package; my $key=shift; # if this key is not here, there's no use appending, so use set() unless ($tin->can($key)) { return set($self,$key,@_); } # get old value my $val=$tin->$key; if (!ref($val)) { $val.=shift; } elsif (ref($val) eq "HASH") { eval Data::Dumper->Dump([$val],['val']); $val={%$val,@_}; } elsif (ref($val) eq "ARRAY") { eval Data::Dumper->Dump([$val],['val']); push(@$val,@_); } elsif (ref($val) eq "CODE") { croak("cannot modify code ref"); } # copy on write: _make_accessor($tin,$key); $tin->$key($val); } # alias *append=*append_classdata; 1; __END__ for docs etc see CPAN |
|
|
|---|