Jerry D. Hedden

NAME

Object::InsideOut - Comprehensive inside-out object support module

VERSION

This document describes Object::InsideOut version 1.36

SYNOPSIS

 package My::Class; {
     use Object::InsideOut;

     # Numeric field with combined get+set accessor
     my @data :Field('Accessor' => 'data', 'Type' => 'NUMERIC');

     # Takes 'DATA' (or 'data', etc.) as a manatory parameter to ->new()
     my %init_args :InitArgs = (
         'DATA' => {
             'Regex'     => qr/^data$/i,
             'Mandatory' => 1,
             'Type'      => 'NUMERIC',
         },
     );

     # Handle class specific args as part of ->new()
     sub init :Init
     {
         my ($self, $args) = @_;

         $self->set(\@data, $args->{'DATA'});
     }
 }

 package My::Class::Sub; {
     use Object::InsideOut qw(My::Class);

     # List field with standard 'get_X' and 'set_X' accessors
     my @info :Field('Standard' => 'info', 'Type' => 'LIST');

     # Takes 'INFO' as an optional list parameter to ->new()
     # Value automatically added to @info array
     # Defaults to [ 'empty' ]
     my %init_args :InitArgs = (
         'INFO' => {
             'Type'    => 'LIST',
             'Field'   => \@info,
             'Default' => 'empty',
         },
     );
 }

 package main;

 my $obj = My::Class::Sub->new('Data' => 69);
 my $info = $obj->get_info();               # [ 'empty' ]
 my $data = $obj->data();                   # 69
 $obj->data(42);
 $data = $obj->data();                      # 42

 $obj = My::Class::Sub->new('INFO' => 'help', 'DATA' => 86);
 $data = $obj->data();                      # 86
 $info = $obj->get_info();                  # [ 'help' ]
 $obj->set_info(qw(foo bar baz));
 $info = $obj->get_info();                  # [ 'foo', 'bar', 'baz' ]

DESCRIPTION

This module provides comprehensive support for implementing classes using the inside-out object model.

This module implements inside-out objects as anonymous scalar references that are blessed into a class with the scalar containing the ID for the object (usually a sequence number). For Perl 5.8.3 and later, the scalar reference is set as readonly to prevent accidental modifications to the ID. Object data (i.e., fields) are stored within the class's package in either arrays indexed by the object's ID, or hashes keyed to the object's ID.

The virtues of the inside-out object model over the blessed hash object model have been extolled in detail elsewhere. See the informational links under "SEE ALSO". Briefly, inside-out objects offer the following advantages over blessed hash objects:

  • Encapsulation

    Object data is enclosed within the class's code and is accessible only through the class-defined interface.

  • Field Name Collision Avoidance

    Inheritance using blessed hash classes can lead to conflicts if any classes use the same name for a field (i.e., hash key). Inside-out objects are immune to this problem because object data is stored inside each class's package, and not in the object itself.

  • Compile-time Name Checking

    A common error with blessed hash classes is the misspelling of field names:

     $obj->{'coment'} = 'Say what?';   # Should be 'comment' not 'coment'

    As there is no compile-time checking on hash keys, such errors do not usually manifest themselves until runtime.

    With inside-out objects, data is accessed using methods, the names of which are checked by the Perl compiler such that any typos are easily caught using perl -c.

This module offers all the capabilities of Class::Std with the following additional key advantages:

  • Speed

    When using arrays to store object data, Object::InsideOut objects are as much as 40% faster than blessed hash objects for fetching and setting data, and even with hashes they are still several percent faster than blessed hash objects.

    For the same types of operations, Object::InsideOut objects are from 2 to 6 times faster than Class::Std objects.

  • Threads

    Object::InsideOut is thread safe, and thoroughly supports sharing objects between threads using threads::shared. Class::Std is not usable in threaded applications (or applications that use fork under ActivePerl).

  • Flexibility

    Allows control over object ID specification, accessor naming, parameter name matching, and more.

  • Runtime Support

    Supports classes that may be loaded at runtime (i.e., using eval { require ...; };). This makes it usable from within mod_perl, as well. Also supports dynamic creation of object fields during runtime.

  • Perl 5.6

    Usable with Perl 5.6.0 and later. Class::Std is only usable with Perl 5.8.1 and later.

  • Exception Objects

    As recommended in Perl Best Practices, Object::InsideOut uses Exception::Class for handling errors in an OO-compatible manner.

  • Object Serialization

    Object::InsideOut has built-in support for object dumping and reloading that can be accomplished in either an automated fashion or through the use of class-supplied subroutines. Serialization using Storable is also supported.

  • Foreign Class Inheritance

    Object::InsideOut allows classes to inherit from foreign (i.e., non-Object::InsideOut) classes, thus allowing you to sub-class other Perl class, and access their methods from your own objects.

Class Declarations

To use this module, your classes will start with use Object::InsideOut;:

 package My::Class: {
     use Object::InsideOut;
     ...
 }

Sub-classes inherit from base classes by telling Object::InsideOut what the parent class is:

 package My::Sub {
     use Object::InsideOut qw(My::Parent);
     ...
 }

Multiple inheritance is also supported:

 package My::Project {
     use Object::InsideOut qw(My::Class Another::Class);
     ...
 }

Object::InsideOut acts as a replacement for the base pragma: It loads the parent module(s), calls their import functions, and sets up the sub-class's @ISA array. Therefore, you should not use base ... yourself, or try to set up @ISA arrays.

If a parent class takes parameters (e.g., symbols to be exported via Exporter), enclose them in an array ref (mandatory) following the name of the parent class:

 package My::Project {
     use Object::InsideOut 'My::Class'      => [ 'param1', 'param2' ],
                           'Another::Class' => [ 'param' ];
     ...
 }

Field Declarations

Object data fields consist of arrays within a class's package into which data are stored using the object's ID as the array index. An array is declared as being an object field by following its declaration with the :Field attribute:

 my @info :Field;

Object data fields may also be hashes:

 my %data :Field;

However, as array access is as much as 40% faster than hash access, you should stick to using arrays. (See "Object ID" concerning when hashes may be required.)

(The case of the word Field does not matter, but by convention should not be all lowercase.)

Object Creation

Objects are created using the ->new() method which is exported by Object::InsideOut to each class:

 my $obj = My::Class->new();

Classes do not (normally) implement their own ->new() method. Class-specific object initialization actions are handled by :Init labeled methods (see "Object Initialization").

Parameters are passed in as combinations of key => value pairs and/or hash refs:

 my $obj = My::Class->new('param1' => 'value1');
     # or
 my $obj = My::Class->new({'param1' => 'value1'});
     # or even
 my $obj = My::Class->new(
     'param_X' => 'value_X',
     'param_Y' => 'value_Y',
     {
         'param_A' => 'value_A',
         'param_B' => 'value_B',
     },
     {
         'param_Q' => 'value_Q',
     },
 );

