The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Class::Tie::InsideOut - Inside-out objects on the cheap using tied hashes

SYNOPSIS

  package MyClass;

  use Class::Tie::InsideOut;

  our @ISA = qw( Class::Tie::InsideOut );

  our %GoodKey;

  sub bad_method {
    my $self = shift;
    return $self->{BadKey}; # this won't work
  }

  sub good_method {
    my $self = shift;
    return $self->{GoodKey}; # %GoodKey is defined
  }

DESCRIPTION

This module is a proof-of-concept for implementing inside-out objects using tied hashes. It makes use of the Tie::InsideOut package to tie hash keys to hashes in the calling package's namespace.

Fields are accessed as hash keys, so in traditional Perl objects can be easily converted into inside-out objects.

To use, inherit our class from Class::Tie::InsideOut and then specify the legal keys for your object to use as hashes within the classes namespace:

  package MyClass;

  use Class::Tie::InsideOut;

  our @ISA = qw( Class::Tie::InsideOut );

  our (%Field1, %Field2, %Field3 ); # Fields used by MyClass

Note that your keys must be specified as our variables so that they are accessible from outside of the class, and not as my variables!

Fields are accessed as hash keys from the object reference:

  sub method {
    my $self = shift;
    if (@_) {
      $self->{Field1} = shift;
    }
    else {
      return $self->{Field1};
    }
  }

Converting a Perl module which uses "traditional" objects into one which uses inside-out objects can be a matter of adding Class::Tie::InsideOut to the @ISA list and adding the field names as global hashes.

However, if child classes do not use parent class methods to access fields in the parent class, then there will be problems. See the "KNOWN ISSUES" section below.

Serialization and Cloning

You can use Storable to serialize clone objects, since there are hooks in Tie::InsideOut which allow for this. To add a clone method to your class:

  use Storable qw( dclone );

  ...

  sub clone {
    my $self = shift;
    my $clone = dclone($self);
    return $clone;
  }

But be aware that if the structure of parent classes are changed, then you may not be able to deserialize objects. (The same can happen with tradititional classes, but Tie::InsideOut will catch this and return an error.)

KNOWN ISSUES

When a class is inherited from from a Class::Tie::InsideOut class, then it too must be an inside out class and have the fields defined as global hashes. This will affect inherited classes downstream.

Child classes cannot directly access the fields of parent classes. They must use appropriate accessor methods from the parent classes. If they create duplicate field names, then those fields can only be accessed from within the those classes.

As a consequence of this, objects may not be serializable or clonable out of the box. Packages such as Clone and Data::Dumper will not work properly.

To use with packages which generate accessor methods such as Class::Accessor with this, you'll need to define the set and get methods inside of your class.

Accessor-generating packages which do not make use of an intermediate method are not compatible with this package. This is partly a Perl issue: the caller information from closures reflects the namespace of the package that created the closure, not the actual package that the closure resides. However, the issue is fixable. The subroutine needs to set its namespace:

  $accessor = sub {
    local *__ANON__ = "${class}::${field}";
    my $self = shift;
    ...
  };

Another alternative is to use Sub::Name to rename subroutines:

  use Sub::Name;

  $accessor = subname "${class}::${field}" => sub {
    my $self = shift;
    ...
  };

However, Sub::Name uses XS and is not a pure-Perl solution.

This version does little checking of the key names, beyond that there is a global hash variable with that name in the namespace of the method that uses it. It might be a hash intended as a field, or it might be one intended for something else. (You could hide them by specifying them as my variables, though.)

There are no checks against using the name of a tied Tie::InsideOut or Class::Tie::InsideOut global hash variable as a key for itself, which has unpredicable (and possibly dangerous) results.

SEE ALSO

This module is a wrapper for Tie::InsideOut.

There are various other inside-out object packages on CPAN. Among them:

  Class::InsideOut
  Class::Std
  Object::InsideOut

AUTHOR

Robert Rothenberg <rrwo at cpan.org>

Acknowledgements

Thanks to Ovid (via Chromatic) and Steven Little for advice in PerlMonks on the namespace issues with Class::Accessor.

Suggestions and Bug Reporting

Feedback is always welcome. Please use the CPAN Request Tracker at http://rt.cpan.org to submit bug reports.

STATUS

This module has not been seriously updated since 2006, and inside-out objects have largely fallen out of favor since then.

It has been marked as ADOPTME on CPAN.

LICENSE

Copyright (c) 2006,2014 Robert Rothenberg. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.