summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorChris Williams <chris@bingosnet.co.uk>2009-09-04 16:05:05 +0100
committerChris Williams <chris@bingosnet.co.uk>2009-09-04 16:05:05 +0100
commit40448def39d68b1ed07295047ee0aea523d33f3d (patch)
tree78304ffafb96ded648f79595b62c42895b88134c /ext
parentb41b8addf5576a157ecc804475e7a0a036c02641 (diff)
downloadperl-40448def39d68b1ed07295047ee0aea523d33f3d.tar.gz
Move Object::Accessor from lib/ to ext/
Diffstat (limited to 'ext')
-rw-r--r--ext/.gitignore1
-rw-r--r--ext/Object-Accessor/lib/Object/Accessor.pm819
-rw-r--r--ext/Object-Accessor/t/00_Object-Accessor.t127
-rw-r--r--ext/Object-Accessor/t/01_Object-Accessor-Subclassed.t51
-rw-r--r--ext/Object-Accessor/t/02_Object-Accessor-allow.t82
-rw-r--r--ext/Object-Accessor/t/03_Object-Accessor-local.t50
-rw-r--r--ext/Object-Accessor/t/04_Object-Accessor-lvalue.t82
-rw-r--r--ext/Object-Accessor/t/05_Object-Accessor-callback.t97
-rw-r--r--ext/Object-Accessor/t/06_Object-Accessor-alias.t33
9 files changed, 1342 insertions, 0 deletions
diff --git a/ext/.gitignore b/ext/.gitignore
index 60a68bfef6..a56447942c 100644
--- a/ext/.gitignore
+++ b/ext/.gitignore
@@ -38,6 +38,7 @@ ppport.h
/Module-Loaded/Makefile.PL
/mro/Makefile.PL
/Net-Ping/Makefile.PL
+/Object-Accessor/Makefile.PL
/Opcode/Makefile.PL
/Package-Constants/Makefile.PL
/PerlIO-encoding/Makefile.PL
diff --git a/ext/Object-Accessor/lib/Object/Accessor.pm b/ext/Object-Accessor/lib/Object/Accessor.pm
new file mode 100644
index 0000000000..e5cd2660f0
--- /dev/null
+++ b/ext/Object-Accessor/lib/Object/Accessor.pm
@@ -0,0 +1,819 @@
+package Object::Accessor;
+
+use strict;
+use Carp qw[carp croak];
+use vars qw[$FATAL $DEBUG $AUTOLOAD $VERSION];
+use Params::Check qw[allow];
+use Data::Dumper;
+
+### some objects might have overload enabled, we'll need to
+### disable string overloading for callbacks
+require overload;
+
+$VERSION = '0.34';
+$FATAL = 0;
+$DEBUG = 0;
+
+use constant VALUE => 0; # array index in the hash value
+use constant ALLOW => 1; # array index in the hash value
+use constant ALIAS => 2; # array index in the hash value
+
+=head1 NAME
+
+Object::Accessor
+
+=head1 SYNOPSIS
+
+ ### using the object
+ $obj = Object::Accessor->new; # create object
+ $obj = Object::Accessor->new(@list); # create object with accessors
+ $obj = Object::Accessor->new(\%h); # create object with accessors
+ # and their allow handlers
+
+ $bool = $obj->mk_accessors('foo'); # create accessors
+ $bool = $obj->mk_accessors( # create accessors with input
+ {foo => ALLOW_HANDLER} ); # validation
+
+ $bool = $obj->mk_aliases( # create an alias to an existing
+ alias_name => 'method'); # method name
+
+ $clone = $obj->mk_clone; # create a clone of original
+ # object without data
+ $bool = $obj->mk_flush; # clean out all data
+
+ @list = $obj->ls_accessors; # retrieves a list of all
+ # accessors for this object
+
+ $bar = $obj->foo('bar'); # set 'foo' to 'bar'
+ $bar = $obj->foo(); # retrieve 'bar' again
+
+ $sub = $obj->can('foo'); # retrieve coderef for
+ # 'foo' accessor
+ $bar = $sub->('bar'); # set 'foo' via coderef
+ $bar = $sub->(); # retrieve 'bar' by coderef
+
+ ### using the object as base class
+ package My::Class;
+ use base 'Object::Accessor';
+
+ $obj = My::Class->new; # create base object
+ $bool = $obj->mk_accessors('foo'); # create accessors, etc...
+
+ ### make all attempted access to non-existant accessors fatal
+ ### (defaults to false)
+ $Object::Accessor::FATAL = 1;
+
+ ### enable debugging
+ $Object::Accessor::DEBUG = 1;
+
+ ### advanced usage -- callbacks
+ { my $obj = Object::Accessor->new('foo');
+ $obj->register_callback( sub { ... } );
+
+ $obj->foo( 1 ); # these calls invoke the callback you registered
+ $obj->foo() # which allows you to change the get/set
+ # behaviour and what is returned to the caller.
+ }
+
+ ### advanced usage -- lvalue attributes
+ { my $obj = Object::Accessor::Lvalue->new('foo');
+ print $obj->foo = 1; # will print 1
+ }
+
+ ### advanced usage -- scoped attribute values
+ { my $obj = Object::Accessor->new('foo');
+
+ $obj->foo( 1 );
+ print $obj->foo; # will print 1
+
+ ### bind the scope of the value of attribute 'foo'
+ ### to the scope of '$x' -- when $x goes out of
+ ### scope, 'foo's previous value will be restored
+ { $obj->foo( 2 => \my $x );
+ print $obj->foo, ' ', $x; # will print '2 2'
+ }
+ print $obj->foo; # will print 1
+ }
+
+
+=head1 DESCRIPTION
+
+C<Object::Accessor> provides an interface to create per object
+accessors (as opposed to per C<Class> accessors, as, for example,
+C<Class::Accessor> provides).
+
+You can choose to either subclass this module, and thus using its
+accessors on your own module, or to store an C<Object::Accessor>
+object inside your own object, and access the accessors from there.
+See the C<SYNOPSIS> for examples.
+
+=head1 METHODS
+
+=head2 $object = Object::Accessor->new( [ARGS] );
+
+Creates a new (and empty) C<Object::Accessor> object. This method is
+inheritable.
+
+Any arguments given to C<new> are passed straight to C<mk_accessors>.
+
+If you want to be able to assign to your accessors as if they
+were C<lvalue>s, you should create your object in the
+C<Object::Acccessor::Lvalue> namespace instead. See the section
+on C<LVALUE ACCESSORS> below.
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $obj = bless {}, $class;
+
+ $obj->mk_accessors( @_ ) if @_;
+
+ return $obj;
+}
+
+=head2 $bool = $object->mk_accessors( @ACCESSORS | \%ACCESSOR_MAP );
+
+Creates a list of accessors for this object (and C<NOT> for other ones
+in the same class!).
+Will not clobber existing data, so if an accessor already exists,
+requesting to create again is effectively a C<no-op>.
+
+When providing a C<hashref> as argument, rather than a normal list,
+you can specify a list of key/value pairs of accessors and their
+respective input validators. The validators can be anything that
+C<Params::Check>'s C<allow> function accepts. Please see its manpage
+for details.
+
+For example:
+
+ $object->mk_accessors( {
+ foo => qr/^\d+$/, # digits only
+ bar => [0,1], # booleans
+ zot => \&my_sub # a custom verification sub
+ } );
+
+Returns true on success, false on failure.
+
+Accessors that are called on an object, that do not exist return
+C<undef> by default, but you can make this a fatal error by setting the
+global variable C<$FATAL> to true. See the section on C<GLOBAL
+VARIABLES> for details.
+
+Note that you can bind the values of attributes to a scope. This allows
+you to C<temporarily> change a value of an attribute, and have it's
+original value restored up on the end of it's bound variable's scope;
+
+For example, in this snippet of code, the attribute C<foo> will
+temporarily be set to C<2>, until the end of the scope of C<$x>, at
+which point the original value of C<1> will be restored.
+
+ my $obj = Object::Accessor->new;
+
+ $obj->mk_accessors('foo');
+ $obj->foo( 1 );
+ print $obj->foo; # will print 1
+
+ ### bind the scope of the value of attribute 'foo'
+ ### to the scope of '$x' -- when $x goes out of
+ ### scope, 'foo' previous value will be restored
+ { $obj->foo( 2 => \my $x );
+ print $obj->foo, ' ', $x; # will print '2 2'
+ }
+ print $obj->foo; # will print 1
+
+
+Note that all accessors are read/write for everyone. See the C<TODO>
+section for details.
+
+=cut
+
+sub mk_accessors {
+ my $self = $_[0];
+ my $is_hash = UNIVERSAL::isa( $_[1], 'HASH' );
+
+ ### first argument is a hashref, which means key/val pairs
+ ### as keys + allow handlers
+ for my $acc ( $is_hash ? keys %{$_[1]} : @_[1..$#_] ) {
+
+ ### already created apparently
+ if( exists $self->{$acc} ) {
+ __PACKAGE__->___debug( "Accessor '$acc' already exists");
+ next;
+ }
+
+ __PACKAGE__->___debug( "Creating accessor '$acc'");
+
+ ### explicitly vivify it, so that exists works in ls_accessors()
+ $self->{$acc}->[VALUE] = undef;
+
+ ### set the allow handler only if one was specified
+ $self->{$acc}->[ALLOW] = $_[1]->{$acc} if $is_hash;
+ }
+
+ return 1;
+}
+
+=head2 @list = $self->ls_accessors;
+
+Returns a list of accessors that are supported by the current object.
+The corresponding coderefs can be retrieved by passing this list one
+by one to the C<can> method.
+
+=cut
+
+sub ls_accessors {
+ ### metainformation is stored in the stringified
+ ### key of the object, so skip that when listing accessors
+ return sort grep { $_ ne "$_[0]" } keys %{$_[0]};
+}
+
+=head2 $ref = $self->ls_allow(KEY)
+
+Returns the allow handler for the given key, which can be used with
+C<Params::Check>'s C<allow()> handler. If there was no allow handler
+specified, an allow handler that always returns true will be returned.
+
+=cut
+
+sub ls_allow {
+ my $self = shift;
+ my $key = shift or return;
+ return exists $self->{$key}->[ALLOW]
+ ? $self->{$key}->[ALLOW]
+ : sub { 1 };
+}
+
+=head2 $bool = $self->mk_aliases( alias => method, [alias2 => method2, ...] );
+
+Creates an alias for a given method name. For all intents and purposes,
+these two accessors are now identical for this object. This is akin to
+doing the following on the symbol table level:
+
+ *alias = *method
+
+This allows you to do the following:
+
+ $self->mk_accessors('foo');
+ $self->mk_aliases( bar => 'foo' );
+
+ $self->bar( 42 );
+ print $self->foo; # will print 42
+
+=cut
+
+sub mk_aliases {
+ my $self = shift;
+ my %aliases = @_;
+
+ while( my($alias, $method) = each %aliases ) {
+
+ ### already created apparently
+ if( exists $self->{$alias} ) {
+ __PACKAGE__->___debug( "Accessor '$alias' already exists");
+ next;
+ }
+
+ $self->___alias( $alias => $method );
+ }
+
+ return 1;
+}
+
+=head2 $clone = $self->mk_clone;
+
+Makes a clone of the current object, which will have the exact same
+accessors as the current object, but without the data stored in them.
+
+=cut
+
+### XXX this creates an object WITH allow handlers at all times.
+### even if the original didnt
+sub mk_clone {
+ my $self = $_[0];
+ my $class = ref $self;
+
+ my $clone = $class->new;
+
+ ### split out accessors with and without allow handlers, so we
+ ### don't install dummy allow handers (which makes O::A::lvalue
+ ### warn for example)
+ my %hash; my @list;
+ for my $acc ( $self->ls_accessors ) {
+ my $allow = $self->{$acc}->[ALLOW];
+ $allow ? $hash{$acc} = $allow : push @list, $acc;
+
+ ### is this an alias?
+ if( my $org = $self->{ $acc }->[ ALIAS ] ) {
+ $clone->___alias( $acc => $org );
+ }
+ }
+
+ ### copy the accessors from $self to $clone
+ $clone->mk_accessors( \%hash ) if %hash;
+ $clone->mk_accessors( @list ) if @list;
+
+ ### copy callbacks
+ #$clone->{"$clone"} = $self->{"$self"} if $self->{"$self"};
+ $clone->___callback( $self->___callback );
+
+ return $clone;
+}
+
+=head2 $bool = $self->mk_flush;
+
+Flushes all the data from the current object; all accessors will be
+set back to their default state of C<undef>.
+
+Returns true on success and false on failure.
+
+=cut
+
+sub mk_flush {
+ my $self = $_[0];
+
+ # set each accessor's data to undef
+ $self->{$_}->[VALUE] = undef for $self->ls_accessors;
+
+ return 1;
+}
+
+=head2 $bool = $self->mk_verify;
+
+Checks if all values in the current object are in accordance with their
+own allow handler. Specifically useful to check if an empty initialised
+object has been filled with values satisfying their own allow criteria.
+
+=cut
+
+sub mk_verify {
+ my $self = $_[0];
+
+ my $fail;
+ for my $name ( $self->ls_accessors ) {
+ unless( allow( $self->$name, $self->ls_allow( $name ) ) ) {
+ my $val = defined $self->$name ? $self->$name : '<undef>';
+
+ __PACKAGE__->___error("'$name' ($val) is invalid");
+ $fail++;
+ }
+ }
+
+ return if $fail;
+ return 1;
+}
+
+=head2 $bool = $self->register_callback( sub { ... } );
+
+This method allows you to register a callback, that is invoked
+every time an accessor is called. This allows you to munge input
+data, access external data stores, etc.
+
+You are free to return whatever you wish. On a C<set> call, the
+data is even stored in the object.
+
+Below is an example of the use of a callback.
+
+ $object->some_method( "some_value" );
+
+ my $callback = sub {
+ my $self = shift; # the object
+ my $meth = shift; # "some_method"
+ my $val = shift; # ["some_value"]
+ # could be undef -- check 'exists';
+ # if scalar @$val is empty, it was a 'get'
+
+ # your code here
+
+ return $new_val; # the value you want to be set/returned
+ }
+
+To access the values stored in the object, circumventing the
+callback structure, you should use the C<___get> and C<___set> methods
+documented further down.
+
+=cut
+
+sub register_callback {
+ my $self = shift;
+ my $sub = shift or return;
+
+ ### use the memory address as key, it's not used EVER as an
+ ### accessor --kane
+ $self->___callback( $sub );
+
+ return 1;
+}
+
+
+=head2 $bool = $self->can( METHOD_NAME )
+
+This method overrides C<UNIVERAL::can> in order to provide coderefs to
+accessors which are loaded on demand. It will behave just like
+C<UNIVERSAL::can> where it can -- returning a class method if it exists,
+or a closure pointing to a valid accessor of this particular object.
+
+You can use it as follows:
+
+ $sub = $object->can('some_accessor'); # retrieve the coderef
+ $sub->('foo'); # 'some_accessor' now set
+ # to 'foo' for $object
+ $foo = $sub->(); # retrieve the contents
+ # of 'some_accessor'
+
+See the C<SYNOPSIS> for more examples.
+
+=cut
+
+### custom 'can' as UNIVERSAL::can ignores autoload
+sub can {
+ my($self, $method) = @_;
+
+ ### it's one of our regular methods
+ if( $self->UNIVERSAL::can($method) ) {
+ __PACKAGE__->___debug( "Can '$method' -- provided by package" );
+ return $self->UNIVERSAL::can($method);
+ }
+
+ ### it's an accessor we provide;
+ if( UNIVERSAL::isa( $self, 'HASH' ) and exists $self->{$method} ) {
+ __PACKAGE__->___debug( "Can '$method' -- provided by object" );
+ return sub { $self->$method(@_); }
+ }
+
+ ### we don't support it
+ __PACKAGE__->___debug( "Cannot '$method'" );
+ return;
+}
+
+### don't autoload this
+sub DESTROY { 1 };
+
+### use autoload so we can have per-object accessors,
+### not per class, as that is incorrect
+sub AUTOLOAD {
+ my $self = shift;
+ my($method) = ($AUTOLOAD =~ /([^:']+$)/);
+
+ my $val = $self->___autoload( $method, @_ ) or return;
+
+ return $val->[0];
+}
+
+sub ___autoload {
+ my $self = shift;
+ my $method = shift;
+ my $assign = scalar @_; # is this an assignment?
+
+ ### a method on our object
+ if( UNIVERSAL::isa( $self, 'HASH' ) ) {
+ if ( not exists $self->{$method} ) {
+ __PACKAGE__->___error("No such accessor '$method'", 1);
+ return;
+ }
+
+ ### a method on something else, die with a descriptive error;
+ } else {
+ local $FATAL = 1;
+ __PACKAGE__->___error(
+ "You called '$AUTOLOAD' on '$self' which was interpreted by ".
+ __PACKAGE__ . " as an object call. Did you mean to include ".
+ "'$method' from somewhere else?", 1 );
+ }
+
+ ### is this is an alias, redispatch to the original method
+ if( my $original = $self->{ $method }->[ALIAS] ) {
+ return $self->___autoload( $original, @_ );
+ }
+
+ ### assign?
+ my $val = $assign ? shift(@_) : $self->___get( $method );
+
+ if( $assign ) {
+
+ ### any binding?
+ if( $_[0] ) {
+ if( ref $_[0] and UNIVERSAL::isa( $_[0], 'SCALAR' ) ) {
+
+ ### tie the reference, so we get an object and
+ ### we can use it's going out of scope to restore
+ ### the old value
+ my $cur = $self->{$method}->[VALUE];
+
+ tie ${$_[0]}, __PACKAGE__ . '::TIE',
+ sub { $self->$method( $cur ) };
+
+ ${$_[0]} = $val;
+
+ } else {
+ __PACKAGE__->___error(
+ "Can not bind '$method' to anything but a SCALAR", 1
+ );
+ }
+ }
+
+ ### need to check the value?
+ if( exists $self->{$method}->[ALLOW] ) {
+
+ ### double assignment due to 'used only once' warnings
+ local $Params::Check::VERBOSE = 0;
+ local $Params::Check::VERBOSE = 0;
+
+ allow( $val, $self->{$method}->[ALLOW] ) or (
+ __PACKAGE__->___error(
+ "'$val' is an invalid value for '$method'", 1),
+ return
+ );
+ }
+ }
+
+ ### callbacks?
+ if( my $sub = $self->___callback ) {
+ $val = eval { $sub->( $self, $method, ($assign ? [$val] : []) ) };
+
+ ### register the error
+ $self->___error( $@, 1 ), return if $@;
+ }
+
+ ### now we can actually assign it
+ if( $assign ) {
+ $self->___set( $method, $val ) or return;
+ }
+
+ return [$val];
+}
+
+=head2 $val = $self->___get( METHOD_NAME );
+
+Method to directly access the value of the given accessor in the
+object. It circumvents all calls to allow checks, callbakcs, etc.
+
+Use only if you C<Know What You Are Doing>! General usage for
+this functionality would be in your own custom callbacks.
+
+=cut
+
+### XXX O::A::lvalue is mirroring this behaviour! if this
+### changes, lvalue's autoload must be changed as well
+sub ___get {
+ my $self = shift;
+ my $method = shift or return;
+ return $self->{$method}->[VALUE];
+}
+
+=head2 $bool = $self->___set( METHOD_NAME => VALUE );
+
+Method to directly set the value of the given accessor in the
+object. It circumvents all calls to allow checks, callbakcs, etc.
+
+Use only if you C<Know What You Are Doing>! General usage for
+this functionality would be in your own custom callbacks.
+
+=cut
+
+sub ___set {
+ my $self = shift;
+ my $method = shift or return;
+
+ ### you didn't give us a value to set!
+ exists $_[0] or return;
+ my $val = shift;
+
+ ### if there's more arguments than $self, then
+ ### replace the method called by the accessor.
+ ### XXX implement rw vs ro accessors!
+ $self->{$method}->[VALUE] = $val;
+
+ return 1;
+}
+
+=head2 $bool = $self->___alias( ALIAS => METHOD );
+
+Method to directly alias one accessor to another for
+this object. It circumvents all sanity checks, etc.
+
+Use only if you C<Know What You Are Doing>!
+
+=cut
+
+sub ___alias {
+ my $self = shift;
+ my $alias = shift or return;
+ my $method = shift or return;
+
+ $self->{ $alias }->[ALIAS] = $method;
+
+ return 1;
+}
+
+sub ___debug {
+ return unless $DEBUG;
+
+ my $self = shift;
+ my $msg = shift;
+ my $lvl = shift || 0;
+
+ local $Carp::CarpLevel += 1;
+
+ carp($msg);
+}
+
+sub ___error {
+ my $self = shift;
+ my $msg = shift;
+ my $lvl = shift || 0;
+ local $Carp::CarpLevel += ($lvl + 1);
+ $FATAL ? croak($msg) : carp($msg);
+}
+
+### objects might be overloaded.. if so, we can't trust what "$self"
+### will return, which might get *really* painful.. so check for that
+### and get their unoverloaded stringval if needed.
+sub ___callback {
+ my $self = shift;
+ my $sub = shift;
+
+ my $mem = overload::Overloaded( $self )
+ ? overload::StrVal( $self )
+ : "$self";
+
+ $self->{$mem} = $sub if $sub;
+
+ return $self->{$mem};
+}
+
+=head1 LVALUE ACCESSORS
+
+C<Object::Accessor> supports C<lvalue> attributes as well. To enable
+these, you should create your objects in the designated namespace,
+C<Object::Accessor::Lvalue>. For example:
+
+ my $obj = Object::Accessor::Lvalue->new('foo');
+ $obj->foo += 1;
+ print $obj->foo;
+
+will actually print C<1> and work as expected. Since this is an
+optional feature, that's not desirable in all cases, we require
+you to explicitly use the C<Object::Accessor::Lvalue> class.
+
+Doing the same on the standard C<Object>>Accessor> class would
+generate the following code & errors:
+
+ my $obj = Object::Accessor->new('foo');
+ $obj->foo += 1;
+
+ Can't modify non-lvalue subroutine call
+
+Note that C<lvalue> support on C<AUTOLOAD> routines is a
+C<perl 5.8.x> feature. See perldoc L<perl58delta> for details.
+
+=head2 CAVEATS
+
+=over 4
+
+=item * Allow handlers
+
+Due to the nature of C<lvalue subs>, we never get access to the
+value you are assigning, so we can not check it againt your allow
+handler. Allow handlers are therefor unsupported under C<lvalue>
+conditions.
+
+See C<perldoc perlsub> for details.
+
+=item * Callbacks
+
+Due to the nature of C<lvalue subs>, we never get access to the
+value you are assigning, so we can not check provide this value
+to your callback. Furthermore, we can not distinguish between
+a C<get> and a C<set> call. Callbacks are therefor unsupported
+under C<lvalue> conditions.
+
+See C<perldoc perlsub> for details.
+
+
+=cut
+
+{ package Object::Accessor::Lvalue;
+ use base 'Object::Accessor';
+ use strict;
+ use vars qw[$AUTOLOAD];
+
+ ### constants needed to access values from the objects
+ *VALUE = *Object::Accessor::VALUE;
+ *ALLOW = *Object::Accessor::ALLOW;
+
+ ### largely copied from O::A::Autoload
+ sub AUTOLOAD : lvalue {
+ my $self = shift;
+ my($method) = ($AUTOLOAD =~ /([^:']+$)/);
+
+ $self->___autoload( $method, @_ ) or return;
+
+ ### *dont* add return to it, or it won't be stored
+ ### see perldoc perlsub on lvalue subs
+ ### XXX can't use $self->___get( ... ), as we MUST have
+ ### the container that's used for the lvalue assign as
+ ### the last statement... :(
+ $self->{$method}->[ VALUE() ];
+ }
+
+ sub mk_accessors {
+ my $self = shift;
+ my $is_hash = UNIVERSAL::isa( $_[0], 'HASH' );
+
+ $self->___error(
+ "Allow handlers are not supported for '". __PACKAGE__ ."' objects"
+ ) if $is_hash;
+
+ return $self->SUPER::mk_accessors( @_ );
+ }
+
+ sub register_callback {
+ my $self = shift;
+ $self->___error(
+ "Callbacks are not supported for '". __PACKAGE__ ."' objects"
+ );
+ return;
+ }
+}
+
+
+### standard tie class for bound attributes
+{ package Object::Accessor::TIE;
+ use Tie::Scalar;
+ use Data::Dumper;
+ use base 'Tie::StdScalar';
+
+ my %local = ();
+
+ sub TIESCALAR {
+ my $class = shift;
+ my $sub = shift;
+ my $ref = undef;
+ my $obj = bless \$ref, $class;
+
+ ### store the restore sub
+ $local{ $obj } = $sub;
+ return $obj;
+ }
+
+ sub DESTROY {
+ my $tied = shift;
+ my $sub = delete $local{ $tied };
+
+ ### run the restore sub to set the old value back
+ return $sub->();
+ }
+}
+
+=back
+
+=head1 GLOBAL VARIABLES
+
+=head2 $Object::Accessor::FATAL
+
+Set this variable to true to make all attempted access to non-existant
+accessors be fatal.
+This defaults to C<false>.
+
+=head2 $Object::Accessor::DEBUG
+
+Set this variable to enable debugging output.
+This defaults to C<false>.
+
+=head1 TODO
+
+=head2 Create read-only accessors
+
+Currently all accessors are read/write for everyone. Perhaps a future
+release should make it possible to have read-only accessors as well.
+
+=head1 CAVEATS
+
+If you use codereferences for your allow handlers, you will not be able
+to freeze the data structures using C<Storable>.
+
+Due to a bug in storable (until at least version 2.15), C<qr//> compiled
+regexes also don't de-serialize properly. Although this bug has been
+reported, you should be aware of this issue when serializing your objects.
+
+You can track the bug here:
+
+ http://rt.cpan.org/Ticket/Display.html?id=1827
+
+=head1 BUG REPORTS
+
+Please report bugs or other issues to E<lt>bug-object-accessor@rt.cpan.orgE<gt>.
+
+=head1 AUTHOR
+
+This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
+
+=head1 COPYRIGHT
+
+This library is free software; you may redistribute and/or modify it
+under the same terms as Perl itself.
+
+=cut
+
+1;
diff --git a/ext/Object-Accessor/t/00_Object-Accessor.t b/ext/Object-Accessor/t/00_Object-Accessor.t
new file mode 100644
index 0000000000..e0f2f13b62
--- /dev/null
+++ b/ext/Object-Accessor/t/00_Object-Accessor.t
@@ -0,0 +1,127 @@
+ BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Test::More 'no_plan';
+use Data::Dumper;
+
+my $Class = 'Object::Accessor';
+
+use_ok($Class);
+
+my $Object = $Class->new;
+my $Acc = 'foo';
+my $Err_re = qr/No such accessor '$Acc'/;
+
+### stupid warnings
+### XXX this will break warning tests though if enabled
+$Object::Accessor::DEBUG = $Object::Accessor::DEBUG = 1 if @ARGV;
+
+
+### check the object
+{ ok( $Object, "Object of '$Class' created" );
+ isa_ok( $Object, $Class );
+}
+
+### check non existant accessor
+{ my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ ok(!$Object->can($Acc), "Cannot '$Acc'" );
+ ok(!$Object->$Acc(), " Method '$Acc' returns false" );
+ like( $warning, $Err_re, " Warning logged" );
+
+ ### check fatal error
+ { local $Object::Accessor::FATAL = 1;
+ local $Object::Accessor::FATAL = 1; # stupid warnings
+
+ my $rv = eval { $Object->$Acc() };
+
+ ok( $@, "Cannot '$Acc' -- dies" );
+ ok(!$rv, " Method '$Acc' returns false" );
+ like( $@, $Err_re, " Fatal error logged" );
+ }
+}
+
+### create an accessor;
+{ my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ ok( $Object->mk_accessors( $Acc ),
+ "Accessor '$Acc' created" );
+
+ ok( $Object->can( $Acc ), " Can '$Acc'" );
+ ok(!$warning, " No warnings logged" );
+}
+
+### try to use the accessor
+{ for my $var ($0, $$) {
+
+ ok( $Object->$Acc( $var ), "'$Acc' set to '$var'" );
+ is( $Object->$Acc(), $var, " '$Acc' still holds '$var'" );
+
+ my $sub = $Object->can( $Acc );
+ ok( $sub, "Retrieved '$Acc' coderef" );
+ isa_ok( $sub, "CODE" );
+ is( $sub->(), $var, " '$Acc' via coderef holds '$var'" );
+
+ ok( $sub->(1), " '$Acc' set via coderef to '1'" );
+ is( $Object->$Acc(), 1, " '$Acc' still holds '1'" );
+ }
+}
+
+### get a list of accessors
+{ my @list = $Object->ls_accessors;
+ ok( scalar(@list), "Accessors retrieved" );
+
+ for my $acc ( @list ) {
+ ok( $Object->can( $acc ), " Accessor '$acc' is valid" );
+ }
+
+ is_deeply( \@list, [$Acc], " Only expected accessors found" );
+}
+
+### clone the original
+{ my $clone = $Object->mk_clone;
+ my @list = $clone->ls_accessors;
+
+ ok( $clone, "Clone created" );
+ isa_ok( $clone, $Class );
+ ok( scalar(@list), " Clone has accessors" );
+ is_deeply( \@list, [$Object->ls_accessors],
+ " Only expected accessors found" );
+
+ for my $acc ( @list ) {
+ ok( !defined( $clone->$acc() ),
+ " Accessor '$acc' is empty" );
+ }
+}
+
+### flush the original values
+{ my $val = $Object->$Acc();
+ ok( $val, "Objects '$Acc' has a value" );
+
+ ok( $Object->mk_flush, " Object flushed" );
+ ok( !$Object->$Acc(), " Objects '$Acc' is now empty" );
+}
+
+### check that only our original object can do '$Acc'
+{ my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ my $other = $Class->new;
+
+
+ ok(!$other->can($Acc), "Cannot '$Acc' via other object" );
+ ok(!$other->$Acc(), " Method '$Acc' returns false" );
+ like( $warning, $Err_re, " Warning logged" );
+}
+
+### check if new() passes it's args correctly
+{ my $obj = $Class->new( $Acc );
+ ok( $obj, "Object created with accessors" );
+ isa_ok( $obj, $Class );
+ can_ok( $obj, $Acc );
+}
+
+1;
diff --git a/ext/Object-Accessor/t/01_Object-Accessor-Subclassed.t b/ext/Object-Accessor/t/01_Object-Accessor-Subclassed.t
new file mode 100644
index 0000000000..8ebe7f1343
--- /dev/null
+++ b/ext/Object-Accessor/t/01_Object-Accessor-Subclassed.t
@@ -0,0 +1,51 @@
+BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Test::More 'no_plan';
+use Data::Dumper;
+
+my $Class = 'Object::Accessor';
+my $MyClass = 'My::Class';
+my $Acc = 'foo';
+
+use_ok($Class);
+
+### establish another package that subclasses our own
+{ package My::Class;
+ use base 'Object::Accessor';
+}
+
+my $Object = $MyClass->new;
+
+### check the object
+{ ok( $Object, "Object created" );
+ isa_ok( $Object, $MyClass );
+ isa_ok( $Object, $Class );
+}
+
+### create an accessor
+{ ok( $Object->mk_accessors( $Acc ),
+ "Accessor '$Acc' created" );
+ ok( $Object->can( $Acc ), " Object can '$Acc'" );
+ ok( $Object->$Acc(1), " Objects '$Acc' set" );
+ ok( $Object->$Acc(), " Objects '$Acc' retrieved" );
+}
+
+### check if we do the right thing when we call an accessor that's
+### not a defined function in the base class, and not an accessors
+### in the object either
+{ my $sub = eval { $MyClass->can( $$ ); };
+
+ ok( !$sub, "No sub from non-existing function" );
+ ok( !$@, " Code handled it gracefully" );
+}
+
+### check if a method called on a class, that's not actually there
+### doesn't get confused as an object call;
+{ eval { $MyClass->$$ };
+
+ ok( $@, "Calling '$$' on '$MyClass' dies" );
+ like( $@, qr/from somewhere else/,
+ " Dies with an informative message" );
+}
diff --git a/ext/Object-Accessor/t/02_Object-Accessor-allow.t b/ext/Object-Accessor/t/02_Object-Accessor-allow.t
new file mode 100644
index 0000000000..396ef2b6f0
--- /dev/null
+++ b/ext/Object-Accessor/t/02_Object-Accessor-allow.t
@@ -0,0 +1,82 @@
+BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Test::More 'no_plan';
+use Data::Dumper;
+
+my $Class = 'Object::Accessor';
+
+use_ok($Class);
+
+my $Object = $Class->new;
+my $Acc = 'foo';
+my $Allow = qr/^\d+$/;
+my $Err_re = qr/is an invalid value for '$Acc'/;
+my ($Ver_re) = map { qr/$_/ } quotemeta qq['$Acc' (<undef>) is invalid];
+
+### stupid warnings
+### XXX this will break warning tests though if enabled
+$Object::Accessor::DEBUG = $Object::Accessor::DEBUG = 1 if @ARGV;
+
+
+### check the object
+{ ok( $Object, "Object of '$Class' created" );
+ isa_ok( $Object, $Class );
+}
+
+### create an accessor;
+{ my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ ok( $Object->mk_accessors( { $Acc => $Allow } ),
+ "Accessor '$Acc' created" );
+
+ ok( $Object->can( $Acc ), " Can '$Acc'" );
+ ok(!$warning, " No warnings logged" );
+ is( $Object->ls_allow( $Acc ), $Allow,
+ " Proper allow handler stored" );
+
+
+}
+
+### try to use the accessor
+{ ### bad
+ { my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ ok( !$Object->$Acc( $0 ), "'$Acc' NOT set to '$0'" );
+ is( $Object->$Acc(), undef, " '$Acc' still holds '<undef>'" );
+ like( $warning, $Err_re, " Warnings logged" );
+
+ ### reset warnings;
+ undef $warning;
+
+
+ my $ok = $Object->mk_verify;
+ ok( !$ok, " Internal verify fails" );
+ like( $warning, $Ver_re, " Warning logged" );
+ }
+
+ $Object->mk_flush;
+
+ ### good
+ { my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ ok( $Object->$Acc( $$ ), "'$Acc' set to '$$'" );
+ is( $Object->$Acc(), $$, " '$Acc' still holds '$$'" );
+ ok(!$warning, " No warnings logged" );
+
+ ### reset warnings;
+ undef $warning;
+
+ my $ok = $Object->mk_verify;
+ ok( $ok, " Internal verify succeeds" );
+ ok( !$warning, " No warnings" );
+
+ }
+
+ $Object->mk_flush;
+
+}
diff --git a/ext/Object-Accessor/t/03_Object-Accessor-local.t b/ext/Object-Accessor/t/03_Object-Accessor-local.t
new file mode 100644
index 0000000000..f085683c3a
--- /dev/null
+++ b/ext/Object-Accessor/t/03_Object-Accessor-local.t
@@ -0,0 +1,50 @@
+BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Test::More 'no_plan';
+use Data::Dumper;
+
+my $Class = 'Object::Accessor';
+
+use_ok($Class);
+
+my $Object = $Class->new;
+my $Acc = 'foo';
+
+### stupid warnings
+### XXX this will break warning tests though if enabled
+$Object::Accessor::DEBUG = $Object::Accessor::DEBUG = 1 if @ARGV;
+
+
+### check the object
+{ ok( $Object, "Object of '$Class' created" );
+ isa_ok( $Object, $Class );
+}
+
+### create an accessor;
+{ my $warning;
+ local $SIG{__WARN__} = sub { $warning .= "@_" };
+
+ ok( $Object->mk_accessors( $Acc ),
+ "Accessor '$Acc' created" );
+
+ ok( $Object->can( $Acc ), " Can '$Acc'" );
+ ok(!$warning, " No warnings logged" );
+
+
+}
+
+### scoped variables
+{ ok( 1, "Testing scoped values" );
+
+ $Object->$Acc( $$ );
+ is( $Object->$Acc, $$, " Value set to $$" );
+
+ ### set it to a scope
+ { $Object->$Acc( $0 => \my $temp );
+ is( $Object->$Acc, $0, " Value set to $0" );
+ }
+
+ is( $Object->$Acc, $$, " Value restored to $$" );
+}
diff --git a/ext/Object-Accessor/t/04_Object-Accessor-lvalue.t b/ext/Object-Accessor/t/04_Object-Accessor-lvalue.t
new file mode 100644
index 0000000000..092c74169a
--- /dev/null
+++ b/ext/Object-Accessor/t/04_Object-Accessor-lvalue.t
@@ -0,0 +1,82 @@
+BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Data::Dumper;
+
+BEGIN {
+ require Test::More;
+ Test::More->import(
+ # silly bbedit [
+ $] >= 5.008
+ ? 'no_plan'
+ : ( skip_all => "Lvalue objects require perl >= 5.8" )
+ );
+}
+
+my $Class = 'Object::Accessor';
+my $LClass = $Class . '::Lvalue';
+
+use_ok($Class);
+
+my $Object = $LClass->new;
+my $Acc = 'foo';
+
+### stupid warnings
+### XXX this will break warning tests though if enabled
+$Object::Accessor::DEBUG = $Object::Accessor::DEBUG = 1 if @ARGV;
+
+
+### check the object
+{ ok( $Object, "Object of '$LClass' created" );
+ isa_ok( $Object, $LClass );
+ isa_ok( $Object, $Class );
+ ok( $Object->mk_clone, " Object cloned" );
+}
+
+### create an accessor;
+{ ok( $Object->mk_accessors( $Acc ),
+ "Accessor '$Acc' created" );
+
+ eval { $Object->$Acc = $$ };
+ ok( !$@, "lvalue assign successful $@" );
+ ok( $Object->$Acc, "Accessor '$Acc' set" );
+ is( $Object->$Acc, $$, " Contains proper value" );
+}
+
+### test allow handlers
+{ my $acc = 'bar';
+ my $clone = $Object->mk_clone;
+
+ ok( $clone, "Cloned the lvalue object" );
+
+ ### lets see if this causes a warning
+ { my $warnings;
+ local $SIG{__WARN__} = sub { $warnings .= "@_" };
+
+ ok( $clone->mk_accessors({ $acc => sub { 0 } }),
+ " Created accessor '$acc'" );
+ like( $warnings, qr/not supported/,
+ " Got warning about allow handlers" );
+ }
+
+ ok( eval{ $clone->$acc = $$ },
+ " Allow handler ignored" );
+ ok( ! $@, " No error occurred" );
+ is( $clone->$acc, $$, " Setting '$acc' worked" );
+}
+
+### test registering callbacks
+{ my $clone = $Object->mk_clone;
+ ok( $clone, "Cloned the lvalue object" );
+
+ { my $warnings;
+ local $SIG{__WARN__} = sub { $warnings .= "@_" };
+ ok( ! $clone->register_callback( sub { } ),
+ "Callback not registered" );
+
+ like( $warnings, qr/not supported/,
+ " Got warning about callbacks" );
+ }
+}
+
diff --git a/ext/Object-Accessor/t/05_Object-Accessor-callback.t b/ext/Object-Accessor/t/05_Object-Accessor-callback.t
new file mode 100644
index 0000000000..5411bbdd0d
--- /dev/null
+++ b/ext/Object-Accessor/t/05_Object-Accessor-callback.t
@@ -0,0 +1,97 @@
+BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Test::More 'no_plan';
+use Data::Dumper;
+
+my $Class = 'Object::Accessor';
+my $LClass = $Class . '::Lvalue';
+
+use_ok($Class);
+
+### stupid warnings
+### XXX this will break warning tests though if enabled
+$Object::Accessor::DEBUG = $Object::Accessor::DEBUG = 1 if @ARGV;
+
+my $Object = $Class->new;
+my $Acc = 'foo';
+my $Func = 'register_callback';
+my $Called = 0;
+my $RetVal = $$;
+my $SetVal = 1;
+
+### 6 tests
+my $Sub = sub {
+ my $obj = shift;
+ my $meth = shift;
+ my $val = shift;
+
+ $Called++;
+
+ ok( 1, " In callback now" );
+ ok( $obj, " Object received" );
+ isa_ok( $obj, $Class, " Object");
+ is( $meth, $Acc, " Method is '$Acc'" );
+ isa_ok( $val, "ARRAY", " Value" );
+ scalar @$val
+ ? is( $val->[0], $SetVal,
+ " Attempted to set $SetVal" )
+ : ok( ! exists $val->[0],
+ " This was a GET request" );
+
+ return $RetVal;
+};
+
+### set up the object
+{ ok( $Object, "Object created" );
+ isa_ok( $Object, $Class );
+ ok( $Object->mk_accessors( $Acc ),
+ " Accessor '$Acc' created" );
+ can_ok( $Object, $Func );
+ ok( $Object->$Func( $Sub ), " Callback registered" );
+}
+
+### test ___get and ___set
+{ $Called = 0;
+
+ my $clone = $Object->mk_clone;
+ ok( $clone, "Object cloned" );
+
+ my $val = $clone->___get($Acc);
+ is( $val, undef, " Direct get returns <undef>" );
+ ok( $clone->___set( $Acc => $SetVal ),
+ " Direct set is able to set the value" );
+ is( $clone->___get( $Acc ), $SetVal,
+ " Direct get returns $SetVal" );
+ ok( !$Called, " Callbacks didn't get called" );
+}
+
+### test callbacks on regular objects
+### XXX callbacks DO NOT work on lvalue objects. This is verified
+### in the lvalue test file, so we dont test here
+{ #diag("Running GET tests on regular objects");
+
+ my $clone = $Object->mk_clone;
+
+ $Called = 0;
+ is( $clone->$Acc, $RetVal, " Method '$Acc' returns '$RetVal' " );
+ is( $clone->___get($Acc), undef,
+ " Direct get returns <undef>" );
+ ok( $Called, " Callback called" );
+
+
+ #diag("Running SET tests on regular objects");
+ $Called = 0;
+ ok( $clone->$Acc($SetVal), " Setting $Acc" );
+ ok( $Called, " Callback called" );
+
+ $Called = 0;
+ is( $clone->$Acc, $RetVal, " Returns $RetVal" );
+ ok( $Called, " Callback called" );
+
+ $Called = 0;
+ is( $clone->___get( $Acc ), $RetVal,
+ " Direct get returns $RetVal" );
+ ok( !$Called, " Callback not called" );
+}
diff --git a/ext/Object-Accessor/t/06_Object-Accessor-alias.t b/ext/Object-Accessor/t/06_Object-Accessor-alias.t
new file mode 100644
index 0000000000..2a8aa81f0d
--- /dev/null
+++ b/ext/Object-Accessor/t/06_Object-Accessor-alias.t
@@ -0,0 +1,33 @@
+BEGIN { chdir 't' if -d 't' };
+
+use strict;
+use lib '../lib';
+use Test::More 'no_plan';
+use Data::Dumper;
+
+my $Class = 'Object::Accessor';
+
+use_ok($Class);
+
+my $Object = $Class->new;
+my $Acc = 'foo';
+my $Alias = 'bar';
+
+### basic sanity test
+{ ok( $Object, "Object created" );
+
+ ok( $Object->mk_accessors( $Acc ),
+ " Accessor ->$Acc created" );
+ ok( $Object->$Acc( $$ ), " ->$Acc set to $$" );
+}
+
+### alias tests
+{ ok( $Object->mk_aliases( $Alias => $Acc ),
+ "Alias ->$Alias => ->$Acc" );
+ ok( $Object->$Alias, " ->$Alias returns value" );
+ is( $Object->$Acc, $Object->$Alias,
+ " ->$Alias eq ->$Acc" );
+ ok( $Object->$Alias( $0 ), " Set value via alias ->$Alias" );
+ is( $Object->$Acc, $Object->$Alias,
+ " ->$Alias eq ->$Acc" );
+}