Additionally, parameters can be segregated in hash refs for specific classes:

 my $obj = My::Class->new(
     'foo' => 'bar',
     'My::Class'      => { 'param' => 'value' },
     'Parent::Class'  => { 'data'  => 'info'  },
 );

The initialization methods for both classes in the above will get 'foo' => 'bar', My::Class will also get 'param' => 'value', and Parent::Class will also get 'data' => 'info'. In this scheme, class-specific parameters will override general parameters specified at a higher level:

 my $obj = My::Class->new(
     'default' => 'bar',
     'Parent::Class'  => { 'default' => 'baz' },
 );

My::Class will get 'default' => 'bar', and Parent::Class will get 'default' => 'baz'.

Calling new on an object works, too, and operates the same as calling new for the class of the object (i.e., $obj->new() is the same as ref($obj)->new()).

NOTE: You cannot create objects from Object::InsideOut itself:

 # This is an error
 # my $obj = Object::InsideOut->new();

In this way, Object::InsideOut is not an object class, but functions more like a pragma.

Object Cloning

Copies of objects can be created using the ->clone() method which is exported by Object::InsideOut to each class:

 my $obj2 = $obj->clone();

When called without arguments, ->clone() creates a shallow copy of the object, meaning that any complex data structures (i.e., refs) stored in the object will be shared with its clone.

Calling ->clone() with a true argument:

 my $obj2 = $obj->clone(1);

creates a deep copy of the object such that no internal data is shared between the objects.

Deep cloning can also be controlled at the field level. See "Field Cloning" below for more details.

Object Initialization

Object initialization is accomplished through a combination of an :InitArgs labeled hash (explained in detail in the next section), and an :Init labeled subroutine.

The :InitArgs labeled hash specifies the parameters to be extracted from the argument list supplied to the ->new() method. These parameters are then sent to the :Init labeled subroutine for processing:

 package My::Class; {
     my @my_field :Field;

     my %init_args :InitArgs = (
         'MY_PARAM' => qr/MY_PARAM/i,
     );

     sub _init :Init
     {
         my ($self, $args) = @_;

         if (exists($args->{'MY_PARAM'})) {
             $self->set(\@my_field, $args->{'MY_PARAM'});
         }
     }
 }

 package main;

 my $obj = My::Class->new('my_param' => 'data');

(The case of the words InitArgs and Init does not matter, but by convention should not be all lowercase.)

This :Init labeled subroutine will receive two arguments: The newly created object requiring further initialization (i.e., $self), and a hash ref of supplied arguments that matched :InitArgs specifications.

Data processed by the subroutine may be placed directly into the class's field arrays (hashes) using the object's ID (i.e., $$self):

 $my_field[$$self] = $args->{'MY_PARAM'};

However, it is strongly recommended that you use the ->set() method:

 $self->set(\@my_field, $args->{'MY_PARAM'});

which handles converting the data to a shared format when needed for applications using threads::shared.

Object Initialization Argument Specifications

The parameters to be handled by the ->new() method are specified in a hash that is labeled with the :InitArgs attribute.

The simplest parameter specification is just a tag:

 my %init_args :InitArgs = (
     'DATA' => '',
 );

In this case, if a key => value pair with an exact match of DATA for the key is found in the arguments sent to the ->new() method, then 'DATA' => value will be included in the argument hash ref sent to the :Init labeled subroutine.

Rather than counting on exact matches, regular expressions can be used to specify the parameter:

 my %init_args :InitArgs = (
     'Param' => qr/^PARA?M$/i,
 );

In this case, the argument key could be any of the following: PARAM, PARM, Param, Parm, param, parm, and so on. If a match is found, then 'Param' => value is sent to the :Init subroutine. Note that the :InitArgs hash key is substituted for the original argument key. This eliminates the need for any parameter key pattern matching within the :Init subroutine.

With more complex parameter specifications, the syntax changes. Mandatory parameters are declared as follows:

 my %init_args :InitArgs = (
     # Mandatory parameter requiring exact matching
     'INFO' => {
         'Mandatory' => 1,
     },
     # Mandatory parameter with pattern matching
     'input' => {
         'Regex'     => qr/^in(?:put)?$/i,
         'Mandatory' => 1,
     },
 );

If a mandatory parameter is missing from the argument list to new, an error is generated.

For optional parameters, defaults can be specified:

 my %init_args :InitArgs = (
     'LEVEL' => {
         'Regex'   => qr/^lev(?:el)?|lvl$/i,
         'Default' => 3,
     },
 );

The parameter's type can also be specified:

 my %init_args :InitArgs = (
     'LEVEL' => {
         'Regex'   => qr/^lev(?:el)?|lvl$/i,
         'Default' => 3,
         'Type'    => 'Numeric',
     },
 );

Available types are:

Numeric

Can also be specified as Num or Number. This uses Scalar::Util::looks_like_number to test the input value.

List

This type permits a single value (that is then placed in an array ref) or an array ref.

A class name

The parameter's type must be of the specified class. For example, My::Class.

Other reference type

The parameter's type must be of the specified reference type (as returned by ref()). For example, CODE.

The first two types above are case-insensitive (e.g., 'NUMERIC', 'Numeric', 'numeric', etc.); the last two are case-sensitive.

The Type keyword can also be paired with a code reference to provide custom type checking. The code ref can either be in the form of an anonymous subroutine, or it can be derived from a (publicly accessible) subroutine. The result of executing the code ref on the initializer should be a boolean value.

 package My::Class; {
     use Object::InsideOut;

     # For initializer type checking, the subroutine can NOT be made 'Private'
     sub is_int {
         my $arg = $_[0];
         return (Scalar::Util::looks_like_number($arg) &&
                 (int($arg) == $arg));
     }

     my @level   :Field;
     my @comment :Field;

     my %init_args :InitArgs = (
         'LEVEL' => {
             'Field' => \@level,
             # Type checking using a named subroutine
             'Type'  => \&is_int,
         },
         'COMMENT' => {
             'Field' => \@comment,
             # Type checking using an anonymous subroutine
             'Type'  => sub { $_[0] ne '' }
         },
     );
 }

You can specify automatic processing for a parameter's value such that it is placed directly info a field hash and not sent to the :Init subroutine:

 my @hosts :Field;

 my %init_args :InitArgs = (
     'HOSTS' => {
         # Allow 'host' or 'hosts' - case-insensitive
         'Regex'     => qr/^hosts?$/i,
         # Mandatory parameter
         'Mandatory' => 1,
         # Allow single value or array ref
         'Type'      => 'List',
         # Automatically put the parameter into @hosts
         'Field'     => \@hosts,
     },
 );

In this case, when the host parameter is found, it is automatically put into the @hosts array, and a 'HOSTS' => value pair is not sent to the :Init subroutine. In fact, if you specify fields for all your parameters, then you don't even need to have an :Init subroutine! All the work will be taken care of for you.

(In the above, Regex may be Regexp or just Re, and Default may be Defaults or Def. They and the other specifier keys are case-insensitive, as well.)

Getting Data

In class code, data can be fetched directly from an object's field array (hash) using the object's ID:

 $data = $field[$$self];
     # or
 $data = $field{$$self};

Setting Data

Object::InsideOut automatically exports a method called set to each class. This method should be used in class code to put data into object field arrays/hashes whenever there is the possibility that the class code may be used in an application that uses threads::shared.

As mentioned above, data can be put directly into an object's field array (hash) using the object's ID:

 $field[$$self] = $data;
     # or
 $field{$$self} = $data;

However, in a threaded application that uses data sharing (i.e., uses threads::shared), $data must be converted into shared data so that it can be put into the field array (hash). The ->set() method handles all those details for you.

The ->set() method, requires two arguments: A reference to the object field array/hash, and the data (as a scalar) to be put in it:

 $self->set(\@field, $data);
     # or
 $self->set(\%field, $data);

To be clear, the ->set() method is used inside class code; not application code. Use it inside any object methods that set data in object field arrays/hashes.

In the event of a method naming conflict, the ->set() method can be called using its fully-qualified name:

 $self->Object::InsideOut::set(\@field, $data);

Automatic Accessor Generation

As part of the "Field Declarations", you can optionally specify the automatic generation of accessor methods.

Accessor Naming

You can specify the generation of a pair of standard-named accessor methods (i.e., prefixed by get_ and set_):

 my @data :Field('Standard' => 'data');

The above results in Object::InsideOut automatically generating accessor methods named get_data and set_data. (The keyword Standard is case-insensitive, and can be abbreviated to Std.)

You can also separately specify the get and/or set accessors:

 my @name :Field('Get' => 'name', 'Set' => 'change_name');
     # or
 my @name :Field('Get' => 'get_name');
     # or
 my @name :Field('Set' => 'new_name');

For the above, you specify the full name of the accessor(s) (i.e., no prefix is added to the given name(s)). (The Get and Set keywords are case-insensitive.)

You can specify the automatic generation of a combined get/set accessor method:

 my @comment :Field('Accessor' => 'comment');

which would be used as follows:

 # Set a new comment
 $obj->comment("I have no comment, today.");

 # Get the current comment
 my $cmt = $obj->comment();

(The keyword Accessor is case-insensitive, and can be abbreviated to Acc or can be specified as get_set or Combined or Combo or Mutator.)

Set Accessor Return Value

For any of the automatically generated methods that perform set operations, the default for the method's return value is the value being set (i.e., the new value).

The Return keyword allows you to modify the default behavior. The other options are to have the set accessor return the old (previous) value (or undef if unset):

 my @data :Field('Set' => 'set_data', 'Return' => 'Old');

or to return the object itself:

 my @data :Field('Set' => 'set_data', 'Return' => 'Object');

Returning the object from a set method allows it to be chained to other methods:

 $obj->set_data($data)->do_something();

If desired, you can explicitly specify the default behavior of returning the new value:

 my @data :Field('Set' => 'set_data', 'Return' => 'New');

(Return may be abbreviated to Ret; Previous, Prev and Prior are synonymous with Old; and Object may be abbreviated to Obj and is also synonymous with Self. All these are case-insensitive.)

Accessor Type Checking

You may, optionally, direct Object::InsideOut to add type-checking code to the set/combined accessor:

 my @level :Field('Accessor' => 'level', 'Type' => 'Numeric');

Available types are:

Numeric

Can also be specified as Num or Number. This uses Scalar::Util::looks_like_number to test the input value.

List or Array

This type permits the accessor to accept multiple values (that are then placed in an array ref) or a single array ref.

Array_ref

This specifies that the accessor can only accept a single array reference. Can also be specified as Arrayref.

Hash

This type allows multiple key => value pairs (that are then placed in a hash ref) or a single hash ref.

Hash_ref

This specifies that the accessor can only accept a single hash reference. Can also be specified as Hashref.

A class name

The accessor will only accept a value of the specified class. For example, My::Class.

Other reference type

The accessor will only accept a value of the specified reference type (as returned by ref()). For example, CODE.

The types above are case-insensitive (e.g., 'NUMERIC', 'Numeric', 'numeric', etc.), except for the last two.

The Type keyword can also be paired with a code reference to provide custom type checking. The code ref can either be in the form of an anonymous subroutine, or a full-qualified subroutine name. The result of executing the code ref on the input argument(s) should be a boolean value.

 package My::Class; {
     use Object::InsideOut;

     # For accessor type checking, the subroutine can be made 'Private'
     sub positive :Private {
         return (Scalar::Util::looks_like_number($_[0]) &&
                 ($_[0] > 0));
     }

     # Code ref is an anonymous subroutine
     # (This one checks that the argument is a SCALAR)
     my @data :Field('Accessor' => 'data', 'Type' => sub { ! ref($_[0]) } );

     # Code ref using a fully-qualified subroutine name
     my @num  :Field('Accessor' => 'num',  'Type' => \&My::Class::positive);
 }

Note that it is an error to use the Type keyword by itself, or in combination with only the Get keyword.

Due to limitations in the Perl parser, you cannot use line wrapping with the :Field attribute:

 # This doesn't work
 # my @level :Field('Get'  => 'level',
 #                  'Set'  => 'set_level',
 #                  'Type' => 'Num');

 # Must be all on one line
 my @level :Field('Get' =>'level', 'Set' => 'set_level', 'Type' => 'Num');

Weak Fields

Frequently, it is useful to store weakened references to data or objects in a field. Such a field can be declared as weak so that data (i.e., references) set via automatically generated accessors, :InitArgs, the ->set() method, etc., will automatically be weakened after being stored in the field array/hash.

 my @data :Field('Weak' => 1);

NOTE: If data in a weak field is set directly (i.e., the ->set() method is not used), then weaken() must be invoked on the stored reference afterwards:

 $field[$$self] = $data;
 Scalar::Util::weaken($field[$$self]);

Field Cloning

Object cloning can be controlled at the field level such that only specified fields are deep copied when ->clone() is called without any arguments. This is done by adding another specifier to the :Field attribute:

 my @data :Field('Clone' => 'deep');
    # or
 my @data :Field('Copy' => 'deep');
    # or
 my @data :Field('Deep' => 1);

As usual, the keywords above are case-insensitive.

Object ID

By default, the ID of an object is derived from a sequence counter for the object's class hierarchy. This should suffice for nearly all cases of class development. If there is a special need for the module code to control the object ID (see Math::Random::MT::Auto as an example), then an :ID labeled subroutine can be specified:

 sub _id :ID
 {
     my $class = $_[0];

     # Determine a unique object ID
     ...

     return ($id);
 }

The ID returned by your subroutine can be any kind of regular scalar (e.g., a string or a number). However, if the ID is something other than a low-valued integer, then you will have to architect all your classes using hashes for the object fields.

Within any class hierarchy only one class may specify an :ID subroutine.

Object Replication

Object replication occurs explicitly when the ->clone() method is called on an object, and implicitly when threads are created in a threaded application. In nearly all cases, Object::InsideOut will take care of all the details for you.

In rare cases, a class may require special handling for object replication. It must then provide a subroutine labeled with the :Replicate attribute. This subroutine will be sent three arguments: The parent and the cloned objects, and a flag:

 sub _replicate :Replicate
 {
     my ($parent, $clone, $flag) = @_;

     # Special object replication processing
     if ($clone eq 'CLONE') {
        # Handling for thread cloning
        ...
     } elsif ($clone eq 'deep') {
        # Deep copy of the parent
        ...
     } else {
        # Shallow copying
        ...
     }
 }

In the case of thread cloning, $flag will be set to 'CLONE', and the $parent object is just an un-blessed anonymous scalar reference that contains the ID for the object in the parent thread.

When invoked via the ->clone() method, $flag may be either an empty string which denotes that a shallow copy is being produced for the clone, or $flag may be set to 'deep' indicating a deep copy is being produced.

The :Replicate subroutine only needs to deal with the special replication processing needed by the object: Object::InsideOut will handle all the other details.

Object Destruction

Object::InsideOut exports a DESTROY method to each class that deletes an object's data from the object field arrays (hashes). If a class requires additional destruction processing (e.g., closing filehandles), then it must provide a subroutine labeled with the :Destroy attribute. This subroutine will be sent the object that is being destroyed:

 sub _destroy :Destroy
 {
     my $obj = $_[0];

     # Special object destruction processing
 }

The :Destroy subroutine only needs to deal with the special destruction processing: The DESTROY method will handle all the other details of object destruction.

Cumulative Methods

Normally, methods with the same name in a class hierarchy are masked (i.e., overridden) by inheritance - only the method in the most-derived class is called. With cumulative methods, this masking is removed, and the same named method is called in each of the classes within the hierarchy. The return results from each call (if any) are then gathered together into the return value for the original method call. For example,

 package My::Class; {
     use Object::InsideOut;

     sub what_am_i :Cumulative
     {
         my $self = shift;

         my $ima = (ref($self) eq __PACKAGE__)
                     ? q/I was created as a /
                     : q/My top class is /;

         return ($ima . __PACKAGE__);
     }
 }

 package My::Foo; {
     use Object::InsideOut 'My::Class';

      sub what_am_i :Cumulative
     {
         my $self = shift;

         my $ima = (ref($self) eq __PACKAGE__)
                     ? q/I was created as a /
                     : q/I'm also a /;

         return ($ima . __PACKAGE__);
     }
 }

 package My::Child; {
     use Object::InsideOut 'My::Foo';

      sub what_am_i :Cumulative
     {
         my $self = shift;

         my $ima = (ref($self) eq __PACKAGE__)
                     ? q/I was created as a /
                     : q/I'm in class /;

         return ($ima . __PACKAGE__);
     }
 }

 package main;

 my $obj = My::Child->new();
 my @desc = $obj->what_am_i();
 print(join("\n", @desc), "\n");

produces:

 My top class is My::Class
 I'm also a My::Foo
 I was created as a My::Child

When called in a list context (as in the above), the return results of cumulative methods are accumulated, and returned as a list.

In a scalar context, a results object is returned that segregates the results from the cumulative method calls by class. Through overloading, this object can then be dereferenced as an array, hash, string, number, or boolean. For example, the above could be rewritten as:

 my $obj = My::Child->new();
 my $desc = $obj->what_am_i();        # Results object
 print(join("\n", @{$desc}), "\n");   # Dereference as an array

The following uses hash dereferencing:

 my $obj = My::Child->new();
 my $desc = $obj->what_am_i();
 while (my ($class, $value) = each(%{$desc})) {
     print("Class $class reports:\n\t$value\n");
 }

and produces:

 Class My::Class reports:
         My top class is My::Class
 Class My::Child reports:
         I was created as a My::Child
 Class My::Foo reports:
         I'm also a My::Foo

As illustrated above, Cumulative methods are tagged with the :Cumulative attribute (or :Cumulative(top down)), and propagate from the top down through the class hierarchy (i.e., from the base classes down through the child classes). If tagged with :Cumulative(bottom up), they will propagated from the object's class upwards through the parent classes.

Note that this directionality is the reverse of Class::Std which defaults to bottom up, and uses BASE FIRST to mean from the base classes downward through the children. (I eschewed the use of the term BASE FIRST because I felt it was ambiguous: base could refer to the base classes at the top of the hierarchy, or the child classes at the base (i.e., bottom) of the hierarchy.)

Chained Methods

In addition to :Cumulative, Object::InsideOut provides a way of creating methods that are chained together so that their return values are passed as input arguments to other similarly named methods in the same class hierarchy. In this way, the chained methods act as though they were piped together.

For example, imagine you had a method called format_name that formats some text for display:

 package Subscriber; {
     use Object::InsideOut;

     sub format_name {
         my ($self, $name) = @_;

         # Strip leading and trailing whitespace
         $name =~ s/^\s+//;
         $name =~ s/\s+$//;

         return ($name);
     }
 }

And elsewhere you have a second class that formats the case of names:

 package Person; {
     use Lingua::EN::NameCase qw(nc);
     use Object::InsideOut;

     sub format_name {
         my ($self, $name) = @_;

         # Attempt to properly case names
         return (nc($name));
     }
 }

And you decide that you'd like to perform some formatting of your own, and then have all the parent methods apply their own formatting. Normally, if you have a single parent class, you'd just call the method directly with $self-SUPER::format_name($name)>, but if you have more than one parent class you'd have to explicitly call each method directly:

 package Customer; {
     use Object::InsideOut qw(Person Subscriber);

     sub format_name {
         my ($self, $name) = @_;

         # Compress all whitespace into a single space
         $name =~ s/\s+/ /g;

         $name = $self->Subscriber::format_name($name);
         $name = $self->Person::format_name($name);

         return $name;
     }
 }

With Object::InsideOut you'd add the :Chained attribute to each class's format_name method, and the methods will be chained together automatically:

 package Subscriber; {
     use Object::InsideOut;

     sub format_name :Chained {
         my ($self, $name) = @_;

         # Strip leading and trailing whitespace
         $name =~ s/^\s+//;
         $name =~ s/\s+$//;

         return ($name);
     }
 }

 package Person; {
     use Lingua::EN::NameCase qw(nc);
     use Object::InsideOut;

     sub format_name :Chained {
         my ($self, $name) = @_;

         # Attempt to properly case names
         return (nc($name));
     }
 }

 package Customer; {
     use Object::InsideOut qw(Person Subscriber);

     sub format_name :Chained {
         my ($self, $name) = @_;

         # Compress all whitespace into a single space
         $name =~ s/\s+/ /g;

         return ($name);
     }
 }

So passing in someone's name to format_name in Customer would cause leading and trailing whitespace to be removed, then the name to be properly cased, and finally whitespace to be compressed to a single space. The resulting $name would be returned to the caller.

The default direction is to chain methods from the base classes at the top of the class hierarchy down through the child classes. You may use the attribute :Chained(top down) to make this more explicit.

If you label the method with the :Chained(bottom up) attribute, then the chained methods are called starting with the object's class and working upwards through the class hierarchy, similar to how :Cumulative(bottom up) works.

Unlike :Cumulative methods, :Chained methods return a scalar when used in a scalar context; not a results object.

Automethods

There are significant issues related to Perl's AUTOLOAD mechanism that cause it to be ill-suited for use in a class hierarchy. Therefore, Object::InsideOut implements its own :Automethod mechanism to overcome these problems

Classes requiring AUTOLOAD-type capabilities must provided a subroutine labeled with the :Automethod attribute. The :Automethod subroutine will be called with the object and the arguments in the original method call (the same as for AUTOLOAD). The :Automethod subroutine should return either a subroutine reference that implements the requested method's functionality, or else undef to indicate that it doesn't know how to handle the request.

Using its own AUTOLOAD subroutine (which is exported to every class), Object::InsideOut walks through the class tree, calling each :Automethod subroutine, as needed, to fulfill an unimplemented method call.

The name of the method being called is passed as $_ instead of $AUTOLOAD, and does not have the class name prepended to it. If the :Automethod subroutine also needs to access the $_ from the caller's scope, it is available as $CALLER::_.

Automethods can also be made to act as "Cumulative Methods" or "Chained Methods". In these cases, the :Automethod subroutine should return two values: The subroutine ref to handle the method call, and a string designating the type of method. The designator has the same form as the attributes used to designate :Cumulative and :Chained methods:

 ':Cumulative'  or  ':Cumulative(top down)'
 ':Cumulative(bottom up)'
 ':Chained'     or  ':Chained(top down)'
 ':Chained(bottom up)'

The following skeletal code illustrates how an :Automethod subroutine could be structured:

 sub _automethod :Automethod
 {
     my $self = shift;
     my @args = @_;

     my $method_name = $_;

     # This class can handle the method directly
     if (...) {
         my $handler = sub {
             my $self = shift;
             ...
             return ...;
         };

         ### OPTIONAL ###
         # Install the handler so it gets called directly next time
         # no strict refs;
         # *{__PACKAGE__.'::'.$method_name} = $handler;
         ################

         return ($handler);
     }

     # This class can handle the method as part of a chain
     if (...) {
         my $chained_handler = sub {
             my $self = shift;
             ...
             return ...;
         };

         return ($chained_handler, ':Chained');
     }

     # This class cannot handle the method request
     return;
 }

Note: The OPTIONAL code above for installing the generated handler as a method should not be used with :Cumulative or :Chained Automethods.

Object Serialization

my $array_ref = $obj->dump();
my $string = $obj->dump(1);

Object::InsideOut exports a method called dump to each class that returns either a Perl or a string representation of the object that invokes the method.

The Perl representation is returned when ->dump() is called without arguments. It consists of an array ref whose first element is the name of the object's class, and whose second element is a hash ref containing the object's data. The object data hash ref contains keys for each of the classes that make up the object's hierarchy. The values for those keys are hash refs containing key => value pairs for the object's fields. For example:

 [
   'My::Class::Sub',
   {
     'My::Class' => {
                      'data' => 'value'
                    },
     'My::Class::Sub' => {
                           'life' => 42
                         }
   }
 ]

The name for an object field (data and life in the example above) can be specified as part of the field declaration using the NAME keyword:

 my @life :Field('Name' => 'life');

If the NAME keyword is not present, then the name for a field will be either the tag from the :InitArgs array that is associated with the field, its get method name, its set method name, or, failing all that, a string of the form ARRAY(0x...) or HASH(0x...).

When called with a true argument, ->dump() returns a string version of the Perl representation using Data::Dumper.

Note that using Data::Dumper directly on an inside-out object will not produce the desired results (it'll just output the contents of the scalar ref). Also, if inside-out objects are stored inside other structures, a dump of those structures will not contain the contents of the object's fields.

In the event of a method naming conflict, the ->dump() method can be called using its fully-qualified name:

 my $dump = $obj->Object::InsideOut::dump();
my $obj = Object::InsideOut->pump($data);

Object::InsideOut-pump()> takes the output from the ->dump() method, and returns an object that is created using that data. If $data is the array ref returned by using $obj->dump(), then the data is inserted directly into the corresponding fields for each class in the object's class hierarchy. If If $data is the string returned by using $obj->dump(1), then it is evaled to turn it into an array ref, and then processed as above.

If any of an object's fields are dumped to field name keys of the form ARRAY(0x...) or HASH(0x...) (see above), then the data will not be reloadable using Object::InsideOut-pump()>. To overcome this problem, the class developer must either add Name keywords to the :Field declarations (see above), or provide a :Dumper/:Pumper pair of subroutines as described below.

:Dumper Subroutine Attribute

If a class requires special processing to dump its data, then it can provide a subroutine labeled with the :Dumper attribute. This subroutine will be sent the object that is being dumped. It may then return any type of scalar the developer deems appropriate. Most likely this would be a hash ref containing key => value pairs for the object's fields. For example,

 my @data :Field;

 sub _dump :Dumper
 {
     my $obj = $_[0];

     my %field_data;
     $field_data{'data'} = $data[$$obj];

     return (\%field_data);
 }

Just be sure not to call your :Dumper subroutine dump as that is the name of the dump method exported by Object::InsideOut as explained above.

:Pumper Subroutine Attribute

If a class supplies a :Dumper subroutine, it will most likely need to provide a complementary :Pumper labeled subroutine that will be used as part of creating an object from dumped data using Object::InsideOut::pump(). The subroutine will be supplied the new object that is being created, and whatever scalar was returned by the :Dumper subroutine. The corresponding :Pumper for the example :Dumper above would be:

 sub _pump :Pumper
 {
     my ($obj, $field_data) = @_;

     $data[$$obj] = $field_data->{'data'};
 }
Storable

Object::InsideOut also supports object serialization using the Storable module. There are two methods for specifying that a class can be serialized using Storable. The first method involves adding Storable to the Object::InsideOut declaration in your package:

 package My::Class; {
     use Object::InsideOut qw(Storable);
     ...
 }

and adding use Storable; in your application. Then you can use the ->store() and ->freeze() methods to serialize your objects, and the retrieve() and thaw() subroutines to deserialize them.

 package main;
 use Storable;
 use My::Class;

 my $obj = My::Class->new(...);
 $obj->store('/tmp/object.dat');
 ...
 my $obj2 = retrieve('/tmp/object.dat');

The other method of specifying Storable serialization involves setting a ::storable variable (inside a BEGIN block) for the class prior to its use:

 package main;
 use Storable;

 BEGIN {
     $My::Class::storable = 1;
 }
 use My::Class;

Dynamic Field Creation

Normally, object fields are declared as part of the class code. However, some classes may need the capability to create object fields on-the-fly, for example, as part of an :Automethod. Object::InsideOut provides a class method for this:

 # Dynamically create a hash field with standard accessors
 Object::InsideOut->create_field($class, '%'.$fld, "'Standard'=>'$fld'");

The first argument is the class to which the field will be added. The second argument is a string containing the name of the field preceeded by either a @ or % to declare an array field or hash field, respectively. The third argument is a string containing key => value pairs used in conjunction with the :Field attribute for generating field accessors.

Here's a more elaborate example used in inside an :Automethod:

 package My::Class; {
     use Object::InsideOut;

     sub auto :Automethod
     {
         my $self = $_[0];
         my $class = ref($self) || $self;
         my $method = $_;

         # Extract desired field name from get_/set_ method name
         my ($fld_name) = $method =~ /^[gs]et_(.*)$/;
         if (! $fld_name) {
             return;    # Not a recognized method
         }

         # Create the field and its standard accessors
         Object::InsideOut->create_field($class, '@'.$fld_name,
                                         "'Standard'=>'$fld_name'");

         # Return code ref for newly created accessor
         no strict 'refs';
         return *{$class.'::'.$method}{'CODE'};
     }
 }

Restricted and Private Methods

Access to certain methods can be narrowed by use of the :Restricted and :Private attributes. :Restricted methods can only be called from within the class's hierarchy. :Private methods can only be called from within the method's class.

Without the above attributes, most methods have public access. If desired, you may explicitly label them with the :Public attribute.

You can also specify access permissions on automatically generated accessors:

 my @data     :Field('Standard' => 'data', 'Permission' => 'private');
 my @info     :Field('Set' => 'set_info',  'Perm'       => 'restricted');
 my @internal :Field('Acc' => 'internal',  'Private'    => 1);
 my @state    :Field('Get' => 'state',     'Restricted' => 1);

Such permissions apply to both of the get and set accessors created on a field. If different permissions are required on an accessor pair, then you'll have to create the accessors yourself, using the :Restricted and :Private attributes when applicable:

 # Create a private set method on the 'foo' field
 my @foo :Field('Set' => 'set_foo', 'Priv' => 1);

 # Read access on the 'foo' field is restricted
 sub get_foo :Restrict
 {
     return ($foo[${$_[0]}]);
 }

 # Create a restricted set method on the 'bar' field
 my %bar :Field('Set' => 'set_bar', 'Perm' => 'restrict');

 # Read access on the 'foo' field is public
 sub get_bar
 {
     return ($bar{${$_[0]}});
 }

Hidden Methods

For subroutines marked with the following attributes:

:ID
:Init
:Replicate
:Destroy
:Automethod
:Dumper
:Pumper

Object::InsideOut normally renders them uncallable (hidden) to class and application code (as they should normally only be needed by Object::InsideOut itself). If needed, this behavior can be overridden by adding the PUBLIC, RESTRICTED or PRIVATE keywords following the attribute:

 sub _init :Init(private)    # Callable from within this class
 {
     my ($self, $args) = @_;

     ...
 }

NOTE: A bug in Perl 5.6.0 prevents using these access keywords. As such, subroutines marked with the above attributes will be left with public access.

NOTE: The above cannot be accomplished by using the corresponding attributes. For example:

 # sub _init :Init :Private    # Wrong syntax - doesn't work

Object Coercion

Object::InsideOut provides support for various forms of object coercion through the overload mechanism. For instance, if you want an object to be usable directly in a string, you would supply a subroutine in your class labeled with the :Stringify attribute:

 sub as_string :Stringify
 {
     my $self = $_[0];
     my $string = ...;
     return ($string);
 }

Then you could do things like:

 print("The object says, '$obj'\n");

For a boolean context, you would supply:

 sub as_string :Boolify
 {
     my $self = $_[0];
     my $true_or_false = ...;
     return ($true_or_false);
 }

and use it in this manner:

 if (! defined($obj)) {
     # The object is undefined
     ....

 } elsif (! $obj) {
     # The object returned a false value
     ...
 }

The following coercion attributes are supported:

:Stringify
:Numerify
:Boolify
:Arrayify
:Hashify
:Globify
:Codify

Coercing an object to a scalar (:Scalarify) is not supported as $$obj is the ID of the object and cannot be overridden.

FOREIGN CLASS INHERITANCE

Object::InsideOut supports inheritance from foreign (i.e., non-Object::InsideOut) classes. This means that your classes can inherit from other Perl class, and access their methods from your own objects.

One method of declaring foreign class inheritance is to add the class name to the Object::InsideOut declaration inside your package:

 package My::Class; {
     use Object::InsideOut qw(Foreign::Class);
     ...
 }

This allows you to access the foreign class's static (i.e., class) methods from your own class. For example, suppose Foreign::Class has a class method called foo. With the above, you can access that method using My::Class->foo() instead.

Multiple foreign inheritance is supported, as well:

 package My::Class; {
     use Object::InsideOut qw(Foreign::Class Other::Foreign::Class);
     ...
 }
$self->inherit($obj, ...);

To use object methods from foreign classes, an object must inherit from an object of that class. This would normally be done inside a class's :Init subroutine:

 package My::Class; {
     use Object::InsideOut qw(Foreign::Class);

     sub init :Init
     {
         my ($self, $args) = @_;

         my $foreign_obj = Foreign::Class->new(...);
         $self->inherit($foreign_obj);
     }
 }

Thus, with the above, if Foreign::Class has an object method called bar, you can call that method from your own objects:

 package main;

 my $obj = My::Class->new();
 $obj->bar();

Object::InsideOut's AUTOLOAD subroutine handles the dispatching of the ->bar() method call using the internally held inherited object (in this case, $foreign_obj).

Multiple inheritance is supported, as well: You can call the ->inherit() method multiple times, or make just one call with all the objects to be inherited from.

->inherit() is a restricted method. In other words, you cannot use it on an object outside of code belonging to the object's class tree (e.g., you can't call it from application code).

In the event of a method naming conflict, the ->inherit() method can be called using its fully-qualified name:

 $self->Object::InsideOut::inherit($obj);
my @objs = $self->heritage();
my $obj = $self->heritage($class);
my @objs = $self->heritage($class1, $class2, ...);

Your class code can retrieve any inherited objects using the ->heritage() method. When called without any arguments, it returns a list of any objects that were stored by the calling class using the calling object. In other words, if class My::Class uses object $obj to store foreign objects $fobj1 and $fobj2, then later on in class My::Class, $obj->heritage() will return $fobj1 and $fobj2.

->heritage() can also be called with one or more class name arguments. In this case, only objects of the specified class(es) are returned.

In the event of a method naming conflict, the ->heritage() method can be called using its fully-qualified name:

 my @objs = $self->Object::InsideOut::heritage();
$self->disinherit($class [, ...])
$self->disinherit($obj [, ...])

The ->disinherit() method disassociates (i.e., deletes) the inheritance of foreign object(s) from an object. The foreign objects may be specified by class, or using the actual inherited object (retrieved via ->heritage(), for example).

The call is only effective when called inside the class code that established the initial inheritance. In other words, if an inheritance is set up inside a class, then disinheritance can only be done from inside that class.

In the event of a method naming conflict, the ->disinherit() method can be called using its fully-qualified name:

 $self->Object::InsideOut::disinherit($obj [, ...])

NOTE: With foreign inheritance, you only have access to class and object methods. The encapsulation of the inherited objects is strong, meaning that only the class where the inheritance takes place has direct access to the inherited object. If access to the inherited objects themselves, or their internal hash fields (in the case of blessed hash objects), is needed outside the class, then you'll need to write your own accessors for that.

LIMITATION: You cannot use fully-qualified method names to access foreign methods (when encapsulated foreign objects are involved). Thus, the following will not work:

 my $obj = My::Class->new();
 $obj->Foreign::Class::bar();

Normally, you shouldn't ever need to do the above: $obj->bar() would suffice.

The only time this may be an issue is when the native class overrides an inherited foreign class's method (e.g., My::Class has its own ->bar() method). Such overridden methods are not directly callable. If such overriding is intentional, then this should not be an issue: No one should be writing code that tries to by-pass the override. However, if the overriding is accidently, then either the native method should be renamed, or the native class should provide a wrapper method so that the functionality of the overridden method is made available under a different name.

use base and Fully-qualified Method Names

The foreign inheritance methodology handled by the above is predicated on non-Object::InsideOut classes that generate their own objects and expect their object methods to be invoked via those objects.

There are exceptions to this rule:

1. Foreign object methods that expect to be invoked via the inheriting class's object, or foreign object methods that don't care how they are invoked (i.e., they don't make reference to the invoking object).

This is the case where a class provides auxiliary methods for your objects, but from which you don't actually create any objects (i.e., there is no corresponding foreign object, and $obj->inherit($foreign) is not used.)

In this case, you can either:

a. Declare the foreign class using the standard method (i.e., use Object::InsideOut qw(Foreign::Class);), and invoke its methods using their full path (e.g., $obj->Foreign::Class::method();); or

b. You can use the base pragma so that you don't have to use the full path for foreign methods.

 package My::Class; {
     use Object::InsideOut;
     use base 'Foreign::Class';
     ...
 }

The former scheme is faster.

2. Foreign class methods that expect to be invoked via the inheriting class.

As with the above, you can either invoke the class methods using their full path (e.g., My::Class->Foreign::Class::method();), or you can use base so that you don't have to use the full path. Again, using the full path is faster.

Class::Singleton is an example of this type of class.

3. Class methods that don't care how they are invoked (i.e., they don't make reference to the invoking class).

In this case, you can either use use Object::InsideOut qw(Foreign::Class); for consistency, or use use base qw(Foreign::Class); if (slightly) better performance is needed.

If you're not familiar with the inner workings of the foreign class such that you don't know if or which of the above exceptions applies, then the formulaic approach would be to first use the documented method for foreign inheritance (i.e., use Object::InsideOut qw(Foreign::Class);). If that works, then I strongly recommend that you just use that approach unless you have a good reason not to. If it doesn't work, then try use base.

THREAD SUPPORT

For Perl 5.8.1 and later, this module fully supports threads (i.e., is thread safe), and supports the sharing of Object::InsideOut objects between threads using threads::shared.

To use Object::InsideOut in a threaded application, you must put use threads; at the beginning of the application. (The use of require threads; after the program is running is not supported.) If object sharing it to be utilized, then use threads::shared; should follow.

If you just use threads;, then objects from one thread will be copied and made available in a child thread.

The addition of use threads::shared; in and of itself does not alter the behavior of Object::InsideOut objects. The default behavior is to not share objects between threads (i.e., they act the same as with use threads; alone).

To enable the sharing of objects between threads, you must specify which classes will be involved with thread object sharing. There are two methods for doing this. The first involves setting a ::shared variable (inside a BEGIN block) for the class prior to its use:

 use threads;
 use threads::shared;

 BEGIN {
     $My::Class::shared = 1;
 }
 use My::Class;

The other method is for a class to add a :SHARED flag to its use Object::InsideOut ... declaration:

 package My::Class; {
     use Object::InsideOut ':SHARED';
     ...
 }

When either sharing flag is set for one class in an object hierarchy, then all the classes in the hierarchy are affected.

If a class cannot support thread object sharing (e.g., one of the object fields contains code refs [which Perl cannot share between threads]), it should specifically declare this fact:

 package My::Class; {
     use Object::InsideOut ':NOT_SHARED';
     ...
 }

However, you cannot mix thread object sharing classes with non-sharing classes in the same class hierarchy:

 use threads;
 use threads::shared;

 package My::Class; {
     use Object::InsideOut ':SHARED';
     ...
 }

 package Other::Class; {
     use Object::InsideOut ':NOT_SHARED';
     ...
 }

 package My::Derived; {
     use Object::InsideOut qw(My::Class Other::Class);   # ERROR!
     ...
 }

Here is a complete example with thread object sharing enabled:

 use threads;
 use threads::shared;

 package My::Class; {
     use Object::InsideOut ':SHARED';

     # One list-type field
     my @data :Field('Accessor' => 'data', 'Type' => 'List');
 }

 package main;

 # New object
 my $obj = My::Class->new();

 # Set the object's 'data' field
 $obj->data(qw(foo bar baz));

 # Print out the object's data
 print(join(', ', @{$obj->data()}), "\n");       # "foo, bar, baz"

 # Create a thread and manipulate the object's data
 my $rc = threads->create(
         sub {
             # Read the object's data
             my $data = $obj->data();
             # Print out the object's data
             print(join(', ', @{$data}), "\n");  # "foo, bar, baz"
             # Change the object's data
             $obj->data(@$data[1..2], 'zooks');
             # Print out the object's modified data
             print(join(', ', @{$obj->data()}), "\n");  # "bar, baz, zooks"
             return (1);
         }
     )->join();

 # Show that changes in the object are visible in the parent thread
 # I.e., this shows that the object was indeed shared between threads
 print(join(', ', @{$obj->data()}), "\n");       # "bar, baz, zooks"

SPECIAL USAGE

Usage With Exporter

It is possible to use Exporter to export functions from one inside-out object class to another:

 use strict;
 use warnings;

 package Foo; {
     use Object::InsideOut 'Exporter';
     BEGIN {
         our @EXPORT_OK = qw(foo_name);
     }

     sub foo_name
     {
         return (__PACKAGE__);
     }
 }

 package Bar; {
     use Object::InsideOut 'Foo' => [ qw(foo_name) ];

     sub get_foo_name
     {
         return (foo_name());
     }
 }

 package main;

 print("Bar got Foo's name as '", Bar::get_foo_name(), "'\n");

Note that the BEGIN block is needed to ensure that the Exporter symbol arrays (in this case @EXPORT_OK) get populated properly.

Usage With require and mod_perl

Object::InsideOut usage under mod_perl and with runtime-loaded classes is supported automatically; no special coding is required.

Singleton Classes

A singleton class is a case where you would provide your own ->new() method that in turn calls Object::InsideOut's ->new() method:

 package My::Class; {
     use Object::InsideOut;

     my $singleton;

     sub new {
         my $thing = shift;
         if (! $singleton) {
             $singleton = $thing->Object::InsideOut::new(@_);
         }
         return ($singleton);
     }
 }

DIAGNOSTICS

This module uses Exception::Class for reporting errors. The base error class for this module is OIO. Here is an example of the basic manner for trapping and handling errors:

 my $obj;
 eval { $obj = My::Class->new(); };
 if (my $e = OIO->caught()) {
     print(STDERR "Failure creating object: $e\n");
     exit(1);
 }

I have tried to make the messages and information returned by the error objects as informative as possible. Suggested improvements are welcome. Also, please bring to my attention any conditions that you encounter where an error occurs as a result of Object::InsideOut code that doesn't generate an Exception::Class object. Here is one such error:

Invalid ARRAY/HASH attribute

This error indicates you forgot the following in your class's code:

 use Object::InsideOut qw(Parent::Class ...);

BUGS AND LIMITATIONS

You cannot overload an object to a scalar context (i.e., can't :SCALARIFY).

You cannot use two instances of the same class with mixed thread object sharing in same application.

Cannot use attributes on subroutine stubs (i.e., forward declaration without later definition) with :Automethod:

 package My::Class; {
     sub method :Private;   # Will not work

     sub _automethod :Automethod
     {
         # Code to handle call to 'method' stub
     }
 }

Due to limitations in the Perl parser, you cannot use line wrapping with the :Field attribute.

If a set accessor accepts scalars, then you can store any inside-out object type in it. If its Type is set to HASH, then it can store any blessed hash object.

It is possible to hack together a fake Object::InsideOut object, and so gain access to another object's data:

 my $fake = bless(\do{my $scalar}, 'Some::Class');
 $$fake = 86;   # ID of another object
 my $stolen = $fake->get_data();

Why anyone would try to do this is unknown. How this could be used for any sort of malicious exploitation is also unknown. However, if preventing this sort of security issue a requirement, then do not use Object::InsideOut.

There are bugs associated with threads::shared that may prevent you from using foreign inheritance with shared objects, or storing objects inside of shared objects.

For Perl 5.6.0 through 5.8.0, a Perl bug prevents package variables (e.g., object attribute arrays/hashes) from being referenced properly from subroutine refs returned by an :Automethod subroutine. For Perl 5.8.0 there is no workaround: This bug causes Perl to core dump. For Perl 5.6.0 through 5.6.2, the workaround is to create a ref to the required variable inside the :Automethod subroutine, and use that inside the subroutine ref:

 package My::Class; {
     use Object::InsideOut;

     my %data;

     sub auto :Automethod
     {
         my $self = $_[0];
         my $name = $_;

         my $data = \%data;      # Workaround for 5.6.X bug

         return sub {
                     my $self = shift;
                     if (! @_) {
                         return ($$data{$name});
                     }
                     $$data{$name} = shift;
                };
     }
 }

For Perl 5.8.1 through 5.8.4, a Perl bug produces spurious warning messages when threads are destroyed. These messages are innocuous, and can be suppressed by adding the following to your application code:

 $SIG{__WARN__} = sub {
         if ($_[0] !~ /^Attempt to free unreferenced scalar/) {
             print(STDERR @_);
         }
     };

For Perl 5.8.4 and 5.8.5, the "Storable" feature does not work due to a Perl bug. Use Object::InsideOut v1.33 if needed.

It is known that thread support is broken in ActiveState Perl 5.8.4 on Windows. (It is not known which other version of ActivePerl may be affected.) The best solution is to upgrade your version of ActivePerl. Barring that, you can tell CPAN to force the installation of Object::InsideOut, and use it in non-threaded applications.

View existing bug reports at, and submit any new bugs, problems, patches, etc. to: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Object-InsideOut

REQUIREMENTS

Perl 5.6.0 or later

Exception::Class v1.22 or later

Scalar::Util v1.10 or later. It is possible to install a pure perl version of Scalar::Util, however, it will be missing the weaken() function which is needed by Object::InsideOut. You'll need to upgrade your version of Scalar::Util to one that supports its XS code.

Test::More v0.50 or later (for installation)

SEE ALSO

Object::InsideOut Discussion Forum on CPAN: http://www.cpanforum.com/dist/Object-InsideOut

Annotated POD for Object::InsideOut: http://annocpan.org/~JDHEDDEN/Object-InsideOut-1.36/lib/Object/InsideOut.pm

The Rationale for Object::InsideOut: http://www.cpanforum.com/posts/1316

Comparison with Class::Std: http://www.cpanforum.com/posts/1326

Inside-out Object Model: http://www.perlmonks.org/?node_id=219378, http://www.perlmonks.org/?node_id=483162, http://www.perlmonks.org/?node_id=515650, Chapters 15 and 16 of Perl Best Practices by Damian Conway

Storable

ACKNOWLEDGEMENTS

Abigail <perl AT abigail DOT nl> for inside-out objects in general.

Damian Conway <dconway AT cpan DOT org> for Class::Std.

David A. Golden <dagolden AT cpan DOT org> for thread handling for inside-out objects.

Dan Kubb <dan.kubb-cpan AT autopilotmarketing DOT com> for :Chained methods.

AUTHOR

Jerry D. Hedden, <jdhedden AT cpan DOT org>

COPYRIGHT AND LICENSE

Copyright 2005, 2006 Jerry D. Hedden. All rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.